linux 定时器函数,Linux 定时器

时间间隔定时器

interval timer(时间间隔定时器)系统调用自从被POSIX标准化后,首次出现于4.2BSD,能够提供比alarm()还多的控制:

#include

int getitimer(int which, struct itimerval *value);

int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);

interval timer的运作如同alarm(),但是可以选择自动重新启动它们,而且可运作在下面其中一种模式之中:

ITIMER_REAL

测量真是事件 。当所指定的真实时间值已过去,内核会送出一个SIGALARM信号给进程

ITIMER_VIRTUAL

只会在进程的用户空间程序代码执行时递减其值。当所选定的进程时间过去后,内核会送出一个SIGVTALRM信号给进城。

ITIMER_PROF

会在进城执行时以及内核替进程服务时(例如进行系统调用)递减其值。当所指定的时间量过去后,内核会送出一个SIGPROF信号给进城。此模式常会与ITIMER_VIRTUAL结合,让程序可以测量进程所耗用的用户时间与内核时间。

ITIMER_REAL所测量的时间如同alarm(),另两种模式则可用于概要分析。

itimerval结构允许用户指定时间量直到定时器到期为止以及指定到底时间,这让你能够以指定的到期时间重新启动定时器:

struct itimerval{

struct timeval it_interval; /*next value*/

struct timeval it_value; /*current value*/

};

如稍早所述,timeval结构可以提供微秒级分辨率:

struct timeval{

long tv_sec; /*seconds*/

long tv_usec; /*microseconds*/

};

setitimer()会使用it_value所指定的到期时间来启动一个which类型的定时器。一旦it_value所指定的时间过去后,内核会使用it_interval所提供的时间重新启动该定时器。因此,it_value是当前定时器剩下的时间。一旦it_value的值为0时,它会被设定为it_interval。如果定时器到期,而且it_interval的值为0,则不会重新启动该定时器。同样地,如果一个活动中的定时器的it_value被设为0,则定时器会停止运行,而且不会被重新启动。

如果ovalue的值不是NULL,则which类型的时间间隔定时器先前的值会被返回。

getitimer()会返回which类型的时间间隔定时器当前的值。

执行成功时,这两个调用都会返回0;发生错误时,则会返回-1,在此情况下errno会被设定会下面其中一个值 :

EFAULT

value或ovalue是一个无效的指针

EINVAL

which不是应该ieyouxiao的时间间隔定时器类型。

下面的程序代码片段会创建一个SIGALRM信号初期程序,接着会以5秒的初始到期时间来启动一个时间间隔定时器,随之而来的是1秒的时间间隔:

有些Unix系统会通过SIGALRM来实现sleep()与usleep(),显然,alarm()与setitimer()会使用SIGALRM。因此,程序设计者必须小心不要重复调用这些函数,否则结果是未定义的。如果只是短暂等待,程序设计者应该使用nanosleep(),按照POSIX的规定将不会使用信号。如果使用定时器,程序设计者应该使用setitimer()或alarm()。

高级定时器

最强大的定时器接口来自POSIX时钟系列

如果使用的是POSIX基于时钟的定时器,则创建、初始化以及删除一个定时器的行动被分为三个不同的函数:timer_create()(创建定时器)、timer_settime()(初始化定时器)以及timer_delete(销毁它)。

创建一个定时器

timer_create()可用于创建一个定时器:

#include

#include

int timer_create(clockid_t clockid,  struct sigevent *evp, timer_t *timerid);

以此成功的timer_create()调用会创建一个与POSIX时钟clockid相关联的新定时器,将独一无二的定时器标识符存入timerid并且返回0。此调用只会替定时器的运行设置环境,实际上任何事情都不会发生,直到定时器被启动,如下一节所示。

下面的范例会创建一个新的定时器CLOCK_PROCESS_CPUTIME_ID的POSIX时钟,以将定时器的标识符存入timer:

timer_t timer;

intret;

ret = timer_create(CLOCK_PROCESS_CPUTIME_ID, NULL, &timer);

if(ret)

perror("timer_create");

执行失败时,此调用会返回-1,不定义timerid,而且此调用会将errno设定会下面其中一个值:

EAGAIN

系统的资源不足以完成此请求

EINVAL

clockid指定了无效的POSIX时钟

ENOTSUP

虽然clockid指定了有效的POSIX时钟,但是系统并不支持使用该时钟的定时器。POSIX保证所有实现均支持使用CLOCK_REALTIME时钟的定时器。至于是否支持其他时钟,则由实现自行决定。

evp参数,如果非NULL值,用于定义当前定时器到期时所发生的异步通知。此结构定义于头文件中,它的内容对程序设计者而言应该是不透明的,但是它至少具有以下字段:

#include

struct sigevent{

union sigval sigev_value;

int sigev_signo;

int sigev_notify;

void (*sigev_notify_function)(union sigval);

pthread_attr_t *sigev_notify_attributes;

};

union sigval{

int sival_int;

void *sival_ptr;

};

当一个定时器到期时,相较于内核通知进程的方式,POSIX基于时钟的定时器可以提供更多的控制权,它允许进程指定内核将送出何种信号,甚至允许内核派生一个现成,并且执行一个函数以响应定时器到期的事实。一个进程可通过sigev_notify指定当定时器到期的行为,它必须是下面其中一个值:

SIGEV_NONE

一个空的通知。当定时器到期,不会有任何事发生

SIGEV_SIGNAL

当定时器到期,内核会将sigev_signo所指定的信号传送给进程。在信号处理程序中,si_value会被设定会sigev_value。

SIGEV_THREAD

当定时器到期,内核会(在此进程内)派生一个新的线程,并且让它执行sigev_notify_function,传入sigev_value作为为一个参数。从此函数返回后,线程会终止。如果sigev_notify_attributes的值不是NULL,则所提供的pthread_attr_t结构用于定义新线程的行为。

如果evp的值是NULL(就像我们前面的例子),则所设置的定时器到期通知宛如sigev_notify的值是SIGEV_SIGNAL、sigev_signo的值是SIGALRM以及sigev_value的值是定时器的标识符。因此,在默认情况下,这些定时器的通知方式类似于POSIX的时间间隔定时器。然而,通过自定义,它们可以做的事情还有很多、很多!

下面的例子会创建一个机遇CLOCK_REALTIME的定时器。当定时器到期时,内核将送出SIGUSR1信号并把si_value设定成存储定时器标识符的地址:

structsigevent evp;

timer_t timer;

intret;

evp.sigev_value.sival_ptr = &timer;

evp.sigev_notify = SIGEV_SIGNAL;

evp.sigev_signo = SIGUSR1;

ret = timer_create(CLOCK_REALTIME, &evp, &timer);

if( ret)

perror("timer_create");

启动一个定时器

timer_create()所创建的定时器并未启动。要将它关联到一个到期时间以及启动时钟周期,可以使用timer_settime():

#include

int timer_settime( timer_t timerid, int flags, const struct itimerspec *value, struct itimerspect *ovalue);

一次成功的timer_settime()调用会使用到期时间value(这时一个itimerspec结构)启动timerid所指定的定时器:

struct itimespec{

struct timespec it_interval;  /*next value*/

struct timespec it_value;    /* current value */

};

如同settimer(),it_value用于指定当前的定时器到期时间。当定时器到期,it_value的值会被更新成it_interval的值。如果it_interval的值为0,则定时器不是一个时间间隔定时器,一旦it_value到期就会回到未启动状态。

如稍早所述,timespec的结构提供了纳秒级分辨率:

struct timespec{

time_t tv_sec; /*seconds*/

long tv_nsec;   /*nanoseconds*/

};

如果flags的值为TIMER_ABSTIME,则value所指定的时间值会被解读成绝对值(此值的默认的解读方式为相对于当前的时间)。这个经修改的行为可避免取得当前时间、计算“该时间”与“所期望的未来时间”的相对差额以及启动定时器期间造成竞争条件。

如果ovalue的值不是NULL,则之前的定时器到期时间会被存入其所提供的itimerspec。如果定时器之前处在未启动状态,则此结构的成员全都会被设定成0。

使用稍早由timer_create()初始化的timer值,下面的例子可以创建一个每秒到期一次的周期性定时器:

structitimerspec ts;

intret;

ts.it_interval.tv_sec = 1;

ts.it_interval.tv_nsec = 0;

ts.it_value.tv_sec = 1;

ts.it_value.tv_nsec = 0;

ret = timer_settime(timer, 0, &ts, NULL);

if( ret )

perror("timer_settime");

取得一个定时器的到期时间

你可以在任何时刻由timer_gettime()取得一个定时器的到期时间而不会重置它:

#include

int timer_gettime(timer_t timerid, struct itimerspec *value);

一次成功的timer_gettime()调用会将timerid所指定的定时器的到期时间存入value所指定的结构并且返回0。执行失败时,此调用会返回-1并且将errno设定为下面的一个值:

EFAULT

value是一个无效的指针

EINVAL

timerid是一个无效的定时器

例如:

structitimerspec ts;

intret;

ret = timer_gettime(timer,&ts_get);

if( ret )

perror("timer_gettime");

else

{

printf("current sec = %ld, nsec=%ld/n", ts_get.it_value.tv_sec, ts_get.it_value.tv_nsec);

printf("next sec = %ld, nsec=ld/n", ts_get.it_interval.tv_sec, ts_get.it_interval.tv_nsec);

}

取得一个定时器的超限运行次数

POSIX定义了一个接口用于确定指定定时器上发生超限运行的次数(如果有):

#include

int timer_getoverrun(timer_t timerid);

执行成功时,timer_getoverrun()会返回定时器初次到期与通知进程(例如通过信号)定时器已到期之间额外发生的定时器到期次数。举例来说,在我们之前的例子中,一个1ms的定时器运行了10ms,则此调用会返回9。

如果超限运行的次数等于或大于DELAYTIMER_MAX,则此调用会返回DELAYTIMER_MAX。

执行失败时,此函数会返回-1并将errno设定会EINVAL,这个唯一的错误情况代表timerid指定了无效的定时器。

例如:

intret;

ret = timer_getoverrun(timer);

if( ret == -1)

perror("timer_getoverrun");

elseif(ret == 0)

printf("no overrun/n");

else

printf("%d overrun(s)/n", ret);

删除一个定时器

要删除一个定时器很容易:

#include

int timer_delete (timer_t timerid);

一次成功的timer_delete()调用会销毁关联到timerid的定时器并且返回0。执行失败时,此调用会返回-1并将errno设定会EINVAL,这个唯一的错误情况代表timerid不是一个有效的定时器。

自己写的小程序:

每隔5秒定时器就程序发出一个SIGALRM信号, 程序接收到这个信号后打印一个调试信息

#include

#include

#include

#include

#include

#include

#define TIMER_ENABLE 1 /* 时钟开启 */

#define DEBUGP(format,args...) fprintf(stdout, format, ##args) /* 调试信息打印 */

static void print_str(char *s) {

while(1) {

printf("%sn", s);

sleep(1);

}

}

static void sigtimer() {

DEBUGP("It's signal!n");

}

static void set_timer(int flag) {

struct itimerval tv;

memset(&tv, 0, sizeof(tv));

tv.it_interval.tv_sec = tv.it_value.tv_sec = flag ? 5 : 0;

tv.it_interval.tv_usec = tv.it_value.tv_usec = 0;

setitimer(ITIMER_REAL, &tv, NULL); /* ITIMER_REAL 返回SIGALARM信号给进程 */

}

int main(int argc, char **argv) {

if(argc < 2) {

printf("argv error!n");

exit(0);

}

/* 定义信号触发函数 */

signal(SIGALRM, sigtimer);

/* 初始化定时器 */

set_timer(TIMER_ENABLE);

print_str(argv[1]);

return 0;

}

你可能感兴趣的:(linux,定时器函数)