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_notify 是 SIGEV_SIGNAL,sigev_signo 是 SIGALRM,并且 sigev_value.sival_int 是定时器 ID。
返回值
成功时,timer_create() 返回 0,并将新定时器的 ID 放入 *timerid 中。失败时,返回 -1,并且 errno 被设置为指示错误。
错误
- EAGAIN
内核分配定时器结构时发生临时错误。
- EINVAL
Clock ID、sigev_notify、sigev_signo 或 sigev_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)