为何向阻塞SIGUSR2的进程发信号后/proc/<pid>/status中SigPnd无变化?
阻塞信号未在SigPnd中显示挂起的原因解析
测试代码(test.c)
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> void handler(int signum) { printf("\nGoodbye!\n"); exit(0); } int main() { struct sigaction sa = { .sa_handler = handler }; // Intercept SIGINT sigemptyset(&sa.sa_mask); if (sigaction(SIGINT, &sa, NULL) == -1) { perror("sigaction"); exit(EXIT_FAILURE); } signal(SIGUSR1, SIG_IGN); // Ignore SIGUSR1 // Block SIGUSR2 sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR2); sigprocmask(SIG_BLOCK, &mask, NULL); // Simulate work for 10 seconds for (int i = 0; i < 10; i++) { printf("Working... %d\n", i); sleep(1); } sigprocmask(SIG_UNBLOCK, &mask, NULL); }
操作步骤
- 编译并运行上述程序
- 程序运行期间执行信号发送命令:
kill -s USR2 $(pgrep test) - 立即查看进程状态:
cat /proc/$(pgrep test)/status | grep Sig
进程状态输出
SigQ: 1/256970 SigPnd: 0000000000000000 SigBlk: 0000000000000800 SigIgn: 0000000000000200 SigCgt: 0000000000000002
疑问
按照信号机制的逻辑,发送给进程的被阻塞信号应该处于挂起状态,直到解除阻塞,但这里SigPnd显示为0,没有任何挂起信号,这是为什么?
解答
问题出在你查看的字段范围和Linux信号挂起的存储逻辑:
- 字段匹配不全:你用
grep Sig过滤状态时,漏掉了ShdPnd(线程组共享未决信号集)字段——这个字段的前缀是Shd而非Sig,所以没被匹配出来。 - 信号挂起的存储规则:当你向进程发送SIGUSR2时,由于进程内所有线程(这里是单线程的主线程)都阻塞了该信号,内核无法找到可递送信号的线程,因此会将这个信号挂起在**线程组共享的未决信号集(ShdPnd)**中,而非线程自身的
SigPnd。
你可以执行以下命令查看完整的未决信号状态:
cat /proc/$(pgrep test)/status | grep -E 'SigPnd|ShdPnd'
此时会看到ShdPnd的值为0000000000000800,正好对应SIGUSR2的信号位(SIGUSR2是第12号信号,对应位为2^(12-1)=2048=0x800),证明信号确实处于挂起状态。
另外,SigQ中的第一个数值1也已经明确提示有1个未决信号,和你发送的SIGUSR2完全对应。当程序执行到sigprocmask(SIG_UNBLOCK)解除SIGUSR2的阻塞后,内核会将共享未决队列中的SIGUSR2递送给主线程,触发默认的进程终止动作。
内容的提问来源于stack exchange,提问作者nowox
相关产品推荐
相关产品推荐

