用gdb分析线程栈是定位多线程死锁最直接有效的方式,通过info threads查看所有线程状态,聚焦等待锁的线程,逐个切换并bt分析调用栈,结合变量名或地址判断争抢同一锁,再交叉推断持有者,最终人工梳理加锁顺序确认循环等待闭环。

用 GDB 分析线程栈信息是定位多线程死锁最直接有效的方式。它不依赖额外工具或代码修改,只要程序还在运行、符号信息完整,就能现场还原锁等待关系。
看全所有线程状态
启动 GDB 附着到卡死进程后,第一件事是执行:
(gdb) info threads
这条命令列出全部线程及其当前状态。重点关注三类线索:
- 带 __lll_lock_wait、pthread_mutex_lock 或 std::mutex::lock 的线程——大概率正在等锁
- 调用栈末尾是 read、write、nanosleep 的线程——可能在做 I/O 或延时,但未必是死锁源头
- 同一时刻多个线程停在同一个 pthread_mutex_lock 调用点,且参数地址一致(可通过 bt full 或 info registers 查 %rdi)——说明它们争抢同一把锁
逐个检查阻塞线程的调用栈
对每个疑似等待锁的线程,切换过去并查看完整路径:
(gdb) thread
(gdb) bt
关键不是只看顶层函数,而是顺着栈往回找:
- 找到最靠近顶部的 pthread_mutex_lock 或 std::mutex::lock 调用行
- 用 frame
跳转到那一层,再执行 list 查看源码上下文,确认它试图加哪把锁 - 结合变量名(如 print &mtx1)或内存地址(如 print *(pthread_mutex_t*)0x7fffe40012a0)判断是否为同一把锁
交叉推断谁持有锁
Linux 的 pthread mutex 默认不记录持有者,但可通过行为间接判断:
- 某个线程刚执行完 pthread_mutex_unlock 或正处在临界区出口附近,很可能是该锁的当前或上一任持有者
- 若线程 A 卡在 lock(mtx1),线程 B 卡在 lock(mtx2),而线程 C 正在执行 unlock(mtx1) 或刚离开 mtx1 保护的代码段——C 很可能刚释放 mtx1,但还没来得及拿 mtx2
- 对 std::mutex,尝试 print ((std::mutex*)0x...)->__lock(需调试符号支持),值为 0 表示未锁,非 0 通常为持有者线程 ID(glibc 实现略有差异)
人工梳理加锁顺序,确认循环等待
死锁本质是循环等待链。你需要从每个阻塞点出发,拼出资源依赖图:
- 线程 1:已持 mtx_a,等待 mtx_b
- 线程 2:已持 mtx_b,等待 mtx_c
- 线程 3:已持 mtx_c,等待 mtx_a
一旦发现这种闭环,死锁就坐实了。此时回到源码,检查各线程加锁顺序是否不一致——比如有的先 a 后 b,有的先 b 后 a,就是典型诱因。











