IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> Linux -- 信号补充 -> 正文阅读

[系统运维]Linux -- 信号补充

alarm()

一个信号从发出到收到,有一个不可避免的时延。所以如果使用信号来计时的话,10ms以内的计时不准确。只要超过这个时间,基本上都能准确的用信号来计时。

alarm没有办法直接实现多任务的计时器,因为当程序中有多个alarm时,程序计时可能就会以最后一个alarm为准,所以alarm不能直接用于多任务的计时器。因此,我们需要思考怎么使用alarm这样一个单一的计时器来实现10秒做一个事,5秒做一个事,1秒做一个事的多任务计时器。

pause()

NAME
       pause - wait for signal

SYNOPSIS
       #include <unistd.h>

       int pause(void);

DESCRIPTION
       pause()  causes the calling process (or thread) to sleep until a signal is delivered that either terminates the process or causes the invocation of asignal-catching function.
       Pause()导致调用(当前正在运行的)进程(或线程)进入休眠状态,直到发送了终止进程或调用信号捕获函数的信号。

我们需要强调的是,有时我们使用sleep延时虽然能实现我们想要的效果,但是sleep只能用来调试,不能用到发布的源码当中,原因是sleep对于不同的平台并不通用,在有的平台上面sleep是由alarm和pause两者封装在一起实现的,假如在你当前程序中还有一个alarm,那么是不是就修改了我当前的定时时间,程序功能是不是就有问题了。也就是说sleep不具有兼容性。

下面,我们来使用alarm做一个定时循环的实验(一个程序疯狂的跑5s)。

第一个版本,使用time函数来实现

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
    time_t end;
    int64_t count=0;//为了防止溢出,使用64位的整型数来计时

    //当前的时间戳 + 5
    end =time(NULL)+5;

    //也就是计时5s
    while(time(NULL)<=end)
    {
        count++;
    }

    printf("%ld\n",count);
    return 0;
}

运行时,我们使用time命令来统计程序的实际运行时间,并把结果输出到当前目录下的log文件中。
在这里插入图片描述
系统会从按时间那一刻开始计算,会把5.001s – 5.9999s都看作是5s的时间。这样一看,这个误差比例还是挺高的,近乎相差1s。但如果计时时间越长,比如计时10000s,误差也是相差1s,从这方面来看,误差比例就相对小很多了。

第二个版本,使用信号alarm函数来实现

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <unistd.h>

int loop = 1;

void alarm_handler(int s)
{
    loop = 0;
}

int main()
{
    int64_t count=0;//为了防止溢出,使用64位的整型数来计时
    
    signal(SIGALRM,alarm_handler);
    alarm(5);

    //也是计时5s
    while(loop)
    {
        count++;
    }

    printf("%ld\n",count);

    return 0;
}

程序中的语句
signal(SIGALRM,alarm_handler);
一定要放在
alarm(5);
之前。

同样,运行时,我们使用time命令来统计程序的实际运行时间。
在这里插入图片描述
real 时间= user时间 +sys时间,我们看user的时间。

可以看出,使用alarm控制的时间更精确。

漏桶

流控(流量控制):每秒钟按规定的字符输出。(音、视频播放器中每秒播放多少字节,网络传输中,每秒固定传输多少字节的内容中广泛应用。)

这里要实现的一个具体的功能是:

实现cat的功能,但是将一次性显示改成每秒显示固定个数的字符。

实现效果如下:
请添加图片描述
使用sleep延时来实现

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

//每秒传输的字符的个数
#define CPS     10
#define BUFSIZE CPS

int main(int argc,char* argv[])
{
    int sfd,dfd=1;
    char buf[BUFSIZE];
    int len,ret,pos;

    if(argc < 2)
    {
        fprintf(stderr,"Usage...\n");
        exit(1);
    }

    do
    {
        sfd = open(argv[1],O_RDONLY);
        if(sfd < 0)
        {
            if(errno!=EINTR)
            {
                perror("open()");
                exit(1);
            }
        }
    }while(sfd<0);

    while(1)
    {
        len = read(sfd,buf,BUFSIZE);
        if(len < 0)
        {
            if(errno == EINTR)
                continue;
            perror("read()");
            break;
        }
        if(len == 0)
        {
            break;
        }
        pos = 0;
        while(len > 0)
        {
            ret = write(dfd,buf+pos,len);
            if(ret < 0)
            {
                if(errno == EINTR)
                    continue;
                perror("write()");
                exit(1);
            }
            pos += ret;
            len -= ret;   
         }
        sleep(1);//等待1s
    }

    close(sfd);

    return 0;
}

需要说明的是,使用sleep延时虽然能实现想要的效果,但是sleep只能用作调试来用,不能用到发布的源码当中,原因是sleep对于不同的平台并不通用,不具有兼容性。

因此,下面我们用alarm()函数来实现

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

//每秒传输的字符的个数
#define CPS     10
#define BUFSIZE CPS

static volatile int loop = 1;

static void alarm_handler(int s)
{
    alarm(1);
    loop = 1;
}

int main(int argc,char* argv[])
{
    int sfd,dfd=1;
    char buf[BUFSIZE];
    int len,ret,pos;

    if(argc < 2)
    {
        fprintf(stderr,"Usage...\n");
        exit(1);
    }

    signal(SIGALRM,alarm_handler);
    alarm(1);
    
    do
    {
        sfd = open(argv[1],O_RDONLY);
        if(sfd < 0)
        {
            if(errno!=EINTR)
            {
                perror("open()");
                exit(1);
            }
        }
    }while(sfd<0);

    while(1)
    {
    	while(!loop)
    	{
    		//等待SIGALRM信号的到来,程序才继续执行。有点类似于sleep。
    		//可避免忙等(什么都不做)。
    		 pause();
    	}
    	loop = 0;
    
        while((len = read(sfd,buf,BUFSIZE))<0)
        {
             //如果是假错,比如遇到中断
              if(errno == EINTR)
              //就跳出当前循环体,重新执行当前层的while循环
                  continue;
             //如果是真错,就退出循环体
            perror("read()");
            break;
         }
         
        if(len == 0)
        {
            break;
        }
        
        pos = 0;
        while(len > 0)
        {
            ret = write(dfd,buf+pos,len);
            if(ret < 0)
            {
                if(errno == EINTR)
                    continue;
                perror("write()");
                exit(1);
            }
            pos += ret;
            len -= ret;
        }
    }

    close(sfd);
    
    return 0;
}

从上面的实现来看,我们可以发现我们所实现的这个漏桶功能的特点是,假设我们从一个(设备)文件中读数据,不管数据量来的很小还是数据量特别大的时候,程序都是一秒钟不紧不慢的最多输出10个字节大小的数据,这样就显得不够灵活。

令牌桶

令牌桶的功能相比于漏桶更加灵活,程序可以在漏桶的基础上进行修改,我们漏桶的权限是每间隔1秒最多能输出10个字节大小的数据,我们想改成当读数据时,没有读到数据的时候就攒权限,当数据量大的时候,就可以根据积攒的权限多输出一些数据。这样也可以避免像漏桶那样一直在忙等。

比如还是一秒钟能输出10个字节的数据,现在,没读到数据,我们就开始积攒权限,假设积攒三秒,就积攒到了连续三次输出10个字节的数据量。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

//每秒传输的字符的个数
#define CPS     10
#define BUFSIZE CPS
//规定一个上限,权限(令牌)不能一直积攒
#define BURST   100

//token -- 令牌
static volatile int token = 0;
//如果程序一开始就输出数据,就让token=1,
//如果一开始就让它积攒权限,1秒后再输出,就让token=0
//如果模拟等待一段时间,token积攒到某个数值时,突然有
//大量数据来的的输出效果,就给token>1的初始值

static void alarm_handler(int s)
{
    alarm(1);
    token++;
    if(token > BURST)//如果token的值大于上限
        token = BURST;//让token的值保持在上限不动
}

int main(int argc,char* argv[])
{
    int sfd,dfd=1;
    char buf[BUFSIZE];
    int len,ret,pos;

    if(argc < 2)
    {
        fprintf(stderr,"Usage...\n");
        exit(1);
    }

    signal(SIGALRM,alarm_handler);
    alarm(1);

    do
    {
        sfd = open(argv[1],O_RDONLY);
        if(sfd < 0)
        {
            if(errno!=EINTR)
            {
                perror("open()");
                exit(1);
            }
        }
    }while(sfd<0);
    
	while(1)
    {
        while(token <= 0)
            pause();
        //读写一圈消耗一个token
        token-=1;

        //假设这里读打印机数据,一开始没读到数据,会阻塞到这,恰好1s时间到了,
        //被信号打断,程序会执行信号捕获函数,token++,执行完之后,程序返回到这里,
        //就会被判断为假错,然后重新执行while循环,当仍然没有读到数据,循环上述步骤。
        //假设在第三秒后,数据来了,此时token = 3。
        while((len = read(sfd,buf,BUFSIZE))<0)
        {
            //如果是假错
            if(errno == EINTR)
            //就跳出当前循环体,重新执行while循环
                continue;
            //如果是真错,就退出循环体
            perror("read()");
            break;
        }

        if(len == 0)
        {
            break;
        }
        //接上面的分析
        //程序开始写数据,每次仍然写10个字节大小的数据,因为有充足的token,所以可以
        //连续的写,而不需要靠执行信号捕获函数来积攒token,这样就给人一种一
        //次读写多个字节大小的数据的感觉。
        pos = 0;
        while(len > 0)
        {
            ret = write(dfd,buf+pos,len);
            if(ret < 0)
            {
                if(errno == EINTR)
                    continue;
                perror("write()");
                exit(1);
            }
            pos += ret;
            len -= ret;
        }
	}
	
    close(sfd);

    return 0;
}
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-08-26 12:31:17  更:2021-08-26 12:33:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/29 8:34:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计