Linux 定时器

时间间隔定时器

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

 

#include <sys/time.h>

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 <signal.h>

#include <time.h>

 

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

 

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

 

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

 

 

 

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

 

EAGAIN

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

EINVAL

    clockid指定了无效的POSIX时钟

ENOTSUP

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

 

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

 

#include <signal.h>

 

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设定成存储定时器标识符的地址:

 

 

 

 

启动一个定时器

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

 

#include <time.h>

 

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值,下面的例子可以创建一个每秒到期一次的周期性定时器:

 

 

 

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

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

 

#include <time.h>

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

 

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

 

EFAULT

    value是一个无效的指针

EINVAL

    timerid是一个无效的定时器

 

例如:

 

 

 

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

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

 

#include <time.h>

int timer_getoverrun(timer_t timerid);

 

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

 

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

 

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

 

例如:

 

 

 

删除一个定时器

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

 

#include <time.h>

int timer_delete (timer_t timerid);

 

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

 

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值