目标进程正阻塞在用户态锁上,如 pthread_mutex 或 std::mutex,内核通过 futex_wait 挂起线程等待唤醒;需结合 gdb 查栈和锁地址确认持有者及锁状态。

strace -p 卡在 futex_wait 是什么信号
这说明目标进程正阻塞在用户态锁(如 pthread_mutex、std::mutex)上,内核通过 futex_wait 系统调用挂起线程等待唤醒。它不是死锁的充分证据,但确实是锁竞争或持有者不释放的典型表征。
关键要区分两种情况:短暂等待(比如临界区短、锁很快释放)和持续挂起(几秒以上不动)。后者才值得深入。
-
futex_wait后面通常跟着一个地址(如0x7f...
),这是锁变量在内存中的地址,可用于后续比对 - 如果
strace -p 本身也卡住不动,说明 tracee 正在不可中断睡眠(D状态),此时strace无法注入 syscall,需换方式 - 注意:glibc 的
pthread_mutex_lock在争用时可能先自旋再 fallback 到futex_wait,所以看到futex_wait前没其他锁操作日志,不代表没尝试加锁
怎么确认是哪个锁、被谁持有
仅靠 strace 输出无法定位锁归属,必须结合进程栈和内存状态。优先使用 gdb attach 获取线程上下文:
- 运行
gdb -p,然后thread apply all bt查所有线程栈,重点找持有锁的线程——它通常停在pthread_mutex_lock返回后、或pthread_mutex_unlock之前 - 若栈中出现
__lll_lock_wait或__pthread_mutex_lock,说明该线程正在等锁;若停在__pthread_mutex_unlock或刚返回,可能是它持有了但没及时释放 - 用
info registers和x/gx $rdi(x86_64 下$rdi通常是 mutex 地址)查看锁结构体内容,检查__data.__owner字段是否非零(即有持有者线程 ID) - 若程序带调试符号,可直接
p *$rdi打印整个pthread_mutex_t结构,观察__owner、__nusers、__kind(判断是否递归锁)
为什么 pstack / cat /proc/pid/stack 不够用
pstack 和 /proc/ 只显示内核态调用栈(scheduler、futex、sys_futex 等),看不到用户态函数名和锁变量地址,对定位锁竞争帮助有限。
-
/proc/中常见的是/stack [这类内核符号,无法反推用户代码哪一行调用了] futex_wait_queue_me lock() -
pstack依赖libpthread符号和 frame pointer,若二进制 strip 过或编译时加了-fomit-frame-pointer,栈帧会截断或错乱 - 它们都无法告诉你「这个
futex_wait对应的是哪个std::mutex m;变量」——只有 gdb + debuginfo 或内存地址比对才能做到
实际排查时容易忽略的点
很多问题卡在看似无关的细节上:
- 被 trace 的进程若启用了
ptrace_scope限制(/proc/sys/kernel/yama/ptrace_scope == 2),普通用户strace -p或gdb -p会失败,报Operation not permitted,需临时改配置或切 root -
futex_wait的超时参数为NULL(无限等待)很常见,但若业务逻辑本应带 timeout 却没设,就容易掩盖设计缺陷 - C++ RAII 锁(如
std::lock_guard)若在异常路径中析构失败(例如析构函数抛异常),会导致锁永远不释放——这种 bug 在栈里往往只显示异常传播路径,而非锁操作本身 - 多进程共享内存中的互斥量(
PTHREAD_PROCESS_SHARED)被不同进程访问时,strace -p只能看到本进程的等待,需同步检查其他进程是否 crash 或 hang 住持有者
真正耗时间的往往不是“看到 futex_wait”,而是确认那个地址对应的锁变量在源码里属于谁、当前谁在用、为什么没放。别跳过符号表和内存内容验证。










