系统中的一个模块需要频繁的获取系统时间,使用linux中内置的函数开销过大,因为需要的精度不是很高(毫秒级),索性用signal函数配合setitimer实现了个简易的全局时钟。
但是后来发现,SIGALRM的中断信号回终止sleep,因为sleep就是用SIGALRM信号量实现的,得另想方案。
这个替代方案就是POSIX中内置的定时器:timer_create()(创建)、timer_settime()(初始化)以及timer_delete(销毁),将自己的时间信号处理函数用timer_create注册为SIGUSR2,这样就不会中断sleep了。
以下内容转载自smart的BLOG,很详细。
POSIX定时器:timer_settime()
最强大的定时器接口来自POSIX时钟系列,其创建、初始化以及删除一个定时器的行动被分为三个不同的函数:timer_create()(创建定时器)、timer_settime()(初始化定时器)以及timer_delete(销毁它)。
创建一个定时器:
int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
struct itimespec{ struct timespec it_interval; struct timespec it_value; };
CLOCK_REALTIME :Systemwide realtime clock.
CLOCK_MONOTONIC:Represents monotonic time. Cannot be set.
CLOCK_PROCESS_CPUTIME_ID :High resolution per-process timer.
CLOCK_THREAD_CPUTIME_ID :Thread-specific timer.
CLOCK_REALTIME_HR :High resolution version of CLOCK_REALTIME.
CLOCK_MONOTONIC_HR :High resolution version of CLOCK_MONOTONIC.
struct sigevent { int sigev_notify; //notification type int sigev_signo; //signal number union sigval sigev_value; //signal valu void (*sigev_notify_function)(union sigval) pthread_attr_t *sigev_notify_attributes } union sigval { int sival_int; //integer valu void *sival_ptr; //pointer value }
timer_create()所创建的定时器并未启动。要将它关联到一个到期时间以及启动时钟周期,可以使用timer_settime()。
int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspect *ovalue);
struct timespec{ time_t tv_sec; long tv_nsec; };如果flags的值为TIMER_ABSTIME,则value所指定的时间值会被解读成绝对值(此值的默认的解读方式为相对于当前的时间)。这个经修改的行为可避免取得当前时间、计算“该时间”与“所期望的未来时间”的相对差额以及启动定时器期间造成竞争条件。
获得一个活动定时器的剩余时间:
int timer_gettime(timer_t timerid,struct itimerspec *value);
取得一个定时器的超限运行次数:有可能一个定时器到期了,而同一定时器上一次到期时产生的信号还处于挂起状态。在这种情况下,其中的一个信号可能会丢失。这就是定时器超限。程序可以通过调用timer_getoverrun来确定一个特定的定时器出现这种超限的次数。定时器超限只能发生在同一个定时器产生的信号上。由多个定时器,甚至是那些使用相同的时钟和信号的定时器,所产生的信号都会排队而不会丢失。
int timer_getoverrun(timer_t timerid);
执行成功时,timer_getoverrun()会返回定时器初次到期与通知进程(例如通过信号)定时器已到期之间额外发生的定时器到期次数。举例来说,在我们之前的例子中,一个1ms的定时器运行了10ms,则此调用会返回9。如果超限运行的次数等于或大于DELAYTIMER_MAX,则此调用会返回DELAYTIMER_MAX。执行失败时,此函数会返回-1并将errno设定会EINVAL,这个唯一的错误情况代表timerid指定了无效的定时器。
删除一个定时器:
int timer_delete (timer_t timerid);
一次成功的timer_delete()调用会销毁关联到timerid的定时器并且返回0。执行失败时,此调用会返回-1并将errno设定会 EINVAL,这个唯一的错误情况代表timerid不是一个有效的定时器。void handle() { time_t t; char p[32]; time(&t); strftime(p, sizeof(p), "%T", localtime(&t)); printf("time is %s\n", p); } int main() { struct sigevent evp; struct itimerspec ts; timer_t timer; int ret; evp.sigev_value.sival_ptr = &timer; evp.sigev_notify = SIGEV_SIGNAL; evp.sigev_signo = SIGUSR1; signal(SIGUSR1, handle); ret = timer_create(CLOCK_REALTIME, &evp, &timer); if( ret ) perror("timer_create"); ts.it_interval.tv_sec = 1; ts.it_interval.tv_nsec = 0; ts.it_value.tv_sec = 3; ts.it_value.tv_nsec = 0; ret = timer_settime(timer, 0, &ts, NULL); if( ret ) perror("timer_settime"); while(1); }
void handle(union sigval v) { time_t t; char p[32]; time(&t); strftime(p, sizeof(p), "%T", localtime(&t)); printf("%s thread %lu, val = %d, signal captured.\n", p, pthread_self(), v.sival_int); return; } int main() { struct sigevent evp; struct itimerspec ts; timer_t timer; int ret; memset (&evp, 0, sizeof (evp)); evp.sigev_value.sival_ptr = &timer; evp.sigev_notify = SIGEV_THREAD; evp.sigev_notify_function = handle; evp.sigev_value.sival_int = 3; //作为handle()的参数 ret = timer_create(CLOCK_REALTIME, &evp, &timer); if( ret) perror("timer_create"); ts.it_interval.tv_sec = 1; ts.it_interval.tv_nsec = 0; ts.it_value.tv_sec = 3; ts.it_value.tv_nsec = 0; ret = timer_settime(timer, TIMER_ABSTIME, &ts, NULL); if( ret ) perror("timer_settime"); while(1); }