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/。