1、Alarms
时钟可以用来调度一个将来要做的任务,Unix提供alarm。使用sleep函数添加时延,sleep(n)将当前进程挂起n秒或者在此期间被一个不能忽略的信号到达所唤醒。
sleep()的工作原理:
系统的每个进程都有一个私有的闹钟(alarm clock),这个闹钟很像一个计时器,可以设置一定秒数后闹铃。时间一到,时钟就发送一个信号SIGALRM到进程。除非进程为SIGALRM设置了处理函数,否则信号将杀死这个进程。这就是sleep的原理,sleep函数由3个步骤组成:
为SIGALRM设置一个处理函数调用alarm(num_seconds);调用pause挂起进程直到信号到达alarm和pause系统调用函数:
alarm():设置发送信号的计时器,old=alarm(unsigned seconds),等待seconds秒后内核发送SIGALRM信号到这个进程,返回值old为计时器剩余时间。pause():pause挂起调用进程直到一个信号到达。如果调用进程被这个信号终止,pause没有返回。如果调用进程用一个处理函数捕获,在控制从处理函数处返回后pause返回。2、间隔计时器
新Unix系统使用间隔计时器,有更高的精度,每个进程都有3个独立的计时器,分别是真实时间、用户时间和用户时间+系统时间。
ITIMER_REAL:这个计时器计量真实时间=用户代码执行时间+内核代码执行时间+睡眠时间,当这个时间用尽,发送SIGALRM信号。ITIMER_VIRTUAL:只有进程在用户态运行时才计时,当虚拟计时器用尽,发送SIGVTALRM。ITIMER_PROF:这个计时器在进程运行于用户态或该进程调用而陷入内核态时计时。当这个计时器用尽,发送SIGPROF信号。每个计时器都有两种间隔:初始it_value和重复it_interval,计时器的数据结构类型:it_value和it_interval都是由秒和微秒两部分组成类似一个小数的整数部分和小数部分。
struct itimerval { struct timeval it_interval; /* next value */ struct timeval it_value; /* current value */ };
struct timeval { time_t tv_sec; /* seconds秒 */ suseconds_t tv_usec; /* microseconds微秒 */ };
getitimer和setitimer系统调用:
getitimer():result=getitimer(int which,strut itimerval *val),将某个特定的计时器which的当前设置读到val指向的结构中。setitimer():result=setitimer(int which,const strut itimerval *newval,strut itimerval *oldval),将某个特定的计时器which设置为newval指向的结构的值,之前该计时器的设定复制到oldval指向的结构中。which的值为指定的计时器,分别是ITIMER_REAL、ITIMER_VIRTUAL、ITIMER_PROF这三种计时器。3、总结
计时器用来挂起执行和调度将要采取的动作,一个计时器是内核的一种机制,通过这种机制,内核在一定的时间之后向进程发送SIGALRM信号。alarm系统调用在特定的实际秒数之后发送SIGALRM给进程。setitimer系统调用以更高的精度控制计时器,同时能够以固定的时间间隔发送信号。
4、代码示例
sleep1.c
sleep工作原理展示
#include<stdio.h> #include<signal.h> void wakeup() { printf("Alarm received from kernel\n"); } int main() { printf("about to sleep for 4 seconds\n"); signal(SIGALRM,wakeup); alarm(4); pause(); printf("Morning so soon?\n"); return 0; }ticker_demo.c
演示如何使用一个间隔计时器
#include<stdio.h> #include<sys/time.h> #include<signal.h> void countdown() { static int num=10; printf("%d..",num--); fflush(stdout); if(num<0) { printf("Done!\n"); exit(0); } } int set_ticker(int n_msecs) { struct itimerval new_timeset; long n_sec,n_usecs; n_sec=n_msecs/1000; n_usecs=(n_msecs%1000)*1000L; new_timeset.it_interval.tv_sec=n_sec; new_timeset.it_interval.tv_usec=n_usecs; new_timeset.it_value.tv_sec=n_sec; new_timeset.it_value.tv_usec=n_usecs; return setitimer(ITIMER_REAL,&new_timeset,NULL); } int main() { signal(SIGALRM,countdown); if(set_ticker(500)==-1) perror("set_ticker"); else while(1) pause(); return 0; }