使用sigwait等待SIGALRM时如何避免阻塞SIGINT信号
问题原因
你当前代码无法响应SIGINT的核心原因有两点:
- 你在
setup_alarm()里全局永久阻塞了SIGALRM,但没有处理sigwait()的中断返回场景:sigwait()是阻塞式系统调用,当不在等待信号集内的信号(比如SIGINT)到达时,它会返回错误值并置errno为EINTR,你的代码没有判断这个返回分支,会直接跳过信号投递的处理逻辑,回到循环继续等待。 - 全局设置信号阻塞掩码的方式会污染业务运行阶段的信号处理上下文,容易出现非预期的信号阻塞问题。
修正方案
按以下逻辑调整即可满足需求:
- 取消全局信号阻塞设置,仅在进入休眠等待阶段前临时阻塞
SIGALRM,等待结束立刻恢复原有的信号掩码,不影响业务运行阶段的信号默认行为。 - 给
sigwait()增加EINTR返回分支处理:当等待被非目标信号打断时,立刻取消未触发的闹钟,直接退出等待逻辑,此时未被阻塞的SIGINT等信号会自动执行默认处理动作(即终止进程),不需要额外编写信号处理函数。 - 补全信号等待的循环逻辑,避免被其他无关信号误唤醒。
修正后的可运行代码如下:
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <errno.h> #include <sys/time.h> #include <unistd.h> void wait_for_alarm(int interval) { printf("setting up alarm for %d seconds\n", interval); sigset_t wait_sigs, old_mask; sigemptyset(&wait_sigs); sigaddset(&wait_sigs, SIGALRM); // 临时阻塞SIGALRM,保存原有信号掩码用于后续恢复 sigprocmask(SIG_BLOCK, &wait_sigs, &old_mask); alarm(interval); printf("waiting for signal\n"); int received_sig; int ret; while (1) { ret = sigwait(&wait_sigs, &received_sig); if (ret == 0) { // 成功捕获等待集内的信号,判断为SIGALRM则正常结束等待 if (received_sig == SIGALRM) { printf("Received alarm signal, finish waiting\n"); break; } } else if (ret == EINTR) { // 被等待集外的信号(如SIGINT)打断,取消未触发的闹钟后退出 alarm(0); break; } else if (ret == EINVAL) { perror("sigwait failed unexpectedly"); exit(1); } } // 恢复原有信号掩码,保证业务运行阶段信号处理不受影响 sigprocmask(SIG_SETMASK, &old_mask, NULL); } int somefunction() { return (rand() % 4) + 1; } int main(int argc, char *argv[]) { // 随机数种子仅初始化一次即可,无需每次调用业务函数都重置 srand(time(NULL)); int alarm_wait = 0; while (1) { // 执行业务逻辑 alarm_wait = somefunction(); // 动态休眠,支持被SIGINT等信号立即打断 wait_for_alarm(alarm_wait); } return 0; }
逻辑说明
- 等待休眠阶段仅临时阻塞
SIGALRM用于sigwait()捕获,SIGINT等其余信号全程未被阻塞或捕获,会保持默认处理行为。 - 当休眠期间收到
SIGINT,sigwait()会立即被中断返回,代码取消剩余闹钟后退出等待函数,内核会自动执行SIGINT默认的终止进程逻辑,完全符合预期。 - 信号掩码的临时修改会在等待结束后立刻恢复,不会对业务计算阶段的信号处理逻辑造成任何影响。
内容的提问来源于stack exchange,提问作者MOHAMMAD RASIM
相关产品推荐
相关产品推荐

