我是否必须为 Posix 计时器使用信号处理程序?

Do I have to use a signal handler for a Posix timer?

我想启动一个计时器并在它到期时调用一个函数。

谷歌搜索了很多示例,包括手册中的示例,所有示例都使用sigaction()设置信号处理程序。

但是,@Patryk 在 this question 中说我们可以

void cbf(union sigval);
struct sigevent sev;
timer_t timer;

sev.sigev_notify = SIGEV_THREAD;
sev.sigev_notify_function = cbf; //this function will be called when timer expires
sev.sigev_value.sival_ptr = (void*) arg;//this argument will be passed to cbf
timer_create(CLOCK_MONOTONIC, &sev, &timer);

哪个更短、更简单、更清晰、更易于维护...

什么给了?这个对吗?它只是 sigaction() 的包装器吗?为什么示例明确设置信号处理程序?

此外,如果我通过此方法或通过 timer_settime 和信号处理程序启动计时器,将取消计时器,因为系统会删除该计时器与回调之间的关联,或者我是否必须明确地这样做吗?


[更新] 您可以选择 信号或我在下面的回答中显示的方法(或两者都选择,但这看起来很傻).这是一个品味问题。 Singals 可能会以复杂化为代价提供更多的功能。

如果你只想启动一个计时器并在它到期时得到通知,我的答案中的方法最简单。

似乎我不必使用信号处理程序并且可以使代码更简单,如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>

static unsigned int pass_value_by_pointer = 42;

void Timer_has_expired(union sigval timer_data)
{
    printf("Timer expiration handler function; %d\n", *(int *) timer_data.sival_ptr);
}

int main(void)
{
    struct sigevent timer_signal_event;
    timer_t timer;

    struct itimerspec timer_period;

    printf("Create timer\n");
    timer_signal_event.sigev_notify = SIGEV_THREAD;
    timer_signal_event.sigev_notify_function = Timer_has_expired;       // This function will be called when timer expires
    // Note that the following is a union. Assign one or the other (preferably by pointer)
    //timer_signal_event.sigev_value.sival_int = 38;                        // This argument will be passed to the function
    timer_signal_event.sigev_value.sival_ptr = (void *) &pass_value_by_pointer;     // as will this (both in a structure)
    timer_signal_event.sigev_notify_attributes = NULL;
    timer_create(CLOCK_MONOTONIC, &timer_signal_event, &timer);

    printf("Start timer\n");
    timer_period.it_value.tv_sec = 1;                                   // 1 second timer
    timer_period.it_value.tv_nsec = 0;                                  // no nano-seconds
    timer_period.it_interval.tv_sec = 0;                                // non-repeating timer
    timer_period.it_interval.tv_nsec = 0;

    timer_settime(timer, 0, &timer_period, NULL);
    sleep(2);

    printf("----------------------------\n");
    printf("Start timer a second time\n");
    timer_settime(timer, 0, &timer_period, NULL);
    sleep(2);

    printf("----------------------------\n");
    printf("Start timer a third time\n");
    timer_settime(timer, 0, &timer_period, NULL);

    printf("Cancel timer\n");
    timer_delete(timer);
    sleep(2);
    printf("The timer expiration handler function should not have been called\n");

    return EXIT_SUCCESS;
}

当 运行 时,它给出以下输出:

Create timer
Start timer
Timer expiration handler function; 42
----------------------------
Start timer a second time
Timer expiration handler function; 42
----------------------------
Start timer a third time
Cancel timer
The timer expiration handler function should not have been called

Linux 有 timerfd。 https://lwn.net/Articles/251413/。这将允许等待时间与 select/poll/epoll 一起使用。或者,您可以在 select/poll/epoll 上使用超时。

Michael Kerrisk 在他的 "The Linux Programming Interface" 书中有一个详细的例子:

/* ptmr_sigev_thread.c

   This program demonstrates the use of threads as the notification mechanism
   for expirations of a POSIX timer. Each of the program's command-line
   arguments specifies the initial value and interval for a POSIX timer. The
   format of these arguments is defined by the function itimerspecFromStr().

   The program creates and arms one timer for each command-line argument.
   The timer notification method is specified as SIGEV_THREAD, causing the
   timer notifications to be delivered via a thread that invokes threadFunc()
   as its start function. The threadFunc() function displays information
   about the timer expiration, increments a global counter of timer expirations,
   and signals a condition variable to indicate that the counter has changed.
   In the main thread, a loop waits on the condition variable, and each time
   the condition variable is signaled, the main thread prints the value of the
   global variable that counts timer expirations.

   Kernel support for Linux timers is provided since Linux 2.6. On older
   systems, an incomplete user-space implementation of POSIX timers
   was provided in glibc.
*/
#include <signal.h>
#include <time.h>
#include <pthread.h>
#include "curr_time.h"              /* Declares currTime() */
#include "tlpi_hdr.h"
#include "itimerspec_from_str.h"    /* Declares itimerspecFromStr() */

static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

static int expireCnt = 0;           /* Number of expirations of all timers */
static void                         /* Thread notification function */
threadFunc(union sigval sv)
{
    timer_t *tidptr;
    int s;

    tidptr = sv.sival_ptr;

    printf("[%s] Thread notify\n", currTime("%T"));
    printf("    timer ID=%ld\n", (long) *tidptr);
    printf("    timer_getoverrun()=%d\n", timer_getoverrun(*tidptr));

    /* Increment counter variable shared with main thread and signal
       condition variable to notify main thread of the change. */

    s = pthread_mutex_lock(&mtx);
    if (s != 0)
        errExitEN(s, "pthread_mutex_lock");

    expireCnt += 1 + timer_getoverrun(*tidptr);

    s = pthread_mutex_unlock(&mtx);
    if (s != 0)
        errExitEN(s, "pthread_mutex_unlock");

    s = pthread_cond_signal(&cond);
    if (s != 0)
        errExitEN(s, "pthread_cond_signal");
}
int
main(int argc, char *argv[])
{
    struct sigevent sev;
    struct itimerspec ts;
    timer_t *tidlist;
    int s, j;

    if (argc < 2)
        usageErr("%s secs[/nsecs][:int-secs[/int-nsecs]]...\n", argv[0]);

    tidlist = calloc(argc - 1, sizeof(timer_t));
    if (tidlist == NULL)
        errExit("malloc");

    sev.sigev_notify = SIGEV_THREAD;            /* Notify via thread */
    sev.sigev_notify_function = threadFunc;     /* Thread start function */
    sev.sigev_notify_attributes = NULL;
            /* Could be pointer to pthread_attr_t structure */

    /* Create and start one timer for each command-line argument */

    for (j = 0; j < argc - 1; j++) {
        itimerspecFromStr(argv[j + 1], &ts);

        sev.sigev_value.sival_ptr = &tidlist[j];
                /* Passed as argument to threadFunc() */

        if (timer_create(CLOCK_REALTIME, &sev, &tidlist[j]) == -1)
            errExit("timer_create");
        printf("Timer ID: %ld (%s)\n", (long) tidlist[j], argv[j + 1]);

        if (timer_settime(tidlist[j], 0, &ts, NULL) == -1)
            errExit("timer_settime");
    }

    /* The main thread waits on a condition variable that is signaled
       on each invocation of the thread notification function. We
       print a message so that the user can see that this occurred. */

    s = pthread_mutex_lock(&mtx);
    if (s != 0)
        errExitEN(s, "pthread_mutex_lock");

    for (;;) {
        s = pthread_cond_wait(&cond, &mtx);
        if (s != 0)
            errExitEN(s, "pthread_cond_wait");
        printf("main(): expireCnt = %d\n", expireCnt);
    }
}

摘自 online source code.

也看了书的第23章,里面对这段代码解释的很详细

要测试上面的代码,可以输入

$ ./ptmr_sigev_thread 5:5 10:10

这将设置两个计时器:一个初始超时为 5 秒,间隔为 5 秒,另一个分别为 10。

辅助函数的定义可以在上面本书源代码中的 link 找到。