C语言sigprocmask阻塞信号解除后第二部分代码不执行问题
问题原因分析
- 标准信号(SIGINT、SIGQUIT都属于此类)在Linux系统下不支持排队,阻塞期间多次触发的同一信号,内核只会保留1个待处理(pending)标记。
- 第一个循环运行时按下的Ctrl+C、Ctrl+\产生的信号会进入pending队列,当调用
sigprocmask(SIG_UNBLOCK, &sigs, NULL)解除阻塞的瞬间,内核会立即将这些pending信号递送给进程。 - SIGINT和SIGQUIT的默认处理动作都是终止进程,因此进程会在解除阻塞的瞬间直接退出,不会执行后续的第二个for循环代码。
解决方案
要实现阻塞期间触发的信号被丢弃、仅解除阻塞后新触发的信号才能终止进程的效果,需要在解除阻塞前先清空阻塞期间累积的pending信号,修改后的代码如下:
#include <signal.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> int main() { int i; sigset_t sigs, pending; sigemptyset(&sigs); sigaddset(&sigs, SIGINT); sigaddset(&sigs, SIGQUIT); sigprocmask(SIG_SETMASK, &sigs, NULL); for (i=0; i<5; ++i) { printf("(block) working ....\n"); sleep (1); } printf("The work is done, now you can ...\n"); // 先获取当前pending的信号集 sigpending(&pending); if (sigismember(&pending, SIGINT) || sigismember(&pending, SIGQUIT)) { // 临时将信号处理设置为忽略,丢弃之前阻塞的信号 signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); sigprocmask(SIG_UNBLOCK, &sigs, NULL); // 恢复信号的默认处理动作,后续新触发的信号可正常终止进程 signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); } else { // 无pending信号直接解除阻塞 sigprocmask(SIG_UNBLOCK, &sigs, NULL); } for (int j=0; j<5; ++j) { printf("(unblock) working ....\n"); sleep (1); } exit(0); }
修改后运行效果完全符合预期:第一个循环期间按下的快捷键信号会被直接丢弃,第二个循环运行时按下对应快捷键会触发默认动作终止程序。
内容的提问来源于stack exchange,提问作者j.hall
相关产品推荐
相关产品推荐

