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;
}