名称
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): 停止和删除计时器。
- SIGEV_THREAD的功能由 glibc 实现,而不是内核。
- 计时器 IDs 保持在用户层,由 glibc 维护,glibc 映射这些 ID 内核使用的计时器 ID。
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)
{
/* 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)
末记
This page is part of release 3.50 of the Linux man-pages project. Adescription 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时的例程,供学习参考。
/*
* sigev_thread.c
*
* Demonstrate use of the SIGEV_THREAD signal mode to handle
* signals by creating a new thread.
*
* Special notes: This program will not compile on Solaris 2.5.
* It will compile on Digital UNIX 4.0 but will not work.
* Digital UNIX 4.0c fixes SIGEV_THREAD, and sources inform me
* that Solaris 2.6 will also fix SIGEV_THREAD. To try this on
* Solaris 2.5, remove the "#ifdef sun" conditionals in main.
*/
#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;
/*
* Thread start routine to notify the application when the
* timer expires. This routine is run "as if" it were a new
* thread, each time the timer expires.
*
* When the timer has expired 5 times, the main thread will
* be awakened, and will terminate the program.
*/
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
/*
* Set the sigevent structure to cause the signal to be
* delivered by creating a new thread.
*/
se.sigev_notify = SIGEV_THREAD;
se.sigev_value.sival_ptr = &timer_id;
se.sigev_notify_function = timer_thread;
se.sigev_notify_attributes = NULL;
/*
* Specify a repeating timer that fires each 5 seconds.
*/
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;
}