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

Catching signals such as SIGSEGV and SIGFPE in multithreaded program

本文关键字:SIGFPE 信号 SIGSEGV 多线程 程序      更新时间:2023-10-16

我正试图为在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)发送>时)或为特定线程(例如,由于执行特定机器语言指令而生成的某些信号,如SIGSEGV和SIGFPE,>线程导向的,以及使用pthread_kill(3)针对特定线程的信号)

如果我在除一个专门捕获信号的线程外的所有线程上阻止SIGSEGV,那么它会捕获另一个线程引发的SIGSEGV吗?

我发现了Linux中多线程的信号处理问题,但我不知道哪些信号是线程特定的,以及如何捕捉它们。

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

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

因此,您无法在其他线程中运行专门用于捕获SIGSEGV的线程。而且你可能不能很容易地将signalfd(2)用于SIGSEGV。。。

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

即使你碰巧做了所有这些,也有传言说Linux内核在这样的执行上并不是非常高效。因此,有传言称,在Linux上以这种方式模仿Hurd/Machi外部寻呼机的效率不是很高。看到这个答案。。。

当然,信号处理程序应该只调用异步信号安全函数(更多信息,请参阅信号(7))。特别是,原则上不能从它们调用fprintf(您可能无法可靠地使用日志系统,但它可以在大多数情况下工作,但不是所有情况下都可以)。

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