Linux下longjmp跳出信号处理器仅生效一次的问题解决问询
问题原因与解决方案
问题根源
Linux 系统中,当信号处理函数执行时,内核会自动屏蔽当前触发的信号(此处为 SIGINT)。你代码中使用的普通 setjmp()/longjmp() 不会恢复被屏蔽的信号掩码,导致第一次处理完 SIGINT 后,该信号始终处于屏蔽状态,后续 Ctrl+C 发送的 SIGINT 无法被程序接收,因此没有响应。
而 macOS 下的 longjmp() 行为不同,会自动恢复信号掩码,所以能正常重复响应 SIGINT。
修改后的代码
将普通跳转函数替换为支持信号掩码保存/恢复的 sigsetjmp() 和 siglongjmp(),即可解决问题:
#include <stdio.h> #include <unistd.h> #include <setjmp.h> #include <signal.h> static sigjmp_buf label; static void handler(int type) { printf("handler(%d)\n", type); signal(SIGINT, handler); // 重新注册信号处理函数 siglongjmp(label, 1); } int main(int argc, char *argv[]) { signal(SIGINT, handler); if (sigsetjmp(label, 1)) // 第二个参数设为1,表示保存当前信号掩码 printf("re-run\n"); printf("running\n"); sleep(1000); printf("done\n"); return 0; }
关键说明
sigsetjmp(label, 1):第二个参数为1时,会将当前的信号掩码保存到sigjmp_buf结构体中。siglongjmp(label, 1):跳转时会自动恢复之前保存的信号掩码,SIGINT 不再被屏蔽,后续信号可以正常触发处理函数。
另外,更推荐使用 sigaction() 替代 signal() 设置信号处理函数,sigaction 行为更稳定且跨平台一致,优化后的代码如下:
#include <stdio.h> #include <unistd.h> #include <setjmp.h> #include <signal.h> static sigjmp_buf label; static void handler(int type) { printf("handler(%d)\n", type); siglongjmp(label, 1); } int main(int argc, char *argv[]) { struct sigaction sa; sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; // 不设置SA_RESTART,避免sleep被信号中断后自动重启 sigaction(SIGINT, &sa, NULL); if (sigsetjmp(label, 1)) printf("re-run\n"); printf("running\n"); sleep(1000); printf("done\n"); return 0; }
使用 sigaction 无需手动重新注册信号处理函数(除非设置 SA_RESETHAND 标志),行为更可靠。
内容的提问来源于stack exchange,提问作者markn
相关产品推荐
相关产品推荐

