核心是识别同一函数被异步多次进入导致的数据竞争或逻辑错乱,常见于主流程操作共享资源时信号打断并再次调用该函数,需重点检查全局/静态变量访问、不可重入函数调用、volatile与sig_atomic_t使用是否合规、信号掩码配置及是否采用self-pipe或signalfd等安全替代方案。

排查信号处理函数中的重入问题,核心是识别“同一函数被异步多次进入”导致的数据竞争或逻辑错乱。这类问题不会总复现,但一旦触发就容易崩溃或行为异常,比如链表损坏、变量值突变、日志丢失、CPU 占满等。
看信号是否在关键临界区被触发
重入最常发生在主流程正操作共享资源(如全局链表、静态缓冲区、文件描述符数组)时,信号突然打断并再次调用相同函数。排查时先定位:
- 哪些全局/静态变量被信号处理函数和主逻辑共同访问?尤其注意未加保护的指针、计数器、状态标志
- 信号处理函数里是否调用了 不可重入函数(如
printf、malloc、strtok、read、write等非 async-signal-safe 函数) - 主程序是否在系统调用中阻塞(如
read、accept),而信号处理函数又调用了同名或依赖相同内部锁的函数
检查 volatile 和 sig_atomic_t 的使用是否到位
如果用全局变量做通信(例如 volatile int sig_received = 0;),必须满足两个条件才安全:
- 变量类型是
sig_atomic_t(这是 POSIX 规定的唯一保证原子读写的整型) - 声明时加上
volatile,防止编译器优化掉重复读取——否则主循环可能永远看不到变化 - 避免对它做复合操作(如
++flag),因为这不是原子操作;只做简单赋值或比较
验证信号掩码与 handler 执行环境
多线程下重入风险更高,因为 sigaction 注册的 handler 是进程级共享的,但信号掩码(pthread_sigmask)是线程私有。排查要点:
- 是否所有线程都显式屏蔽了可能干扰的信号(如
SIGUSR1)?主线程注册 handler 后,子线程默认不继承掩码 - handler 中是否意外调用了线程不安全函数(如
errno直接赋值、getpwuid)?这些函数内部可能用到静态缓冲区 - 是否误用了
SA_NODEFER标志?它会让同类信号在 handler 执行中不被自动阻塞,极易引发嵌套重入
用 self-pipe 或 signalfd 替代裸 handler
最稳妥的规避方式,是不让复杂逻辑出现在信号上下文中:
- 信号处理函数只往一个 pipe 写 1 字节,主循环用
select/epoll监听该 pipe 读端——这样所有业务逻辑都在主上下文执行,完全避开重入 - Linux 2.6.27+ 支持
signalfd(),把信号转成文件描述符事件,直接接入 epoll 循环,比 pipe 更轻量且支持实时信号排队 - 避免在 handler 中调
longjmp、setjmp或修改栈,这类非本地跳转在信号上下文中极难调试且破坏栈帧一致性











