linux API 文档 timer_create(2)

名称

       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)次超时。
           $ ./a.out 1 100
           Establishing handler for signal 34
           Blocking signal 34
           timer ID is 0x804c008
           Sleeping for 1 seconds
           Unblocking signal 34
           Caught signal 34
               sival_ptr = 0xbfb174f4;     *sival_ptr = 0x804c008
               overrun count = 10004886

源码

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

       #define CLOCKID CLOCK_REALTIME
       #define SIG SIGRTMIN

       #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                               } while (0)

       static void
       print_siginfo(siginfo_t *si)
       {
           timer_t *tidp;
           int or;


           tidp = si->si_value.sival_ptr;


           printf("    sival_ptr = %p; ", si->si_value.sival_ptr);
           printf("    *sival_ptr = 0x%lx\n", (long) *tidp);


           or = timer_getoverrun(*tidp);
           if (or == -1)
               errExit("timer_getoverrun");
           else
               printf("    overrun count = %d\n", or);
       }

       static void
       handler(int sig, siginfo_t *si, void *uc)
       {
           /* Note: calling printf() from a signal handler is not
              strictly correct, since printf() is not async-signal-safe;
              see signal(7) */


           printf("Caught signal %d\n", sig);
           print_siginfo(si);
           signal(sig, SIG_IGN);
       }

       int
       main(int argc, char *argv[])
       {
           timer_t timerid;
           struct sigevent sev;
           struct itimerspec its;
           long long freq_nanosecs;
           sigset_t mask;
           struct sigaction sa;

           if (argc != 3) {
               fprintf(stderr, "Usage: %s <sleep-secs> <freq-nanosecs>\n",
                       argv[0]);
               exit(EXIT_FAILURE);
           }

           /* Establish handler for timer signal */

           printf("Establishing handler for signal %d\n", SIG);
           sa.sa_flags = SA_SIGINFO;
           sa.sa_sigaction = handler;
           sigemptyset(&sa.sa_mask);
           if (sigaction(SIG, &sa, NULL) == -1)
               errExit("sigaction");

           /* Block timer signal temporarily */

           printf("Blocking signal %d\n", SIG);
           sigemptyset(&mask);
           sigaddset(&mask, SIG);
           if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1)
               errExit("sigprocmask");

           /* Create the timer */

           sev.sigev_notify = SIGEV_SIGNAL;
           sev.sigev_signo = SIG;
           sev.sigev_value.sival_ptr = &timerid;
           if (timer_create(CLOCKID, &sev, &timerid) == -1)
               errExit("timer_create");

           printf("timer ID is 0x%lx\n", (long) timerid);

           /* Start the timer */

           freq_nanosecs = atoll(argv[2]);
           its.it_value.tv_sec = freq_nanosecs / 1000000000;
           its.it_value.tv_nsec = freq_nanosecs % 1000000000;
           its.it_interval.tv_sec = its.it_value.tv_sec;
           its.it_interval.tv_nsec = its.it_value.tv_nsec;

           if (timer_settime(timerid, 0, &its, NULL) == -1)
                errExit("timer_settime");

           /* Sleep for a while; meanwhile, the timer may expire
              multiple times */

           printf("Sleeping for %d seconds\n", atoi(argv[1]));
           sleep(atoi(argv[1]));

           /* Unlock the timer signal, so that timer notification
              can be delivered */

           printf("Unblocking signal %d\n", SIG);
           if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
               errExit("sigprocmask");


           exit(EXIT_SUCCESS);
       }

参考

       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时的例程,供学习参考。

/*
 * sigev_thread.c
 *
 * Demonstrate use of the SIGEV_THREAD signal mode to handle
 * signals by creating a new thread.
 *
 * Special notes: This program will not compile on Solaris 2.5.
 * It will compile on Digital UNIX 4.0 but will not work.
 * Digital UNIX 4.0c fixes SIGEV_THREAD, and sources inform me
 * that Solaris 2.6 will also fix SIGEV_THREAD. To try this on
 * Solaris 2.5, remove the "#ifdef sun" conditionals in main.
 */
#include <pthread.h>
#include <sys/signal.h>
#include <sys/time.h>
#include "errors.h"

timer_t timer_id;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int counter = 0;

/*
 * Thread start routine to notify the application when the
 * timer expires. This routine is run "as if" it were a new
 * thread, each time the timer expires.
 *
 * When the timer has expired 5 times, the main thread will
 * be awakened, and will terminate the program.
 */
void
timer_thread (void *arg)
{
    int status;

    status = pthread_mutex_lock (&mutex);
    if (status != 0)
        err_abort (status, "Lock mutex");
    if (++counter >= 5) {
        status = pthread_cond_signal (&cond);
        if (status != 0)
            err_abort (status, "Signal condition");
    }
    status = pthread_mutex_unlock (&mutex);
    if (status != 0)
        err_abort (status, "Unlock mutex");

    printf ("Timer %d\n", counter);
}

main()
{
    int status;
    struct itimerspec ts;
    struct sigevent se;

#ifdef sun
    fprintf (
        stderr,
        "This program cannot compile on Solaris 2.5.\n"
        "To build and run on Solaris 2.6, remove the\n"
        "\"#ifdef sun\" block in main().\n");
#else
    /*
     * Set the sigevent structure to cause the signal to be
     * delivered by creating a new thread.
     */
    se.sigev_notify = SIGEV_THREAD;
    se.sigev_value.sival_ptr = &timer_id;
    se.sigev_notify_function = timer_thread;
    se.sigev_notify_attributes = NULL;

    /*
     * Specify a repeating timer that fires each 5 seconds.
     */
    ts.it_value.tv_sec = 5;
    ts.it_value.tv_nsec = 0;
    ts.it_interval.tv_sec = 5;
    ts.it_interval.tv_nsec = 0;

    DPRINTF (("Creating timer\n"));
    status = timer_create(CLOCK_REALTIME, &se, &timer_id);
    if (status == -1)
        errno_abort ("Create timer");

    DPRINTF ((
        "Setting timer %d for 5-second expiration...\n", timer_id));
    status = timer_settime(timer_id, 0, &ts, 0);
    if (status == -1)
        errno_abort ("Set timer");

    status = pthread_mutex_lock (&mutex);
    if (status != 0)
        err_abort (status, "Lock mutex");
    while (counter < 5) {
        status = pthread_cond_wait (&cond, &mutex);
        if (status != 0)
            err_abort (status, "Wait on condition");
    }
    status = pthread_mutex_unlock (&mutex);
    if (status != 0)
        err_abort (status, "Unlock mutex");

#endif /* Sun */
    return 0;
}


你可能感兴趣的:(linux API 文档 timer_create(2))