这是一个元问题,因为我认为我有一个适合我的解决方案,但它有其自身的缺点和优点。我需要做一件相当普通的事情,抓住SIGSEGV
一个线程(没有专门的崩溃处理线程),转储一些调试信息并退出。
这里的问题是,在崩溃时,我的应用程序运行llvm-symbolizer
需要一段时间(相对而言)并导致产量(由于clone + execve
或超过线程的时间量,我在自己进行符号化时看到后者发生 -过程使用libLLVM
)。这样做的原因是为了得到一个带有解扰符号和行/文件信息(存储在单独的 DWP 文件中)的堆栈跟踪。出于显而易见的原因,我不希望在我的SIGSEGV
处理程序中发生屈服,因为我打算在应用程序(线程组)执行后终止它并且永远不会从信号处理程序返回。
我不太熟悉 Linux 信号处理和 glibc 的包装器在它们周围做魔法,虽然,我知道基本的陷阱,但没有太多关于处理信号细节的信息,比如同步信号处理程序是否获得任何类型的特殊优先级在调度方面。
头脑风暴,我有一些想法和缺点:
pthread_kill(<every other thread>, SIGSTOP)
- 使用更多线程很麻烦,与信号处理程序交互,这似乎会产生意想不到的副作用。还需要拦截来自其他库的线程创建以跟踪线程列表,并增加每次系统调用抢占的机会。一旦它们停止指向系统调用exit
存根或完全使用,甚至可能更改它们的上下文SIGKILL
。- 全局标志用作所有线程的取消点(有点像
pthread_cancel/pthread_testcancel
)。更安全,但需要大量维护,并且在大型代码库中它可能是地狱般的,除了轻微的性能开销。全局标志也可能导致错误级联,因为程序已经处于不可预测的状态,所以让任何其他线程在那里运行已经不是很好了。 - “滥用”我当前选择的调度程序,我的实现是答案之一。切换到
FIFO
调度策略并提高优先级因此成为该组中唯一可运行的线程。 - 核心转储不是一种选择,因为这里的目标是首先避免它们。除了符号器之外,我更希望不需要辅助程序。
Environment 是一个典型glibc
的基于 Linux (4.4) 的发行版,带有NPTL
.
我知道崩溃处理程序现在相当普遍,所以我相信我选择的方法都不是那么好,特别是考虑到我从未见过调度程序“hack”以这种方式使用过。因此,有没有人有比调度程序“hack”更清洁、风险更低的更好选择,我是否错过了关于信号的一般想法中的任何重要点?
编辑:似乎我并没有在这个等式中真正考虑过 MP(根据评论)以及其他线程在 MP 情况下仍然可以运行并且可以愉快地继续FIFO
在不同处理器上与线程一起运行的事实。但是,我可以将进程的亲和性更改为仅在与崩溃线程相同的内核上执行,这基本上将有效地冻结调度边界处的所有其他线程。但是,这仍然会导致“由于阻塞 IO 导致 FIFO 线程屈服”场景处于打开状态。
似乎该FIFO + SIGSTOP
选项是最好的选项,尽管我确实想知道是否有任何其他技巧可以使线程无法调度而无法使用SIGSTOP
. 从文档看来,似乎不可能将线程的 CPU 亲和性设置为零(使其处于技术上可运行的边缘状态,除非没有处理器可供其运行)。