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;
end =time(NULL)+5;
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;
signal(SIGALRM,alarm_handler);
alarm(5);
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);
}
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)
{
pause();
}
loop = 0;
while((len = read(sfd,buf,BUFSIZE))<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;
}
}
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
static volatile int token = 0;
static void alarm_handler(int s)
{
alarm(1);
token++;
if(token > BURST)
token = BURST;
}
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-=1;
while((len = read(sfd,buf,BUFSIZE))<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;
}
}
close(sfd);
return 0;
}
|