间隔定时器的接口如下:
#include <sys/time.h>
int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
结构定义:
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 */
};
可以通过调用上面两个API接口来设置和获取间隔定时器信息。系统为每个进程提供了3种itimer,每种定时器在不同时间域递减,当定时器超时,就会向进程发送一个信号,并且重置定时器。3种定时器的类型,如下表所示:
取值 | 含义 | 信号发送 |
---|---|---|
ITIMER_REAL | 定时真实时间,与alarm类型相同。 | SIGALRM |
ITIMER_VIRT | 定时进程在用户态下的实际执行时间。 | SIGVTALRM |
ITIMER_PROF | 定时进程在用户态和核心态下的实际执行时间。 | SIGPROF |
表1 参数which与定时器类型
在Linux 2.6.16 之前,itimer的实现是基于内核定时器timer wheel封装成的定时器接口。内核封装的定时器接口是提供给其他内核模块使用,也是其他定时器的基础。itimer通过内核定时器的封装,生成提供给用户层使用的接口setitimer和getitimer。内核定时器timer wheel提供的内核态调用接口为:可参考
https://github.com/torvalds/linux/blob/master/kernel/time/timer.c
add_timer()
del_timer()
init_timer()
在Linux 2.6.16 以来,itimer不再采用基于timer wheel的内核定时器进行实现。而是换成了高精度的内核定时器hrtimer进行实现。具体实现可参考:
https://github.com/torvalds/linux/blob/master/kernel/time/itimer.c
如果定时器超时时,进程是处于运行状态,那么超时的信号会被立刻传送给该进程,否则信号会被延迟传送,延迟时间要根据系统的负载情况。所以这里有一个BUG:当系统负载很重的情况下,对于ITIMER_REAL定时器有可能在上一次超时信号传递完成前再次超时,这样就会导致第二次超时的信号丢失。
每个进程中同一种定时器只能使用一次。
该系统调用在POSIX.1-2001中定义了,但在POSIX.1-2008中已被废弃。所以建议使用POSIX定时器API(timer_gettime, timer_settime)代替。
函数alarm本质上设置的是低精确、非重载的ITIMER_REAL类定时器,它只能精确到秒,并且每次设置只能产生一次定时。函数setitimer 设置的定时器则不同,它们不但可以计时到微妙(理论上),还能自动循环定时。在一个Unix进程中,不能同时使用alarm和ITIMER_REAL类定时器。
下面是测试代码:
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <iostream>
void sig_handler(int signo)
{
std::cout<<"recieve sigal: "<<signo<<std::endl;
}
int main()
{
signal(SIGALRM, sig_handler);
struct itimerval timer_set;
//启动时间(5s后启动)
timer_set.it_value.tv_sec = 5;
timer_set.it_value.tv_usec = 0;
//间隔定时器间隔:2s
timer_set.it_interval.tv_sec = 2;
timer_set.it_interval.tv_usec = 0;
if(setitimer(ITIMER_REAL, &timer_set, NULL) < 0)
{
std::cout<<"start timer failed..."<<std::endl;
return 0;
}
int temp;
std::cin>>temp;
return 0;
}