使用timeout命令发送INT信号时程序双重接收信号问题排查
双重信号接收的核心原因
问题不来自你的程序实现,根源是GNU coreutils中timeout命令的运行机制,完整触发链路如下:
- 直接运行程序时,前台进程组仅包含编译生成的
test进程,按Ctrl+C触发的SIGINT是终端向整个前台进程组单次广播的,只会触发一次信号处理函数,输出符合预期。 - 使用
timeout -s INT 2 ./test运行时,流程会产生两次独立的SIGINT信号递送,最终让程序输出两次日志:- 第一次触发:2秒计时阈值到达后,
timeout作为父进程,首先向被监控的test进程发送SIGINT,程序进入信号处理函数,输出第一行内容。 - 第二次触发:
timeout自身会注册SIGINT捕获逻辑,保证收到终止信号时可以正确向子进程转发信号、避免僵死。由于timeout和test默认处于同一个前台进程组,计时到点发送的SIGINT会同时广播给组内所有成员(包括timeout自身);timeout收到这个SIGINT后,会按照自身逻辑再向test进程转发一次SIGINT,触发第二次信号处理,输出第二行内容。
- 第一次触发:2秒计时阈值到达后,
验证方法与规避方案
- 可通过在信号处理函数中打印进程ID、信号编号的方式,验证两次触发的SIGINT都是被
test进程正常接收的,不存在信号错送问题。替换后的处理函数代码参考:void gest_sig_int(int signo) { char buf[64]; int len = snprintf(buf, sizeof(buf), "I receive signal %d, pid=%d\n", signo, getpid()); write(1, buf, len); } - 针对这种双重触发的问题,可选规避方案:
- 在信号处理逻辑中增加短时去重判断,用静态变量记录上一次处理同信号的时间戳,短时间内重复到来的同信号直接丢弃,不执行核心逻辑。
- 调整sigaction的
sa_mask配置,让SIGINT在处理完成后的短时间内保持屏蔽状态,避免重复触发。 - 调整
timeout的运行参数,让被监控程序独立于timeout所在的进程组,从源头减少二次信号转发的触发可能。
补充说明:你当前的sigaction配置是符合POSIX信号规则的——
sa_flags设为0时,信号处理函数执行期间会自动屏蔽当前触发的SIGINT,避免同个信号重入打断处理流程;处理函数返回后,屏蔽状态自动解除,因此后续新到来的SIGINT可以正常被捕获触发,不存在实现逻辑上的疏漏。
内容的提问来源于stack exchange,提问作者Amition
相关产品推荐
相关产品推荐

