首页 > 解决方案 > 多线程进程在信号处理程序中退出时出现死锁

问题描述

一个进程中有两个线程。当主线程从信号处理程序接收到 SEGV 时,我曾经使用 pthread_kill 向其他辅助线程发送一些内部信号,并使用此内部信号将辅助线程捕获到睡眠状态,以便我现在可以进行强制清理和堆栈跟踪转储考虑到现在的单线程进程,从主线程进入文件(因为其他辅助线程处于睡眠状态)。

但是,一旦我遇到主线程退出时,进程就会离开(不退出)并且似乎在两个线程之间处于死锁状态。

请帮助我为什么以及哪部分代码导致死锁。

提前致谢!!

Auxiliary Thread stack:

Thread 2 (Thread 0x7fc565b5b700 (LWP 13831)):
#0  0x00007fc5668e81fd in nanosleep () from /lib64/libc.so.6
#1  0x00007fc566915214 in usleep () from /lib64/libc.so.6
#2  0x00000000009699a2 in SignalHandFun() at ...........
#3  <signal handler called>
#4  0x00007fc56691820a in mmap64 () from /lib64/libc.so.6
#5  0x00007fc5668a5bfc in _IO_file_doallocate_internal () from /lib64/libc.so.6
#6  0x00007fc5668b386c in _IO_doallocbuf_internal () from /lib64/libc.so.6
#7  0x00007fc5668b215b in _IO_new_file_underflow () from /lib64/libc.so.6
#8  0x00007fc5668b38ae in _IO_default_uflow_internal () from /lib64/libc.so.6
#9  0x00007fc566894bad in _IO_vfscanf_internal () from /lib64/libc.so.6
#10 0x00007fc5668a2cd8 in fscanf () from /lib64/libc.so.6
..... 
......
.....
#15 0x00007fc567259806 in start_thread () from /lib64/libpthread.so.0
#16 0x00007fc56691b64d in clone () from /lib64/libc.so.6
#17 0x0000000000000000 in ?? ()

Main Thread stack:

Thread 1 (Thread 0x7fc5679c0720 (LWP 13795)):
#0  0x00007fc56692878e in __lll_lock_wait_private () from /lib64/libc.so.6
#1  0x00007fc5668b504b in _L_lock_1309 () from /lib64/libc.so.6
#2  0x00007fc5668b3d9a in _IO_flush_all_lockp () from /lib64/libc.so.6
#3  0x00007fc5668b4181 in _IO_cleanup () from /lib64/libc.so.6
#4  0x00007fc566872630 in __run_exit_handlers () from /lib64/libc.so.6
#5  0x00007fc5668726b5 in exit () from /lib64/libc.so.6
#6  0x00000000009698e3 in SignalHandFun() at ....
#7  <signal handler called>
#8  0x000000b1000000b0 in ?? ()
#9  0x0000000000000000 in ?? ()

标签: multithreadingpthreadssignalsdeadlocksignal-handling

解决方案


我假设您将信号发送到另一个线程,因为您想做一些异步信号安全函数无法完成的工作。

问题是,如果在获取任何锁的线程上调用您的信号处理程序(例如在您的情况下,内部 libio 列表锁),那么任何尝试获取相同锁的线程都将无限期阻塞:您不能从您的 SIGSEGV 处理程序,因此锁将永远无法再次用于锁定,并且没有等待锁的线程会取得进展。在您的情况下,该exit函数需要获取 libio 列表锁,因为它必须遍历所有打开的文件流的列表并刷新它们,而打开新文件的线程在将新文件放在列表中时会获取锁。

虽然这是一个实现细节,并且可以想象在未来的某个(远)点在 glibc 中解决(我们最近所做的小改进对您的情况没有帮助),唯一的方法是您_exit在最终过程之前调用glibc 中的退出过程,在您需要进行清理之后。在您的情况下,可能可以从atexit您尽早注册的处理程序中执行此操作,但这取决于您的应用程序。

关于崩溃处理程序,我们在这里发布了一些建议:

本文侧重于fork,但在您的情况下,死锁问题几乎相同。


推荐阅读