TIMER_CREATE - Linux手册页

时间:2019-08-20 17:59:34  来源:igfitidea点击:

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/