线程定时器

名称

       timer_create - create a POSIX per-process timer

摘要

       #include <signal.h>
       #include <time.h>
       int timer_create(clockid_t clockid, struct sigevent *sevp,
                        timer_t *timerid);
       Link with -lrt.


   特性测试宏 glibc (参见 feature_test_macros(7)):


       timer_create(): _POSIX_C_SOURCE >= 199309L

描述

       timer_create() 创建每个进程间隔计时器。 新计时器的 ID 由指向 timerid的指针参数返回,这个参数必须是一个非空指针。这个 ID 在计时器被删除前在进程内是唯一的。初始化的计时器是未启动的。
       参数 clockid 指出新的计时器使用何种来测度时间。它能被指定为以下值之一:
        CLOCK_REALTIME
              一个可设置的系统范围内的实时时钟,计算从1970年1月1日午夜0点起的毫秒数。
        CLOCK_MONOTONIC
              一个不可设置的 单调增加 时钟,它测度系统启动后某个非特定时间点起的时间。
        CLOCK_PROCESS_CPUTIME_ID  ( Linux 2.6.12 起有效)
              这个时钟测量调用 进程 (包含其中所有线程)所消耗的 (用户和系统) CPU 时间。
        CLOCK_THREAD_CPUTIME_ID  (Linux 2.6.12 起有效)
              这个时钟测量调用 线程 所消耗的 (用户和系统) CPU 时间。


       除了以上参数, clockid 可以指定为 clock_getcpuclockid(3) 或 pthread_getcpuclockid(3)的返回值。

       参数 sevp 指向一个 sigevent 数据结构,它指出计时器到期时,调用者应如何被通知这个事件。对于这个结构的定义及一般细节请参见 sigevent(7)。

       sevp.sigev_notify 域可置为如下值之一:

        SIGEV_NONE
              当计时器到期不进行异步通知。使用 timer_gettime(2) 可以监视计时器进展。
        SIGEV_SIGNAL
              到计时器到期时,在进程中产生信号量 sigev_signo 。一般细节参见 sigevent(7) 。siginfo_t结构的si_code 域将被设置为 SI_TIMER。在任何时间点,对于给定计时器最多仅有一个信号量被塞入队列,更多信息参见 timer_getoverrun(2) 。
        SIGEV_THREAD
              到计时器到期时,调用 sigev_notify_function 好像它是一个新线程的启动函数,细节参见 sigevent(7)。
        SIGEV_THREAD_ID  (Linux 特有)
              类似 SIGEV_SIGNAL,但信号量signal 针对由 sigev_notify_thread_id 指定的线程,这个线程必须和调用线程在同一个进程中。 sigev_notify_thread_id 域指定一个内核线程 ID,即这个值由clone(2) 或 gettid(2)返回。 这个标记仅限于线程库使用

       sevp 设置为 NULL 相当于设定一个 sigevent结构,其中sigev_notify 是 SIGEV_SIGNAL,sigev_signo 是 SIGALRM,sigev_value.sival_int 是计时器 ID。

返回值

       成功时,timer_create() 返回 0,新计时器的 ID 保存在 *timerid。 失败时返回 -1, errno 指出错误。

错误



       EAGAIN Temporary error during kernel allocation of timer structures.


       EINVAL Clock ID, sigev_notify, sigev_signo, or sigev_notify_thread_id is
              invalid.


       ENOMEM Could not allocate memory.

版本

       这个系统调用从 Linux 2.6 开始有效。

符合

       POSIX.1-2001.

注意



       A program may create multiple interval timers using timer_create().


       Timers are not inherited by the child of a fork(2), and are disarmed and
       deleted during an execve(2).

       The kernel preallocates a "queued real-time signal" for each timer created
       using timer_create().  Consequently, the number of timers is limited by the
       RLIMIT_SIGPENDING resource limit (see setrlimit(2)).

       The timers created by timer_create() are commonly known as "POSIX
       (interval) timers".  The POSIX timers API consists of the following
       interfaces:
  • timer_create(): 创建一个计时器
  • timer_settime(2):  装备(启动)或卸装 (停止)一个计时器。
  • timer_gettime(2):  抓取计时器下次到期前的剩余时间,以及计时器的间隔设置。
  • timer_getoverrun(2): 返回最近计时器到期的溢出计数。
  • timer_delete(2): 停止和删除计时器。
       POSIX 计时器 API 的部分实现由 glibc 提供,特别地:
  •  SIGEV_THREAD的功能由 glibc 实现,而不是内核。
  • 计时器 IDs 保持在用户层,由 glibc 维护,glibc 映射这些 ID 内核使用的计时器 ID。
       The POSIX timers system calls first appeared in Linux 2.6.  Prior to this,
       glibc provided an incomplete user-space implementation (CLOCK_REALTIME
       timers only) using POSIX threads, and current glibc falls back to this
       implementation on systems running pre-2.6 Linux kernels.

例程

       下面这个例程使用2个参数,一个是睡眠周期,以秒为单位,另一个是时钟间隔,以纳秒为单位。程序执行如下指令:

  1. 创建一个用于计时器的信号量
  2. 阻塞这个信号量
  3. 以给定周期创建并启动一个计时器
  4. 睡眠指定时间
  5. 去阻塞计时器信号量

       假设在程序睡眠期间计时器至少到期一次,信号量处理函数 handler 将被调用并显示有关计时器通知的信息。程序在信号量处理函数被调用一次后退出。

       以下程序在创建计时器后睡眠1秒钟,计时器周期为100纳秒当信号量被去阻塞并发出后,有约一千万(10,000,000)次超时。
[plain]  view plain  copy
  1. $ ./a.out 1 100  
  2. Establishing handler for signal 34  
  3. Blocking signal 34  
  4. timer ID is 0x804c008  
  5. Sleeping for 1 seconds  
  6. Unblocking signal 34  
  7. Caught signal 34  
  8.     sival_ptr = 0xbfb174f4;     *sival_ptr = 0x804c008  
  9.     overrun count = 10004886  

源码

[cpp]  view plain  copy
  1. #include <stdlib.h>  
  2. #include <unistd.h>  
  3. #include <stdio.h>  
  4. #include <signal.h>  
  5. #include <time.h>  
  6.   
  7. #define CLOCKID CLOCK_REALTIME  
  8. #define SIG SIGRTMIN  
  9.   
  10. #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \  
  11.                         } while (0)  
  12.   
  13. static void  
  14. print_siginfo(siginfo_t *si)  
  15. {  
  16.     timer_t *tidp;  
  17.     int or;  
  18.   
  19.   
  20.     tidp = si->si_value.sival_ptr;  
  21.   
  22.   
  23.     printf("    sival_ptr = %p; ", si->si_value.sival_ptr);  
  24.     printf("    *sival_ptr = 0x%lx\n", (long) *tidp);  
  25.   
  26.   
  27.     or = timer_getoverrun(*tidp);  
  28.     if (or == -1)  
  29.         errExit("timer_getoverrun");  
  30.     else  
  31.         printf("    overrun count = %d\n", or);  
  32. }  
  33.   
  34. static void  
  35. handler(int sig, siginfo_t *si, void *uc)  
  36. {  
  37.     /* Note: calling printf() from a signal handler is not 
  38.        strictly correct, since printf() is not async-signal-safe; 
  39.        see signal(7) */  
  40.   
  41.   
  42.     printf("Caught signal %d\n", sig);  
  43.     print_siginfo(si);  
  44.     signal(sig, SIG_IGN);  
  45. }  
  46.   
  47. int  
  48. main(int argc, char *argv[])  
  49. {  
  50.     timer_t timerid;  
  51.     struct sigevent sev;  
  52.     struct itimerspec its;  
  53.     long long freq_nanosecs;  
  54.     sigset_t mask;  
  55.     struct sigaction sa;  
  56.   
  57.     if (argc != 3) {  
  58.         fprintf(stderr, "Usage: %s <sleep-secs> <freq-nanosecs>\n",  
  59.                 argv[0]);  
  60.         exit(EXIT_FAILURE);  
  61.     }  
  62.   
  63.     /* Establish handler for timer signal */  
  64.   
  65.     printf("Establishing handler for signal %d\n", SIG);  
  66.     sa.sa_flags = SA_SIGINFO;  
  67.     sa.sa_sigaction = handler;  
  68.     sigemptyset(&sa.sa_mask);  
  69.     if (sigaction(SIG, &sa, NULL) == -1)  
  70.         errExit("sigaction");  
  71.   
  72.     /* Block timer signal temporarily */  
  73.   
  74.     printf("Blocking signal %d\n", SIG);  
  75.     sigemptyset(&mask);  
  76.     sigaddset(&mask, SIG);  
  77.     if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1)  
  78.         errExit("sigprocmask");  
  79.   
  80.     /* Create the timer */  
  81.   
  82.     sev.sigev_notify = SIGEV_SIGNAL;  
  83.     sev.sigev_signo = SIG;  
  84.     sev.sigev_value.sival_ptr = &timerid;  
  85.     if (timer_create(CLOCKID, &sev, &timerid) == -1)  
  86.         errExit("timer_create");  
  87.   
  88.     printf("timer ID is 0x%lx\n", (long) timerid);  
  89.   
  90.     /* Start the timer */  
  91.   
  92.     freq_nanosecs = atoll(argv[2]);  
  93.     its.it_value.tv_sec = freq_nanosecs / 1000000000;  
  94.     its.it_value.tv_nsec = freq_nanosecs % 1000000000;  
  95.     its.it_interval.tv_sec = its.it_value.tv_sec;  
  96.     its.it_interval.tv_nsec = its.it_value.tv_nsec;  
  97.   
  98.     if (timer_settime(timerid, 0, &its, NULL) == -1)  
  99.          errExit("timer_settime");  
  100.   
  101.     /* Sleep for a while; meanwhile, the timer may expire 
  102.        multiple times */  
  103.   
  104.     printf("Sleeping for %d seconds\n", atoi(argv[1]));  
  105.     sleep(atoi(argv[1]));  
  106.   
  107.     /* Unlock the timer signal, so that timer notification 
  108.        can be delivered */  
  109.   
  110.     printf("Unblocking signal %d\n", SIG);  
  111.     if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)  
  112.         errExit("sigprocmask");  
  113.   
  114.   
  115.     exit(EXIT_SUCCESS);  
  116. }  

参考

       clock_gettime(2), setitimer(2), timer_delete(2), timer_getoverrun(2),
       timer_settime(2), timerfd_create(2), clock_getcpuclockid(3),
       pthread_getcpuclockid(3), pthreads(7), sigevent(7), signal(7), time(7)

末记

       This page is part of release 3.50 of the Linux man-pages project.  A
       description of the project, and information about reporting bugs, can be

       found at http://www.kernel.org/doc/man-pages/.

Linux                               2010-09-27                     TIMER_CREATE(2)


以上 即 Linux man文档中的部分翻译,精力有限,不足以后增补。

由于上面的例程给出的是异步时钟,没有给出类似Win32中SetTimer 或 timeSetEvent 的计时器触发机制,网上找到sevp.sigev_notify 等于 SIGEV_THREAD时的例程,供学习参考。

[cpp]  view plain  copy
  1. /* 
  2.  * sigev_thread.c 
  3.  * 
  4.  * Demonstrate use of the SIGEV_THREAD signal mode to handle 
  5.  * signals by creating a new thread. 
  6.  * 
  7.  * Special notes: This program will not compile on Solaris 2.5. 
  8.  * It will compile on Digital UNIX 4.0 but will not work. 
  9.  * Digital UNIX 4.0c fixes SIGEV_THREAD, and sources inform me 
  10.  * that Solaris 2.6 will also fix SIGEV_THREAD. To try this on 
  11.  * Solaris 2.5, remove the "#ifdef sun" conditionals in main. 
  12.  */  
  13. #include <pthread.h>  
  14. #include <sys/signal.h>  
  15. #include <sys/time.h>  
  16. #include "errors.h"  
  17.   
  18. timer_t timer_id;  
  19. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  
  20. pthread_cond_t cond = PTHREAD_COND_INITIALIZER;  
  21. int counter = 0;  
  22.   
  23. /* 
  24.  * Thread start routine to notify the application when the 
  25.  * timer expires. This routine is run "as if" it were a new 
  26.  * thread, each time the timer expires. 
  27.  * 
  28.  * When the timer has expired 5 times, the main thread will 
  29.  * be awakened, and will terminate the program. 
  30.  */  
  31. void  
  32. timer_thread (void *arg)  
  33. {  
  34.     int status;  
  35.   
  36.     status = pthread_mutex_lock (&mutex);  
  37.     if (status != 0)  
  38.         err_abort (status, "Lock mutex");  
  39.     if (++counter >= 5) {  
  40.         status = pthread_cond_signal (&cond);  
  41.         if (status != 0)  
  42.             err_abort (status, "Signal condition");  
  43.     }  
  44.     status = pthread_mutex_unlock (&mutex);  
  45.     if (status != 0)  
  46.         err_abort (status, "Unlock mutex");  
  47.   
  48.     printf ("Timer %d\n", counter);  
  49. }  
  50.   
  51. main()  
  52. {  
  53.     int status;  
  54.     struct itimerspec ts;  
  55.     struct sigevent se;  
  56.   
  57. #ifdef sun  
  58.     fprintf (  
  59.         stderr,  
  60.         "This program cannot compile on Solaris 2.5.\n"  
  61.         "To build and run on Solaris 2.6, remove the\n"  
  62.         "\"#ifdef sun\" block in main().\n");  
  63. #else  
  64.     /* 
  65.      * Set the sigevent structure to cause the signal to be 
  66.      * delivered by creating a new thread. 
  67.      */  
  68.     se.sigev_notify = SIGEV_THREAD;  
  69.     se.sigev_value.sival_ptr = &timer_id;  
  70.     se.sigev_notify_function = timer_thread;  
  71.     se.sigev_notify_attributes = NULL;  
  72.   
  73.     /* 
  74.      * Specify a repeating timer that fires each 5 seconds. 
  75.      */  
  76.     ts.it_value.tv_sec = 5;  
  77.     ts.it_value.tv_nsec = 0;  
  78.     ts.it_interval.tv_sec = 5;  
  79.     ts.it_interval.tv_nsec = 0;  
  80.   
  81.     DPRINTF (("Creating timer\n"));  
  82.     status = timer_create(CLOCK_REALTIME, &se, &timer_id);  
  83.     if (status == -1)  
  84.         errno_abort ("Create timer");  
  85.   
  86.     DPRINTF ((  
  87.         "Setting timer %d for 5-second expiration...\n", timer_id));  
  88.     status = timer_settime(timer_id, 0, &ts, 0);  
  89.     if (status == -1)  
  90.         errno_abort ("Set timer");  
  91.   
  92.     status = pthread_mutex_lock (&mutex);  
  93.     if (status != 0)  
  94.         err_abort (status, "Lock mutex");  
  95.     while (counter < 5) {  
  96.         status = pthread_cond_wait (&cond, &mutex);  
  97.         if (status != 0)  
  98.             err_abort (status, "Wait on condition");  
  99.     }  
  100.     status = pthread_mutex_unlock (&mutex);  
  101.     if (status != 0)  
  102.         err_abort (status, "Unlock mutex");  
  103.   
  104. #endif /* Sun */  
  105.     return 0;  
  106. }  

猜你喜欢

转载自blog.csdn.net/c1194758555/article/details/79009188