Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2818479
  • 博文数量: 272
  • 博客积分: 5544
  • 博客等级: 大校
  • 技术积分: 5496
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-08 00:48
个人简介

  每个人都要有一个骨灰级的爱好,不为金钱,而纯粹是为了在这个领域享受追寻真理的快乐。

文章分类

全部博文(272)

文章存档

2015年(2)

2014年(5)

2013年(25)

2012年(58)

2011年(182)

分类: LINUX

2012-01-13 10:36:04

转载: http://blog.csdn.net/billpig/article/details/5571929

时间间隔定时器

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:

 

 

[c-sharp] view plaincopy
  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设定成存储定时器标识符的地址:

 

 

 

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

 


[c-sharp] view plaincopy
  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是一个无效的定时器

 

例如:

 


[c-sharp] view plaincopy
  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指定了无效的定时器。

 

例如:

 


[c-sharp] view plaincopy
  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不是一个有效的定时器。



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

  1. #include <stdio.h>
  2. #include <sys/time.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. #include <unistd.h>
  6. #include <signal.h>
  7. #define TIMER_ENABLE 1 /* 时钟开启 */
  8. #define DEBUGP(format,args...) fprintf(stdout, format, ##args) /* 调试信息打印 */

  9. static void print_str(char *s) {
  10.     while(1) {
  11.         printf("%sn", s);
  12.         sleep(1);
  13.     }
  14. }

  15. static void sigtimer() {
  16.     DEBUGP("It's signal!n");
  17. }

  18. static void set_timer(int flag) {
  19.     struct itimerval tv;

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

  21.     tv.it_interval.tv_sec = tv.it_value.tv_sec = flag ? 5 : 0;
  22.     tv.it_interval.tv_usec = tv.it_value.tv_usec = 0;
  23.     setitimer(ITIMER_REAL, &tv, NULL); /* ITIMER_REAL 返回SIGALARM信号给进程 */
  24. }

  25. int main(int argc, char **argv) {
  26.     if(argc < 2) {
  27.         printf("argv error!n");
  28.         exit(0);
  29.     }
  30.     
  31.     /* 定义信号触发函数 */
  32.     signal(SIGALRM, sigtimer);

  33.     /* 初始化定时器 */
  34.     set_timer(TIMER_ENABLE);

  35.     print_str(argv[1]);

  36.     return 0;
  37. }





阅读(2612) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~