信号处理不阻塞主线程,但信号未及时递达、被阻塞、handler 耗时过长或用户态忙循环无系统调用,均会导致主线程“假性卡死”;排查应聚焦 pending 状态、signal mask、handler 安全性及递达时机。

信号处理本身不会阻塞主线程,但不当的信号使用或 handler 实现可能让主线程“看起来卡住”——本质是信号递达时机、handler 执行逻辑或信号掩码配置出了问题。排查重点不在“信号阻塞线程”,而在“为什么信号没被及时处理”或“为什么处理过程拖住了主线程”。
确认信号是否真的到达进程(pending 状态)
很多情况下,信号根本没递达,只是被挂起了。先查它是不是在未决集合里:
- 用 kill -l 确认你要测试的信号编号(比如 SIGUSR1 是 10)
- 向目标进程发信号:kill -10
- 立即运行:cat /proc/
/status | grep Sig ,关注 SigPnd(pending)、ShdPnd(shared pending)、SigBlk(blocked)三列 - 如果 SigPnd 对应位为 1,说明信号已入队但未递达;若 SigBlk 对应位也为 1,则信号被阻塞,需检查 sigprocmask 设置
检查信号是否被意外阻塞(signal mask)
主线程可能在某处调用了 sigprocmask(SIG_BLOCK, &set, NULL) 却忘了恢复,导致后续同类信号一直 pending:
- 查看当前阻塞集:cat /proc/
/status | grep SigBlk ,对照 /usr/include/asm-generic/signal.h 中的位图定义解码 - 代码中搜索 sigprocmask、pthread_sigmask、sigsuspend 调用,尤其注意成对出现(block/unblock)是否缺失
- 多线程程序要特别注意:每个线程有独立 signal mask;主线程阻塞的信号,工作线程未必阻塞,但若主线程负责处理 SIGCHLD 或 SIGHUP,而它自己把对应信号 block 了,就会“收不到”
验证信号 handler 是否陷入长耗时或死锁
即使信号成功递达,handler 若执行太久或调用不可重入函数(如 malloc、printf、strtok),可能引发竞态、死锁或使主线程长时间停在内核态:
- 用 gdb attach
后执行 info threads 和 thread apply all bt,看是否有线程卡在 signal handler 内部(比如卡在 write()、malloc() 或自旋等待) - 检查 handler 函数:是否只做最小动作(如仅设置全局 volatile 标志、写 pipe、调用 sigqueue() 转发)?是否误用了非异步信号安全函数?
- 典型陷阱:在 handler 里调用 printf → 可能重入 stdout 锁;调用 malloc → glibc malloc 内部加锁,若主线程正持锁,就会死锁
观察信号递达时机是否被用户态忙循环耽误
Linux 信号只在进程从内核态返回用户态时检查 pending —— 如果主线程长期在纯用户态空转(比如 while(1); 或 tight loop 无系统调用),就不会触发检查点:
- 用 perf record -e syscalls:sys_enter_* -p
-- sleep 5 看主线程是否几乎不进内核 - 临时加一句 pause()、sigsuspend() 或 nanosleep({0,100000}) 在循环末尾,强制让出 CPU 并提供信号检查机会
- 更健壮的做法:用 signalfd() 把信号转为文件描述符,配合 epoll 等 I/O 多路复用,彻底规避“时机不可控”问题


















