Linux的定时器

在服务端程序设计中,与时间有关的常见任务有:

  1. 获取当前时间,计算时间间隔;
  2. 定时操作,比如在预定的时间执行一项任务,或者在一段延时之后执行一项任务。

Linux 时间函数

Linux 的计时函数,用于获得当前时间:

  • time(2) / time_t (秒)
  • ftime(3) / struct timeb (毫秒)
  • gettimeofday(2) / struct timeval (微秒)
  • clock_gettime(2) / struct timespec (纳秒)
  • gmtime / localtime / timegm / mktime / strftime / struct tm (这些与当前时间无关)

定时函数,用于让程序等待一段时间或安排计划任务:

  • sleep
  • alarm
  • getitimer / setitimer
  • timer_create / timer_settime / timer_gettime / timer_delete
  • timerfd_create / timerfd_gettime / timerfd_settime
  • 条件变量pthread_cond_timedwait实现
  • IO多路复用select, epoll实现

一般情况下

获取当前时间常用

gettimerofday,因为它的精度是1us,并且在x86平台上它是用户态实现的,没有系统调用和上下文切换的开销。

定时函数中:

  1. sleep / alarm / usleep 在实现时有可能用了信号 SIGALRM,在多线程程序中处理信号是个相当麻烦的事情,应当尽量避免。(近期我会写一篇博客仔细讲讲“多线程、RAII、fork() 与信号”)
  2. nanosleep 和 clock_nanosleep 是线程安全的,但是在非阻塞网络编程中,绝对不能用让线程挂起的方式来等待一段时间,程序会失去响应。正确的做法是注册一个时间回调函数。
  3. getitimer 和 timer_create 也是用信号来 deliver 超时,在多线程程序中也会有麻烦。timer_create 可以指定信号的接收方是进程还是线程,算是一个进步,不过在信号处理函数(signal handler)能做的事情实在很受限。
  4. timerfd_create 把时间变成了一个文件描述符,该“文件”在定时器超时的那一刻变得可读,这样就能很方便地融入到 select/poll 框架中,用统一的方式来处理 IO 事件和超时事件。l
  5. 利用select, epoll的timeout实现定时功能,它们的缺点是定时精度只有毫秒,远低于 timerfd_settime 的定时精度。

 

 实现

 下面是用timer_create实现的一个定时器:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>

void sig_handler(int signo)
{
    switch(signo) {
    case SIGUSR1:
        printf("receive sigusr1! \n");
        break;
    case SIGALRM:
        printf("receive sigarlm!\n");
        break;
    }
}

int main()
{
    /** 
     struct sigaction
     { 
     void (*sa_handler)(int);信号响应函数地址
     void (*sa_sigaction)(int, siginfo_t *, void *);   但sa_flags为SA——SIGINFO时才使用
     sigset_t sa_mask;         说明一个信号集在调用捕捉函数之前,会加入进程的屏蔽中,当捕捉函数返回时,还原
     int sa_flags;
     void (*sa_restorer)(void);未用
     }; 
    */
    timer_t timer1, timer2;
    struct sigevent evp1, evp2;
    struct sigaction act;

    //for timer1
    memset(&act, 0, sizeof(act));
    act.sa_handler = sig_handler;
    act.sa_flags = 0;

    sigemptyset(&act.sa_mask);

    if (sigaction(SIGUSR1, &act, NULL) == -1) {
        perror("fail to sigaction");
        exit(-1);
    }

    //for timer2 
    memset(&act, 0, sizeof(act));
    act.sa_handler = sig_handler;
    act.sa_flags = 0;

    sigemptyset(&act.sa_mask);

    if (sigaction(SIGALRM, &act, NULL) == -1) {
        perror("fail to sigaction");
        exit(-1);
    }
    //for timer1
    memset(&evp1, 0, sizeof(struct sigevent));
    evp1.sigev_signo = SIGUSR1;
    evp1.sigev_notify = SIGEV_SIGNAL;
    if (timer_create(CLOCK_REALTIME, &evp1, &timer1) == -1) {
        perror("fail to timer_create");
        exit(-1);
    }

    struct itimerspec it;
    it.it_interval.tv_sec = 2;
    it.it_interval.tv_nsec = 0;
    it.it_value.tv_sec = 1;
    it.it_value.tv_nsec = 0;
    if (timer_settime(timer1, 0, &it, 0) == -1) {
        perror("fail to timer_settime");
        exit(-1);
    }

    //for timer2
    memset(&evp2, 0, sizeof(struct sigevent));
    evp2.sigev_signo = SIGALRM;
    evp2.sigev_notify = SIGEV_SIGNAL;
    if (timer_create(CLOCK_REALTIME, &evp2, &timer2) == -1) {
        perror("fail to timer_create");
        exit(-1);
    }
    it.it_interval.tv_sec = 4;
    it.it_interval.tv_nsec = 0;
    it.it_value.tv_sec = 2;
    it.it_value.tv_nsec = 0;
    if (timer_settime(timer2, 0, &it, 0) == -1) {
         perror("fail to timer_settime");
         exit(-1);
    }

    for(;;);

    return 0;
}

  以及一个用setitimer实现的定时器

setitimer中的第一个参数有三类:

 这里说得很清楚。

ITIMER_REAL    decrements in real time, and delivers SIGALRM upon expiration.

ITIMER_VIRTUAL decrements only when the process is executing, and delivers SIGVTALRM upon expiration.

ITIMER_PROF    decrements both when the process executes and when the system is executing on behalf of the process.  Coupled with ITIMER_VIRTUAL, this timer is usually used to profile the time spent by the application in user and kernel space.  SIGPROF is delivered upon expiration.
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>

void timer_handler (int signum)
{
    switch(signum) {
    case SIGALRM:
        printf("sigarlm !\n");
        break;
    case SIGVTALRM:
        printf("sigvtalrm !\n");
        break;
    }
}

int main ()
{
    struct sigaction sa;
    struct itimerval timer;
    memset (&sa, 0, sizeof (sa));
    sa.sa_handler = &timer_handler;
    sigaction (SIGVTALRM, &sa, NULL);

    sa.sa_handler = &timer_handler;
    sigaction(SIGALRM, &sa, NULL);

    timer.it_value.tv_sec = 0;
    timer.it_value.tv_usec = 250000;
   timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = 250000;

    setitimer (ITIMER_REAL, &timer, NULL);

    timer.it_value.tv_sec = 1;
    timer.it_value.tv_usec = 0;

    timer.it_interval.tv_sec = 1;
    timer.it_interval.tv_usec = 0;
    setitimer (ITIMER_VIRTUAL, &timer, NULL);

    while (1);
}

  

参考:http://www.cnblogs.com/Solstice/archive/2011/02/06/1949555.html

posted @ 2014-03-26 21:37  CobbLiu  阅读(7166)  评论(0编辑  收藏  举报