软件条件
当满足某种软件条件时,也可以驱使内核发送信号。Linux系统中的alarm()函数就是一个典型的产生软件条件信号的信号源。
1、alarm()
alarm()函数的功能相当于计时器,驱使内核在指定秒数后发送信号到调用该函数的进程。alarm()函数存在于函数库unistd.h中,其函数声明如下:
unsigned int alarm(unsigned int seconds);
若进程中不是第一次调用alarm(),且上一个的alarm()尚有剩余秒数,则该函数的成功调用后会返回旧计时器的剩余秒数,否则返回0。例如在定时器alarm(5)启动3秒后,新定时器alarm(4)启动,那么alarm(4)的返回值为2;若又3秒后第三个定时器alarm(2)启动,那么alarm(2)的返回值为0;若额外设置alarm(0),将会取消计时器。计时器采用自然定时法,无论当前进程是否处于运行态,计时器都会计时。
计时结束后,alarm()函数会发送14号信号SIGALRM到当前进程,进程收到该信号后默认终止运行。
案例2:在程序中设置计时器,使进程在指定秒数后终止运行。
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 int main()
5 {
6 alarm(1); //设置计时器
7 while(1) //循环保证进程不退出
8 printf("process will finish.\n");
9 return 0;
10 }
在案例2中先设置了一个1秒的计时器;为了保证进程在信号到达之前保持运行,又在进程中添加while循环,使进程不断打印信息。1秒后计时器会驱使内核发送SIGALRM信号到进程,因此进程会在1秒之后结束。
编译案例2,执行程序,观察到屏幕不断打印“process will finish.”,1秒后停止打印,并输出“Alarm clock”,表示计时器生效,使进程终止。
2、setitimer()
setitimer()函数也可以设置定时器,与alarm()相比,它精确到微秒,精度更高,并且可实现周期定时。该函数存在于函数库sys/time.h中,函数声明如下:
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
若setitimer()函数成功调用则返回0;否则返回-1,并设置errno。该函数有3个参数,其中参数which用来设置以何种方式计时,which有3个取值,不同的值对应不同的计时方法,产生不同的信号。which取值及对应含义如下:
● 若参数为ITIMER_REAL,使用自然定时法计时,计算自然流逝的时间,计时结束递送14号信号SIGALRM;
● 若参数为ITIMER_VIRTUAL,只计算进程占用cpu的时间,计时结束后递送26号信号SIGVTALRM;
● 若参数为ITIMER_PROF,计算进程占用cpu以及执行系统调用的时间,即进程在用户空间和内核空间运行时间的总和,计时结束后递送27号信号SIGPROF。
setitimer()的第二个参数是一个传入参数,表示计时器定时时长,其本质是一个itimerval类型数据结构的指针,itimerval中有两个timerval类型的成员,这两个成员也是结构体类型。itimerval与timeval的定义如下:
struct itimerval {
struct timeval it_interval;
struct timeval it_value;
};
struct timeval {
long tv_sec; //秒
long tv_usec; //微秒
};
timerval结构体的两个成员分别提供秒级精度和微秒级精度;itimerval结构体的两个成员it_interval和it_value分别指定间隔时间和初始定时时间。若只指定it_value,则只实现一次定时;若同时指定it_interval,则用来实现重复定时。setitimer()的工作机制是,先对it_value倒计时,当it_value计时结束时,触发信号发送条件,然后重置it_value为it_interval,继续对it_value倒计时,如此一直循环。
setitimer()函数的第三个参数用来保存先前设置的new_value值,通常设置为NULL。
案例3:使用setitimer()函数自实现一个alarm()函数。
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/time.h>
4 #include <error.h>
5 unsigned int my_alarm(unsigned int sec)
6 {
7 struct itimerval it,oldit;
8 int ret;
9 it.it_value.tv_sec=sec; //指定时间
10 it.it_value.tv_usec=0;
11 it.it_interval.tv_sec=0; //指定重复次数
12 it.it_interval.tv_usec=0;
13 ret=setitimer(ITIMER_REAL,&it,&oldit);
14 if(ret==1){
15 perror("setitimer");
16 exit(1);
17 }
18 return oldit.it_value.tv_sec;
19 }
20 int main()
21 {
22 my_alarm(1);
23 while(1)
24 printf("process will finish\n");
25 return 0;
26 }
由于alarm()只实现一次计时,因此my_alarm()中调用的setitimer()的参数it的成员it_interval的值都为0;因为alarm()只精确到秒,因此setitimer()中参数it表示微秒的成员变量it_value.tv_usec设置为0即可。
编译案例3,执行程序,1秒后停止输出“process will finish”,并输出“Alarm clock”,说明my_alarm()实现成功,输出信息如下:
……
process will finish
process will finish
process will finishAlarm clock