TIMER_CREATE - Linux手册页
Linux程序员手册 第2部分
更新日期: 2020-04-11
名称
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指向的缓冲区中返回,该缓冲区必须是非空指针。该ID在该过程中是唯一的,直到删除计时器为止。新计时器最初被撤防。
clockid参数指定新计时器用来测量时间的时钟。可以将其指定为以下值之一:
- CLOCK_REALTIME
- 可设置的系统范围的实时时钟。
- CLOCK_MONOTONIC
- 不可设置的单调递增时钟,用于测量从过去某个未指定点开始的时间,该时间在系统启动后不会更改。
- CLOCK_PROCESS_CPUTIME_ID(since Linux 2.6.12)
- 一个时钟,用于测量调用进程(其中的所有线程)消耗的(用户和系统)CPU时间。
- CLOCK_THREAD_CPUTIME_ID(since Linux 2.6.12)
- 一个时钟,用于测量调用线程消耗的(用户和系统)CPU时间。
- CLOCK_BOOTTIME(Since Linux 2.6.39)
- 像CLOCK_MONOTONIC一样,这是一个单调递增的时钟。但是,尽管CLOCK_MONOTONIC时钟不测量系统挂起时的时间,但CLOCK_BOOTTIME时钟确实包括系统挂起的时间。这对于需要暂停感知的应用程序很有用。 CLOCK_REALTIME不适合此类应用,因为该时钟受系统时钟不连续更改的影响。
- CLOCK_REALTIME_ALARM(since Linux 3.0)
- 该时钟类似于CLOCK_REALTIME,但是如果挂起它将唤醒系统。调用者必须具有CAP_WAKE_ALARM功能才能针对此时钟设置计时器。
- CLOCK_BOOTTIME_ALARM(since Linux 3.0)
- 该时钟类似于CLOCK_BOOTTIME,但是如果挂起了系统,它将唤醒系统。调用者必须具有CAP_WAKE_ALARM功能才能针对此时钟设置计时器。
- CLOCK_TAI(since Linux 3.10)
- 从挂钟时间派生的系统范围时钟,但忽略leap秒。
有关上述时钟的更多详细信息,请参见clock_getres(2)。
与上述值一样,可以将clockid指定为对clock_getcpuclockid(3)或pthread_getcpuclockid(3)的调用返回的时钟ID。
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-specific)
- 与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
- 时钟ID,sigev_notify,sigev_signo或sigev_notify_thread_id无效。
- ENOMEM
- 无法分配内存。
- ENOTSUP
- 内核不支持针对此时钟标识创建计时器。
- EPERM
- 时钟ID为CLOCK_REALTIME_ALARM或CLOCK_BOOTTIME_ALARM,但呼叫者没有CAP_WAKE_ALARM功能。
版本
从Linux 2.6开始,此系统调用可用。
遵循规范
POSIX.1-2001,POSIX.1-2008。
备注
程序可以使用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):撤防并删除计时器。
从Linux 3.10开始,/ proc / [pid] / timers文件可用于列出具有PID pid的进程的POSIX计时器。有关更多信息,请参见proc(5)。
从Linux 4.10开始,对POSIX计时器的支持是默认情况下启用的可配置选项。可以通过CONFIG_POSIX_TIMERS选项禁用内核支持。
C library/kernel differences
glibc提供了POSIX计时器API的部分实现。特别是:
- *
- SIGEV_THREAD的许多功能是在glibc中而不是内核中实现的。 (这确实是必须的,因为处理通知的线程是必须由C库的POSIX线程实现管理的线程。)尽管传递给进程的通知是通过线程实现的,但内部NPTL实现使用的sigev_notify值为SIGEV_THREAD_ID以及由实现保留的实时信号(请参阅nptl(7))。
- *
- evp为NULL的默认情况的实现是在glibc内部处理的,它会使用适当填充的sigevent结构调用基础系统调用。
- *
- 在用户级别显示的计时器ID由glibc维护,后者将这些ID映射到内核采用的计时器ID。
POSIX计时器系统调用最早出现在Linux 2.6中。在此之前,glibc使用POSIX线程提供了一个不完整的用户空间实现(仅CLOCK_REALTIME计时器),并且在2.17之前的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
Program source
#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 safe
(and should not be done in production programs), since
printf() is not async-signal-safe; see signal-safety(7).
Nevertheless, we use printf() here as a simple way of
showing that the handler was called. */
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),信号(7),时间(7)
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。

