timer_create(2) - Linux 手册页

名称

timer_create - 创建一个 POSIX 进程定时器

概要

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

int timer_create(clockid_t clockid, struct sigevent *sevp,
                 timer_t *timerid);
链接 -lrt

glibc 的功能测试宏要求(参见 feature_test_macros(7))

timer_create(): _POSIX_C_SOURCE >= 199309L

描述

timer_create() 创建一个新的进程定时器。新定时器的 ID 将被放入 timerid 指向的缓冲区中,该缓冲区必须是一个非 NULL 指针。此 ID 在进程内是唯一的,直到定时器被删除。新的定时器最初处于未激活状态。

clockid 参数指定新定时器用来测量时间的时钟。它可以指定为以下值之一

CLOCK_REALTIME
一个可设置的系统范围内的实时时钟。
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) 调用返回的 clockid

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 类似,但信号是针对与调用者位于同一进程中的线程,其 ID 由 sigev_notify_thread_id 指定。sigev_notify_thread_id 字段指定一个内核线程 ID,即由 clone(2) 或 gettid(2) 返回的值。此标志仅供线程库使用。
sevp 指定为 NULL 等效于指定一个 sigevent 结构体的指针,其中 sigev_notifySIGEV_SIGNALsigev_signoSIGALRM,并且 sigev_value.sival_int 是定时器 ID。

返回值

成功时,timer_create() 返回 0,并将新定时器的 ID 放入 *timerid 中。失败时,返回 -1,并且 errno 被设置为指示错误。

错误

EAGAIN

内核分配定时器结构时发生临时错误。

EINVAL

Clock ID、sigev_notifysigev_signosigev_notify_thread_id 无效。

ENOMEM

无法分配内存。

版本

此系统调用自 Linux 2.6 起可用。

符合

POSIX.1-2001。

说明

程序可以使用 timer_create() 创建多个间隔定时器。

定时器不会被 fork(2) 的子进程继承,并且在 execve(2) 期间会被解除激活并删除。

内核为使用 timer_create() 创建的每个定时器预分配一个“排队的实时信号”。因此,定时器的数量受 RLIMIT_SIGPENDING 资源限制的限制(请参阅 setrlimit(2))。

timer_create() 创建的定时器通常被称为“POSIX(间隔)定时器”。POSIX 定时器 API 由以下接口组成

*

timer_create(): 创建一个定时器。

*

timer_settime(2): 激活(启动)或解除激活(停止)定时器。

*

timer_gettime(2): 获取到定时器下一次到期的剩余时间,以及定时器的间隔设置。

*

timer_getoverrun(2): 返回上次定时器到期的超次数。

*

timer_delete(2): 解除激活并删除定时器。

POSIX 定时器 API 的部分实现由 glibc 提供。特别是
*

SIGEV_THREAD 的功能是在 glibc 内实现的,而不是在内核中。

*

用户级别呈现的定时器 ID 由 glibc 维护,glibc 将这些 ID 映射到内核使用的定时器 ID。

POSIX 定时器系统调用首次出现在 Linux 2.6 中。在此之前,glibc 提供了使用 POSIX 线程的不完整用户空间实现(仅 CLOCK_REALTIME 定时器),并且当前的 glibc 在运行低于 2.6 的 Linux 内核的系统上回退到此实现。

示例

下面的程序接受两个参数:秒数内的睡眠时间,以及纳秒内的定时器频率。该程序为它用于定时器的信号建立处理程序,阻塞该信号,创建一个以给定频率到期的定时器,睡眠指定的秒数,然后取消阻塞定时器信号。假设定时器在程序睡眠期间至少到期一次,则将调用信号处理程序,并且该处理程序将显示有关定时器通知的一些信息。程序在调用一次信号处理程序后终止。

在下面的示例运行中,程序在创建频率为 100 纳秒的定时器后睡眠 1 秒。到信号被取消阻塞并传递时,已经发生了大约一千万次超次数。

$ ./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)

引用自

clock_nanosleep(2), getitimer(2), nanosleep(2), sigaction(2), ualarm(3), usleep(3)