有\_lll\_lock\_wait即线程卡在futex等待互斥锁;多线程均停在此且各自持锁→高概率死锁;通过\_\_m\_owner查持有者lwp id,再匹配info threads中的线程号。

直接看 thread apply all bt 输出里有没有 _lll_lock_wait,有就说明线程卡在 futex 等待锁了——这是最快速确认互斥锁阻塞点的方法。
怎么一眼识别锁等待线程
死锁发生时,多数线程会停在内核态等待,GDB 中表现为堆栈末尾反复出现系统调用符号。重点关注以下特征:
-
_lll_lock_wait出现在调用栈底部(常见于pthread_mutex_lock内部),代表线程正通过 futex 等待互斥锁释放 -
__pthread_cond_wait或sem_wait也可能出现,但那是条件变量或信号量,不是互斥锁本身;别混淆 - 若看到
nanosleep、poll、epoll_wait,大概率是业务逻辑休眠或 I/O 阻塞,和锁无关 - 多个线程都停在不同 mutex 的
_lll_lock_wait,且各自持有别的锁 → 高概率循环等待死锁
怎么查出哪个线程持有了目标互斥锁
Linux 下 std::mutex 底层是 pthread_mutex_t,其 __m_owner 字段存着持有者线程的 LWP ID(轻量级进程 ID,即 gettid() 返回值)。操作步骤如下:
- 先用
info threads列出所有线程及其 LWP ID(第一列数字) - 对疑似持锁线程执行
thread <id></id>切换过去,再print <mutex_var></mutex_var> - 观察输出中
__m_owner的值,比如$1 = { ..., __m_owner = 0x2527, ...} - 把
0x2527转成十进制(print /d 0x2527→ 得到9511),去info threads里找 LWP ID 为9511的那一行,对应线程编号就是持有者 - 注意:若
__m_owner == 0,说明锁当前空闲,不是它在拦路
为什么 print mutex 有时显示 __m_owner == 0 却 still waiting
这不是矛盾,而是典型「锁已释放但唤醒未完成」或「虚假唤醒竞争窗口」现象。常见原因有:
- 持有线程刚 unlock,内核 futex 唤醒还没来得及送达等待队列(极短时间窗口)
- 使用了错误的 mutex 类型,比如本该用
PTHREAD_MUTEX_ERRORCHECK却用了默认类型,导致 unlock 失败也不报错 - 程序里混用了
pthread_mutex_lock和std::mutex::lock()操作同一个内存地址(UB,__m_owner可能被覆盖或未更新) - GDB attach 时机太早,锁还在释放路径中间,
__m_owner清零但 waiters 还没处理完
此时不能只信 __m_owner,要结合 thread apply all bt 看谁在等、谁刚 unlock、谁最后修改了该 mutex 内存 —— 观察点 watch <mutex_var></mutex_var> 在这种边界场景下比 print 更可靠。











