如何确保每次按下Ctrl+C时,结合longjmp的SIGINT信号处理器均被触发?
问题根源分析
问题出在**longjmp()直接跳过了信号处理函数的正常返回流程,导致内核没有解除对SIGINT信号的阻塞**。
当信号触发时,内核会自动将当前信号(此处为SIGINT)加入进程的信号掩码,防止递归触发信号。如果信号处理函数正常返回,内核会自动移除该掩码;但你用longjmp()直接跳回主函数,信号处理函数未正常退出,内核也就不会解除对SIGINT的阻塞,后续的SIGINT信号都会被系统屏蔽。
两种解决方案
方案1:手动解除信号阻塞(基础版)
在longjmp()执行前,调用sigprocmask()手动解除对SIGINT的阻塞:
#include <stdio.h> #include <unistd.h> #include <signal.h> #include <setjmp.h> jmp_buf sjbuf; void onintr(int); void onintr(int i) { signal(SIGINT, onintr); printf("\nInterrupt(%d)\n", i); // 解除SIGINT的阻塞状态 sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigprocmask(SIG_UNBLOCK, &mask, NULL); longjmp(sjbuf, 0); } int main(int argc, char* argv[]) { int sleep_t = 1; int ctr = 0; int timeout = 10; if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, onintr); setjmp(sjbuf); printf("Starting loop...\n"); while (ctr < timeout) { printf("Going to sleep for %d second(s)\n", sleep_t); ctr++; sleep(sleep_t); } printf("\n"); return 0; }
方案2:使用信号专用跳转函数(推荐)
sigsetjmp()和siglongjmp()是标准库为信号场景设计的跳转函数,会自动保存和恢复进程的信号掩码,无需手动处理阻塞逻辑:
#include <stdio.h> #include <unistd.h> #include <signal.h> #include <setjmp.h> // 替换jmp_buf为sigjmp_buf sigjmp_buf sjbuf; void onintr(int); void onintr(int i) { signal(SIGINT, onintr); printf("\nInterrupt(%d)\n", i); // siglongjmp第二个参数不能为0,标准要求返回值需非0 siglongjmp(sjbuf, 1); } int main(int argc, char* argv[]) { int sleep_t = 1; int ctr = 0; int timeout = 10; if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, onintr); // 第二个参数为1,表示保存当前信号掩码 if (sigsetjmp(sjbuf, 1) == 0) { printf("Starting loop...\n"); } while (ctr < timeout) { printf("Going to sleep for %d second(s)\n", sleep_t); ctr++; sleep(sleep_t); } printf("\n"); return 0; }
验证效果
修改后运行程序,每次按下Ctrl+C都会触发信号处理函数,不会出现后续信号被忽略的情况:
./a.out Starting loop... Going to sleep for 1 second(s) Going to sleep for 1 second(s) ^C Interrupt(2) Starting loop... Going to sleep for 1 second(s) ^C Interrupt(2) Starting loop... Going to sleep for 1 second(s) ...
内容的提问来源于stack exchange,提问作者gomfy
相关产品推荐
相关产品推荐

