在多线程程序中捕获SIGSEGV和SIGFPE等信号

时间:2013-11-30 19:32:53

标签: c++ linux multithreading posix

我正在尝试为在linux上运行的程序编写多线程日志记录系统。

在主程序线程中调用日志系统会将包含要记录的数据的数据结构推送到FIFO队列中。专用线程选择队列的数据并输出数据,而程序主线程继续其任务。

如果主程序导致SIGSEGV或其他信号被引发,我需要在终止之前确保队列为空。

我的计划是使用pthread_sigmask http://man7.org/linux/man-pages/man3/pthread_sigmask.3.html阻止除一个线程以外的所有信号,但是在http://man7.org/linux/man-pages/man7/signal.7.html上读取信号列表时我注意到了:

  

可以为整个过程(例如,当使用kill(2)发送>或使用kill(2))或针对特定线程(例如,某些信号,例如SIGSEGV和)生成(并因此待决)信号SIGFPE,>由于执行特定的机器语言指令而生成的   线程定向,以及使用pthread_kill(3)针对特定线程的信号。

如果我在所有线程上阻塞SIGSEGV但是一个专用于捕获信号的线程,那么它会捕获由不同线程引发的SIGSEGV吗?

我发现了问题Signal handling with multiple threads in Linux,但我对于哪些信号是特定于线程以及如何捕获它们一无所知。

1 个答案:

答案 0 :(得分:7)

我同意这些评论:在实践中,捕捉和处理SIGSEGV往往是一件坏事。

并且SIGSEGV被传递到特定的线程(请参阅this),该线程运行的机器指令访问了某个非法地址。

因此,您无法在其他线程中运行专用于捕获SIGSEGV的线程。您可能无法轻松地将signalfd(2)用于SIGSEGV ...

捕获(并正常从其信号处理程序返回)SIGSEGV是一个复杂的处理器特定的事物(它不能是“可移植的C代码”)。您需要检查并更改处理程序中的机器状态,即修改地址空间(通过调用mmap(2)等...)或修改当前线程的寄存器状态。因此,将sigaction(2)SA_SIGINFO一起使用,并更改信号处理程序的第三个参数(类型ucontext_t*)指向的机器特定状态。然后深入了解处理器特定的 uc_mcontext字段。有乐趣改变单个寄存器等...如果你没有改变故障线程的机器状态,则恢复执行(在从SIGSEGV处理程序返回之后)与之前相同的情况,以及另一个{{ 1}}信号立即发送....或者简单地说,不要从SIGSEGV处理程序正常返回(例如使用siglongjmp(3)abort(3)_exit(2) ... )。

即使你碰巧做了这一切,也有传言说Linux内核在这种执行方面效率不高。因此有传言说在Linux上尝试以这种方式模仿Hurd / Mach外部寻呼机效率不高。见this answer ...

当然,信号处理程序应该只调用(请参阅signal(7)以获取更多信息)async-signal-safe函数。特别是,您原则上不能从它们调用SIGSEGV(并且您可能无法使用可靠您的日志记录系统,但它可以在大多数情况下工作,但不是所有情况都可以。)

我在fprintf上所说的内容也适用于SIGSEGVSIGBUS(以及其他特定于线程的异步信号,如果存在的话)。