Ubuntu22.04下C++进程中raise()优先触发默认信号处理器而非sigwait()的疑问
我在Ubuntu-22.04上运行一个C++进程,主线程通过sigwait()阻塞,直到收到SIG_INT或SIG_TERM信号后退出。外部用kill -INT <pid>发送信号时,sigwait()能正常解除阻塞,主线程顺利退出;但在进程内部调用raise(SIG_INT)时,sigwait()仍处于阻塞状态,信号会被默认处理器处理(未安装自定义处理器)。原本预期raise()和外部kill命令效果一致,但实际不符。换成kill(getpid(), signal)就能达到预期(sigwait()解除阻塞),想知道两者行为差异的原因。
以下是使用sigwait()的等待实现:
int wait_stop_signals() { sigset_t sigset; sigemptyset(&sigset); // 添加要阻塞并等待的终止信号 sigaddset(&sigset, SIGINT); sigaddset(&sigset, SIGTERM); // 阻塞要等待的信号,避免触发默认处理器 int rc = pthread_sigmask(SIG_BLOCK, &sigset, nullptr); if (rc != 0) { return rc; } int sig = 0; // 阻塞直到等待的信号到达 return sigwait(&sigset, &sig); }
行为差异的核心原因
1. raise()的信号投递逻辑
raise(sig)本质等价于pthread_kill(pthread_self(), sig),它是向当前调用线程定向发送信号。由于主线程已经通过pthread_sigmask阻塞了SIG_INT/SIG_TERM,调用raise()后,信号会被标记为当前线程的「待处理(pending)」状态,但因为线程本身阻塞了该信号,这个pending信号不会被sigwait()捕获——sigwait()优先处理的是进程级的pending信号,而非当前线程的pending信号。最终这个无法被处理的信号会触发默认处理器。
2. kill(getpid(), sig)的信号投递逻辑
kill(getpid(), sig)是向整个进程发送信号,信号会被标记为进程级的pending状态。sigwait()的设计就是监听这类进程级pending信号:当检测到目标信号处于pending且被阻塞时,就会捕获该信号并解除阻塞,这就和外部用kill命令发送信号的行为一致了。
简单总结:raise()是线程定向的信号,而kill(getpid())是进程定向的信号,sigwait()只识别进程级的pending信号,这就是两者行为差异的根源。
内容的提问来源于stack exchange,提问作者codentary

