多线程进程在信号处理程序中退出时死锁
Deadlock while multi-threaded process exit in signal handler
一个进程中有两个线程。当主线程从信号处理程序接收到 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 ?? ()
我假设你向另一个线程发送信号是因为你想做一些异步信号安全函数无法完成的工作。
问题是,如果您的信号处理程序在已获取任何锁的线程上调用(例如在您的情况下,内部 libio 列表锁),那么任何试图获取相同锁的线程都将无限期阻塞: 你不能从你的 SIGSEGV 处理程序 return ,所以锁永远不会再次锁定,等待锁的线程也不会取得进展。在您的情况下, exit
函数需要获取 libio 列表锁,因为它必须遍历所有打开的文件流的列表并刷新它们,而打开新文件的线程在放置新文件时获取锁列表中的文件。
虽然这是一个实现细节,可以想象在将来的某个(远)点在 glibc 内部解决(我们最近所做的小改进对您的情况没有帮助),唯一的方法是您在 glibc 中的最终进程退出过程之前调用 _exit
,在你需要做的清理之后。在您的情况下,可以通过您尽早注册的 atexit
处理程序来执行此操作,但这取决于您的应用程序。
关于崩溃处理程序,我们在这里发布了一些建议:
这篇文章的重点是 fork
,但死锁问题与您的情况几乎相同。
一个进程中有两个线程。当主线程从信号处理程序接收到 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 ?? ()
我假设你向另一个线程发送信号是因为你想做一些异步信号安全函数无法完成的工作。
问题是,如果您的信号处理程序在已获取任何锁的线程上调用(例如在您的情况下,内部 libio 列表锁),那么任何试图获取相同锁的线程都将无限期阻塞: 你不能从你的 SIGSEGV 处理程序 return ,所以锁永远不会再次锁定,等待锁的线程也不会取得进展。在您的情况下, exit
函数需要获取 libio 列表锁,因为它必须遍历所有打开的文件流的列表并刷新它们,而打开新文件的线程在放置新文件时获取锁列表中的文件。
虽然这是一个实现细节,可以想象在将来的某个(远)点在 glibc 内部解决(我们最近所做的小改进对您的情况没有帮助),唯一的方法是您在 glibc 中的最终进程退出过程之前调用 _exit
,在你需要做的清理之后。在您的情况下,可以通过您尽早注册的 atexit
处理程序来执行此操作,但这取决于您的应用程序。
关于崩溃处理程序,我们在这里发布了一些建议:
这篇文章的重点是 fork
,但死锁问题与您的情况几乎相同。