第一步是确认“谁真正在等”,重点观察info threads中调用栈顶部为pthread_mutex_lock、std::mutex::lock或__lll_lock_wait的线程,多线程停在同一锁地址即表明资源争抢。

用 info threads 看清所有线程当前状态
死锁或资源争抢问题里,第一步不是猜谁卡了,而是确认“谁真正在等”。执行 info threads 后,重点盯三类线索:
• 出现在调用栈顶部是 pthread_mutex_lock、std::mutex::lock 或 __lll_lock_wait 的线程——基本就是阻塞源头
• 多个线程停在同一个 pthread_mutex_lock 调用点,且参数地址一致(比如 bt full 里看到 %rdi 值相同)——说明它们争同一把锁
• 线程状态显示为 Blocked 或长时间停在 nanosleep/read,但不是死锁主因,先标记后查
用 thread <n></n> + bt full 追进每个可疑线程
不能只看顶层函数,要顺着栈往回找加锁动作的实际位置:
• 先 thread 2 切过去,再 bt full 查完整栈帧和局部变量值
• 找到最靠近栈顶的 pthread_mutex_lock 行,用 frame <n></n> 跳进去,list 看源码上下文,确认它锁的是哪个变量(比如 &mtx_a)
• 若符号可用,直接 print &mtx_a 或 print *(pthread_mutex_t*)0x7fffe40012a0 比对地址,判断是否同一把锁
• 对 std::mutex,可试 print ((std::mutex*)0x...)->__lock,非 0 值通常对应持有者线程 ID(glibc 实现)
交叉验证谁在持锁:靠行为推断,别等系统记录
Linux 的 pthread mutex 默认不存持有者信息,得从行为反推:
• 某线程刚执行完 pthread_mutex_unlock,或正处在临界区出口附近(比如 unlock 下一行),大概率是当前或上一任持有者
• 如果线程 A 卡在 lock(mtx_a),线程 B 卡在 lock(mtx_b),而线程 C 正在执行 unlock(mtx_a) 或刚离开 mtx_a 保护的代码段——C 很可能刚释放 mtx_a,但还没来得及拿 mtx_b
• 注意:不要依赖 info registers 直接读取锁状态,不同 libc 版本对 __lock 字段解释不一致,行为推断更稳
拼出循环等待链:从“已持”和“等待”两个字段画图
死锁闭环必须人工梳理,GDB 不会自动告诉你有没有循环:
• 对每个阻塞线程,记下两件事:“已持哪些锁” + “正在等哪把锁”
• 例如得到:
线程1:已持 mtx_a,等待 mtx_b
线程2:已持 mtx_b,等待 mtx_c
线程3:已持 mtx_c,等待 mtx_a
→ 这就是典型的循环等待链
• 关键细节:加锁顺序不一致才是根因。比如有的线程先 lock(mtx_a) 再 lock(mtx_b),另一些反过来,就极易触发闭环。这点必须回源码比对,不能只看运行时栈











