学科分类
目录
Linux编程

软件条件

当满足某种软件条件时,也可以驱使内核发送信号。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
点击此处
隐藏目录