一、特点:
1、使用 POSIX Timer,一个进程可以创建任意多个 Timer。
2、setitmer 计时器时间到达时,可以有多种通知方式,比如信号,或者启动线程。
3、POSIX Timer 则可以使用实时信号。
4、POSIX Timer 是针对有实时要求的应用所设计的,接口支持 ns 级别的时钟精度。
二、相关API
(1)创建timer
int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
参数:
clock_id:说明定时器是基于哪个时钟的,一般使用CLOCK_REALTIME即可。取值如下:
CLOCK_REALTIME :Systemwide realtime clock. //时间是系统保存的时间,即可以由 date 命令显示的时间,该时间可以重新设置。
CLOCK_MONOTONIC:Represents monotonic time. Cannot be set.
CLOCK_PROCESS_CPUTIME_ID :High resolution per-process timer. //CLOCK_PROCESS_CPUTIME_ID 的含义与 setitimer 的 ITIMER_VIRTUAL 类似。
CLOCK_THREAD_CPUTIME_ID :Thread-specific timer.// CLOCK_THREAD_CPUTIME_ID 以线程为计时实体,当前进程中的某个线程真正地运行了一定时间才触发 Timer。
CLOCK_REALTIME_HR :High resolution version of CLOCK_REALTIME.
CLOCK_MONOTONIC_HR :High resolution version of CLOCK_MONOTONIC.
*timerid 装载的是被创建的定时器的 ID句柄,类型是int。就像open函数返回的文件描述符一样。
*evp 指定了定时器到期要产生的异步通知,结构体定义如下:
union sigval { /* Data passed with notification */
int sival_int; /* Integer value */
void *sival_ptr; /* Pointer value */
};
typedef struct sigevent {
sigval_t sigev_value;
int sigev_signo;
int sigev_notify;
union {
int _pad[SIGEV_PAD_SIZE];
int _tid;
struct {
void (*_function)(sigval_t);
void *_attribute; /* really pthread_attr_t */
} _sigev_thread;
} _sigev_un;
} sigevent_t;
struct sigevent 结构中的成员 evp->sigev_notify说明了定时器到期时应该采取的行动,可以设定:
(a)SIGEV_NONE。不需要异步通知,程序自己调用timer_gettime来轮询timer的当前状态
(b)SIGEV_SIGNAL。使用sinal这样的异步通知方式。发送的信号由sigev_signo定义。如果发送的是realtime signal,该信号的附加数据由sigev_value定义。
(c)SIGEV_THREAD。创建一个线程执行timer超期callback函数,_attribute定义了该线程的属性。
(d)SIGEV_THREAD_ID。行为和SIGEV_SIGNAL类似,不过发送的信号被送达进程内的一个指定的thread,这个thread由_tid标识。
如果evp为 NULL,那么定时器到期会产生默认的信号,对 CLOCK_REALTIMER来说,默认信号就是SIGALRM。如果要产生除默认信号之外的其它信号,程序必须将 evp->sigev_signo设置为期望的信号码。
(2)设定timer
#include <time.h>
int timer_settime(timer_t timerid, int flags, const struct itimerspec *new_value,
struct itimerspec * old_value);
struct itimespec{
struct timespec it_interval; // 后续按照该时间间隔
struct timespec it_value; // 最初开始时间间隔
};
如果new_value.it_value是一个非0值,那么调用timer_settime可以启动该timer。如果new_value.it_value是一个0值,那么调用timer_settime可以stop该timer。
如果flags的值为TIMER_ABSTIME,则value所指定的时间值会被解读成绝对值(此值的默认的解读方式为相对于当前的时间)。这个经修改的行为可避免取得当前时间、计算“该时间”与“所期望的未来时间”的相对差额以及启动定时器期间造成竞争条件。 如果ovalue的值不是NULL,则之前的定时器到期时间会被存入其所提供的itimerspec。如果定时器之前处在未启动状态,则此结构的成员全都会被设定成0。
(3)删除timer
#include <time.h>
int timer_delete(timer_t timerid);
有创建就有删除,timer_delete用来删除指定的timer,释放资源。
三、使用示例
(1)线程通知
#include <stdio.h>
#include <sys/time.h>
#include <string.h>
#include <signal.h>
#include <time.h>
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(int argc, char *argv[]){
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);
}
ref:
Linux 系统上最常用的定时器 - 知乎
Linux时间子系统之(三):用户空间接口函数
深入学习定时器 | AlloyTeam