名称
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个参数,一个是睡眠周期,以秒为单位,另一个是时钟间隔,以纳秒为单位。程序执行如下指令:
- 创建一个用于计时器的信号量
- 阻塞这个信号量
- 以给定周期创建并启动一个计时器
- 睡眠指定时间
- 去阻塞计时器信号量
假设在程序睡眠期间计时器至少到期一次,信号量处理函数 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)
- {
-
-
-
-
-
- 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);
- }
-
-
-
- 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");
-
-
-
- printf("Blocking signal %d\n", SIG);
- sigemptyset(&mask);
- sigaddset(&mask, SIG);
- if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1)
- errExit("sigprocmask");
-
-
-
- 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);
-
-
-
- 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");
-
-
-
-
- printf("Sleeping for %d seconds\n", atoi(argv[1]));
- sleep(atoi(argv[1]));
-
-
-
-
- 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时的例程,供学习参考。
-
-
-
-
-
-
-
-
-
-
-
-
- #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;
-
-
-
-
-
-
-
-
-
- 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
-
-
-
-
- se.sigev_notify = SIGEV_THREAD;
- se.sigev_value.sival_ptr = &timer_id;
- se.sigev_notify_function = timer_thread;
- se.sigev_notify_attributes = NULL;
-
-
-
-
- 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;
- }