APUE编程:90---信号处理(getitimer、setitimer函数:struct itimerval、SIGALRM信号、SIGVTALRM信号、SIGPROF信号)
原创
©著作权归作者所有:来自51CTO博客作者董哥的黑板报的原创作品,请联系作者获取转载授权,否则将追究法律责任
一、定时器种类
- 系统为每个进程提供三个间隔计时器,每个间隔计时器在不同的时间域中递减。当任何计时器过期时,将向进程发送一个信号,然后计时器(可能)重新启动,计时器的种类如下:
- ITIMER_REAL(值为0):计时器的值实时递减。计时器超时后,向进程发送SIGALRM信号
- ITIMER_VIRTUAL(值为1):当进程在执行的过程中计数,仅在进程执行时递减。计时器超时后,发送SIGVTALRM信号
- ITIMER_PROF(值为2):进程和系统执行时都递减计时器的值。这个计时器通常用于分析应用程序在用户和内核空间中花费的时间。计时器超时后,发送SIGPROF信号
二、struct itimerval结构体
struct itimerval {
struct timeval it_interval; /* 计时器到期后重新启动的时间间隔 */
struct timeval it_value; /* 计时器安装后第一次启动的时间*/
};
struct timeval {
time_t tv_sec; /* seconds,秒 */
suseconds_t tv_usec; /* microseconds,微秒 */
};
- struct itimerval结构成员介绍:
- it_value:是计时器第一次启动到期的时间(到期之后便根据计时器的种类发送对应的信号)
- it_interval:当计时器第一次启动到期(it_value到期)之后,会将it_value重置为it_interval所指的值,然后每隔it_interval时间执行一次计时器(到期之后便根据计时器的种类发送对应的信号)
- 计时器停止工作:
- 如果it_value为0,那么计时器将不生效
- 如果计时器it_value过期之后,it_interval为0,那么计时器也将停止工作
- struct timeval结构的tv_sec、tv_usec字段都对计时器的持续时间有重要影响
三、函数介绍
getitimer()函数
#include <sys/time.h>
int getitimer(int which, struct itimerval *curr_value);
- 功能:计时器的当前值填写在curr_value指向的结构体中
- 备注:
- 定时器由ITIMER_REAL、ITIMER_VIRTUAL或ITIMER_PROF之一指定
- curr_value结构中的it_value字段被设置为计时器上剩余的时间量,如果计时器被禁用,则为零。类似地,it_interval被设置为重置值
- 参数:
- which:间歇计时器类型,为上面介绍的3种定时器种类之一
- curr_value:一个struct itimerval结构体类型,保存当前计时器的值
- 返回值:成功返回0;出错返回-1,并设置errno值,常见的错误值如下:
- EFAULT:curr_value无效
- EINVAL:which参数不是ITIMER_REAL, ITIMER_VIRTUAL,或ITIMER_PROF之一
setitimer()函数
#include <sys/time.h>
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
- 功能:将指定的计时器设置为new_value中的值。如果old_value是非空的,则计时器的旧值存储在那里
- 参数:
- which:间歇计时器类型,为上面介绍的3种定时器种类之一
- new_value:将此参数指向的结构体设为计时器的当前值
- old_value:如果此参数不为NULL,用来保存计时器原有值
- 返回值:成功返回0;出错返回-1,并设置errno值
- EFAULT:new_value或old_value无效
- EINVAL:which参数不是ITIMER_REAL, ITIMER_VIRTUAL,或ITIMER_PROF之一;或者new_value指向的结构中的tv_usec字段包含0到999999范围之外的值(Linux 2.6.22)
四、定时器注意事项
- 计时器永远不会在请求的时间之前过期,但是可能会在之后的一些(短)时间过期,这取决于系统计时器的分辨率和系统负载;过期时,将生成一个信号并重置计时器。如果计时器在进程处于活动状态时过期(对于ITIMER_VIRTUAL始终为true),则在生成时将立即发送信号。否则,交付将抵消一个小的时间依赖于系统加载
五、演示案例
- 下面编程一个程序,定时器种类为ITIMER_REAL,定时器首次在5秒后启动,然后每隔1秒执行一次,发送的信号为SIGALRM
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
void sig_func(int signo);
int main()
{
//获取当前时间
time_t current_time;
(void)time(¤t_time);
//将时间转换为可读的时期和时间格式(struct tm)
struct tm*tm_ptr;
tm_ptr=gmtime(¤t_time);
printf("time:%02d:%02d:%2d,main start\n",tm_ptr->tm_hour,tm_ptr->tm_min,tm_ptr->tm_sec);
//为SIGALRM信号绑定处理函数
signal(SIGALRM,sig_func);
struct itimerval tick;
tick.it_value.tv_sec = 5; //计时器首次在5秒之后启动
tick.it_value.tv_usec = 0;
tick.it_interval.tv_sec =1; //定时器第一次到时之后,每隔1秒到期一次
tick.it_interval.tv_usec = 0;
//开启一个计时器,种类为ITIMER_REAL,触发的信号为SIGALRM
int ret = setitimer(ITIMER_REAL, &tick, NULL);
if ( ret != 0)
{
printf("setitimer error:%s \n", strerror(errno) );
exit(EXIT_FAILURE);
}
//pause函数:使进程/线程挂起,在接收到一个信号并从信号处理函数返回之后解除挂起操作
while(1)
pause();
exit(EXIT_SUCCESS);
}
void sig_func(int signo)
{
time_t current_time;
(void)time(¤t_time);
struct tm*tm_ptr;
tm_ptr=gmtime(¤t_time);
//打印时间和信号ID
printf("time:%02d:%02d:%2d,recv signal %d\n",tm_ptr->tm_hour,tm_ptr->tm_min,tm_ptr->tm_sec,signo);
}