为什么我注册的SIGFPE信号处理函数仅会被触发一次?
问题根因
- POSIX 标准默认规则:当信号处理函数被触发执行时,触发该处理的信号会被自动加入进程的信号阻塞掩码,直到处理函数正常返回才会自动解除,除非在
sigaction配置中显式指定SA_NODEFER标志。 - 你在 SIGFPE 的处理函数
sigAction中直接调用longjmp跳出处理函数,没有走信号处理的正常返回流程,内核自动添加的 SIGFPE 阻塞位没有被清除。 - 你使用的标准
setjmp/longjmp接口不会保存和恢复进程的信号掩码,所以跳回calc函数的错误处理分支后,SIGFPE 仍然处于阻塞状态。 - 你看到的
longjmp之后掩码为 0x0 是错误打印导致的假象:sigset_t是操作系统定义的不透明类型,不能直接用%x格式化打印,你实际打印的是结构体起始地址的部分内容,不是真实的信号掩码值。
后续每次调用 raise(SIGFPE) 时,由于 SIGFPE 处于阻塞状态,信号只会被挂起不会递送给进程,因此处理函数永远不会被触发。
修复方案
你可以选择以下任意一种方案修复:
- 替换信号安全的跳转接口:将
setjmp/longjmp替换为sigsetjmp/siglongjmp,调用sigsetjmp时第二个参数传非0值,跳回时会自动恢复调用时的信号掩码:- 全局变量
static jmp_buf begin修改为static sigjmp_buf begin setjmp(begin)修改为sigsetjmp(begin, 1)longjmp(begin, 1)修改为siglongjmp(begin, 1)
- 全局变量
- 添加SA_NODEFER标志:在
sigaction配置中添加SA_NODEFER标志,进入 SIGFPE 处理函数时不会自动阻塞 SIGFPE:act.sa_flags |= SA_SIGINFO | SA_NODEFER; - 手动解除阻塞:在
setjmp返回非0的错误处理分支中,手动调用sigprocmask解除 SIGFPE 的阻塞。
内容的提问来源于stack exchange,提问作者Bugsy
相关产品推荐
相关产品推荐

