Chinaunix首页 | 论坛 | 博客
  • 博客访问: 696467
  • 博文数量: 183
  • 博客积分: 2650
  • 博客等级: 少校
  • 技术积分: 1428
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-22 17:02
文章分类
文章存档

2017年(1)

2015年(46)

2014年(4)

2013年(8)

2012年(2)

2011年(27)

2010年(35)

2009年(60)

分类: LINUX

2011-01-23 20:39:11

时间间隔定时器

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:

 

  1. timer_t timer;  
  2. int ret;  
  3.   
  4. ret = timer_create(CLOCK_PROCESS_CPUTIME_ID, NULL, &timer);  
  5. if(ret)  
  6.     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设定成存储定时器标识符的地址:

 

 

  1. struct sigevent evp;  
  2. timer_t timer;  
  3. int ret;  
  4.   
  5. evp.sigev_value.sival_ptr = &timer;  
  6. evp.sigev_notify = SIGEV_SIGNAL;  
  7. evp.sigev_signo = SIGUSR1;  
  8.   
  9. ret = timer_create(CLOCK_REALTIME, &evp, &timer);  
  10.   
  11. if( ret)  
  12.     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值,下面的例子可以创建一个每秒到期一次的周期性定时器:

  1. struct itimerspec ts;  
  2. int ret;  
  3.   
  4.        ts.it_interval.tv_sec = 1;  
  5.        ts.it_interval.tv_nsec = 0;  
  6.        ts.it_value.tv_sec = 1;  
  7.        ts.it_value.tv_nsec = 0;  
  8.   
  9.        ret = timer_settime(timer, 0, &ts, NULL);  
  10.        if( ret )  
  11.                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是一个无效的定时器

例如:

  1. struct itimerspec ts;  
  2. int ret;  
  3.   
  4. ret = timer_gettime(timer,&ts_get);  
  5.        if( ret )  
  6.                perror("timer_gettime");  
  7.        else  
  8.        {  
  9.                printf("current sec = %ld, nsec=%ld\n", ts_get.it_value.tv_sec, ts_get.it_value.tv_nsec);  
  10.                printf("next sec = %ld, nsec=ld\n", ts_get.it_interval.tv_sec, ts_get.it_interval.tv_nsec);  
  11.        }  

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

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

#include

int timer_getoverrun(timer_t timerid);

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

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

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

例如:

  1. int ret;  
  2.   
  3. ret = timer_getoverrun(timer);  
  4.        if( ret == -1)  
  5.                perror("timer_getoverrun");  
  6.        else if (ret == 0)  
  7.                printf("no overrun\n");  
  8.        else  
  9.                printf("%d overrun(s)\n", ret);  

删除一个定时器

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

#include

int timer_delete (timer_t timerid);

 

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

阅读(1058) | 评论(0) | 转发(0) |
0

上一篇:C语言的inline

下一篇:Linux下时间和定时器

给主人留下些什么吧!~~