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
相关产品推荐
相关产品推荐

