linux 定时轮训任务实现

#include <stdio.h>

#include <signal.h>

#include <sys/time.h>



int main(int argc, char *argv[])

{

        sigset_t block;

        struct itimerval itv;



        sigemptyset(&block);

        sigaddset(&block, SIGALRM);

        sigprocmask(SIG_BLOCK, &block, NULL);



        itv.it_interval.tv_sec = 2;

        itv.it_interval.tv_usec = 0;

        itv.it_value = itv.it_interval;

        setitimer(ITIMER_REAL, &itv, NULL);



        while (1) {

                printf("%d\n", time(NULL));

                sigwaitinfo(&block, NULL);

        }



        return 0;

}


进程的间隔计时器能够提供的时间精度为微秒,对于大多数的应用来说,应该已经足够,如果需要更高的时间精度,或者需要多个定时器,那么每个进程一个的实时间隔定时器就无能为力了,这个时候我们可以选择POSIX实时扩展中的定时器:



      #include <signal.h>
       #include <time.h>

       int timer_create(clockid_t clockid, struct sigevent *restrict evp,
              timer_t *restrict timerid); 
       int timer_getoverrun(timer_t timerid);
       int timer_gettime(timer_t timerid, struct itimerspec *value);
       int timer_settime(timer_t timerid, int flags,
              const struct itimerspec *restrict value,
              struct itimerspec *restrict ovalue);
它实际上就是进程间隔定时器的增强版,除了可以定制时钟源(nanosleep也存在能定制时钟源的版本:clock_nanosleep)和时间精度提高到纳秒外,它还能通过将evp->sigev_notify设定为如下值来定制定时器到期后的行为:

  • SIGEV_SIGNAL: 发送由evp->sigev_sino指定的信号到调用进程,evp->sigev_value的值将被作为siginfo_t结构体中si_value的值。
  • SIGEV_NONE:什么都不做,只提供通过timer_gettime和timer_getoverrun查询超时信息。
  • SIGEV_THREAD: 以evp->sigev_notification_attributes为线程属性创建一个线程,在新建的线程内部以evp->sigev_value为参数调用evp->sigev_notification_function。
  • SIGEV_THREAD_ID:和SIGEV_SIGNAL类似,不过它只将信号发送到线程号为evp->sigev_notify_thread_id的线程,注意:这里的线程号不一定是POSIX线程号,而是线程调用gettid返回的实际线程号,并且这个线程必须实际存在且属于当前的调用进程。
更新后的程序如下(需要连接实时扩展库: -lrt)

#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <sched.h>

int main(int argc, char *argv[])
{
        timer_t timer;
        struct itimerspec timeout;
        sigset_t block;
        struct sched_param param;

        sigemptyset(&block);
        sigaddset(&block, SIGALRM);
        sigprocmask(SIG_BLOCK, &block, NULL);

        timer_create(CLOCK_MONOTONIC, NULL, &timer);
        timeout.it_interval.tv_sec = 2;
        timeout.it_interval.tv_nsec = 0;
        timeout.it_value = timeout.it_interval;
        timer_settime(timer, 0, &timeout, NULL);

        while (1) {
                fprintf(stderr, "%d\n", time(NULL));
                sigwaitinfo(&block, NULL);
        }

        return 0;
}

至于时钟源为什么是CLOCK_MONOTONIC而不是CLOCK_REALTIME,主要是考虑到系统的实时时钟可能会在程序运行过程中更改,所以存在一定的不确定性,而CLOCK_MONOTONIC则不会,较为稳定。

至此为止,我们已经找到了目前Linux提供的精度最高的定时器API,它应该能满足大多数情况的要求了

sitemap