You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux:SIGRTMIN计时器仅触发一次的原因排查

为何定时器信号处理程序仅被调用一次?

这段代码基于Linux手册页的timer_create示例修改,当传入参数2 500000000(程序睡眠2秒,定时器每500毫秒触发一次)时,预期会触发4次定时器信号,但实际仅触发一次。

// TimerAlarm.cpp 
//
#include <chrono>
#include <stdint.h>
#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 satus;

    tidp = static_cast< timer_t*>(si->si_value.sival_ptr);

    printf("    sival_ptr = %p; ", si->si_value.sival_ptr);
    printf("    *sival_ptr = %#jx\n", (uintmax_t)*tidp);

    satus = timer_getoverrun(*tidp);
    if (satus == -1)
        errExit("timer_getoverrun");
    else
        printf("    overrun count = %d\n", satus);
}

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("Handle:r 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 %#jx\n", (uintmax_t)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("Unblocking signal %d\n", SIG);
       if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
            errExit("sigprocmask");
    printf("Sleeping for %d seconds\n", atoi(argv[1]));
    int remain = atoi(argv[1]);
    std::chrono::high_resolution_clock::time_point startTime =        std::chrono::high_resolution_clock::now();

    while (remain)
    {
        remain = sleep(remain);
        printf("\nRemain to sleep %d", remain);
        perror("\nSleep exit with ");
    }


    printf("\nTime passed %ld msec\n", std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - startTime).count());

    exit(EXIT_SUCCESS);
}

问题根源

信号处理函数handler中的signal(sig, SIG_IGN);是罪魁祸首:

  • 这行代码将当前信号的处理方式设置为忽略,第一次触发信号并执行处理程序后,后续所有同类型的信号都会被系统直接丢弃,不会再调用处理程序。
  • 原手册页示例中并没有这行代码,属于修改时误加的错误逻辑。

修复方法

直接删除signal(sig, SIG_IGN);这一行即可。修复后:

  • 实时信号(SIGRTMIN属于实时信号)会被排队处理,不会丢失,程序会按预期触发4次信号处理程序。
  • timer_getoverrun会正确返回定时器的溢出计数(如果处理程序执行不及时导致信号堆积的话)。

内容的提问来源于stack exchange,提问作者igor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 01:59:57