排查多线程死锁需定位等待关系:用info threads查状态,bt看调用栈,结合锁地址与源码分析持有者和等待者,确认循环等待。

排查多线程死锁,关键不是“看到线程卡住”,而是快速定位哪几个线程在等谁、等什么资源。GDB 本身不直接检测死锁,但配合线程状态、锁持有关系和调用栈分析,能高效还原死锁现场。
查看所有线程状态与当前执行点
程序挂起时,先用 info threads 列出所有线程及其状态(running / sleeping / blocked)。重点关注处于 Blocked 或长时间停在 pthread_mutex_lock、pthread_cond_wait 等同步函数中的线程。
- 用 thread
切换到可疑线程,再执行 bt 查看完整调用栈 - 对每个阻塞线程,检查其是否在等待互斥锁(如调用栈末尾是
__lll_lock_wait或pthread_mutex_lock) - 注意线程 ID(LWP)与 GDB 中的线程编号区别,必要时用 thread apply all bt 一键打印全部线程栈
识别锁的持有者与等待者
Linux 下 pthread mutex 本身不记录持有者信息,但可通过以下方式交叉推断:
- 若某线程正执行 pthread_mutex_unlock 或刚离开临界区,它很可能是该锁的上一任持有者
- 若多个线程都卡在同一个 pthread_mutex_lock 调用,且参数地址相同(可在 bt full 或 info registers 中查
%rdi寄存器值),说明它们竞争同一把锁 - 结合源码,确认该 mutex 变量的全局地址或局部作用域,再用 print *(pthread_mutex_t*)0x... 查看其内部状态(仅限调试版 glibc,且需符号支持)
结合源码与变量观察锁定路径
死锁本质是循环等待。需人工梳理各线程加锁顺序:
- 对每个阻塞线程,用 frame
跳转到调用 pthread_mutex_lock 的源码行,确认它已持有哪些锁(查局部/全局 mutex 变量值或加锁历史) - 用 print 或 display 命令持续观察关键 mutex 变量(如
print my_mutex.__data.__owner,glibc 实现细节可能因版本而异) - 若使用 C++ std::mutex,可尝试 print *(std::mutex*)0x... 并查看其内部 __lock 字段(需 libstdc++ 调试符号)
辅助技巧:提前注入调试能力
线上问题难复现?建议开发阶段就埋点:
- 用 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK) 替换普通 mutex,使重复加锁或非法解锁触发明确错误
- 封装 lock/unlock,记录每次加锁的线程 ID、时间、锁地址到环形缓冲区,崩溃时通过 GDB dump memory 提取
- 配合 set follow-fork-mode child 和 set detach-on-fork off,确保 fork 后子进程也能被 GDB 跟踪
死锁分析依赖对程序逻辑和加锁顺序的理解,GDB 是放大镜,不是自动诊断仪。重点始终是:谁拿了什么锁、谁在等什么锁、有没有循环依赖。











