strace -p 显示卡在 futex_wait 或 poll 表明进程正阻塞于内核态等待同步事件或 I/O 就绪,需通过 ps、/proc/PID/stack、gdb、lsof 等工具定位等待原因、持有者及死锁链。

当 strace -p 挂上进程后卡在 futex_wait 或 poll,说明进程正阻塞在内核态等待某个同步事件(如互斥锁、条件变量、I/O 就绪等),此时 strace 本身不会卡住,但你看到的“卡”其实是进程真实处于睡眠状态。关键不是让 strace “继续”,而是定位它为何等、等什么、谁在控制这个等待。
确认是否真卡:区分挂起与正常阻塞
strace 显示停在 futex_wait 或 poll 是常见且正常的——这表示系统调用尚未返回,进程被内核调度为 S(可中断睡眠)或 D(不可中断睡眠)状态。
- 用
ps -o pid,ppid,comm,wchan,state -p PID查看当前等待的内核函数(wchan)和状态:
• 若wchan是futex_wait_queue_me、do_futex,说明卡在 futex 上;
• 若是do_sys_poll、ep_poll,说明卡在 poll/epoll 等待 I/O;
• 若state是D(尤其是 futex 场景),需警惕死锁或资源耗尽(如信号量未释放、持有锁的线程已崩溃)。 - 用
cat /proc/PID/stack查看内核栈(需 root),可确认具体等待路径,例如是否在mutex_lock_slowpath→futex_wait,或ep_poll→wait_event_interruptible。
深挖 futex_wait:定位锁持有者与死锁链
futex 等待本质是用户态加锁失败后陷入内核等待唤醒,真正线索在用户态锁状态和持有者线程。
- 用
gdb -p PID进入进程,执行:
•info threads查看所有线程及状态;
• 对每个线程执行bt(尤其关注状态为Blocked或长时间停在pthread_mutex_lock、__lll_lock_wait的线程);
• 若有符号,可查pthread_mutex_t地址,用print *(pthread_mutex_t*)0xADDR看__owner字段(持有线程 tid)。 - 若无调试符号,可用
pstack PID快速抓全部线程用户栈(等价于 gdb + bt 所有线程);结合/proc/PID/maps和readelf -S可尝试定位锁变量内存位置。 - 检查是否发生死锁:多个线程互相等待对方持有的 mutex/condvar,典型表现为 A 等 B 持有的锁,B 等 C 持有的锁,C 又等 A 持有的锁 —— 此时需人工梳理锁获取顺序或用
lsof -p PID+ls /proc/PID/fd辅助判断资源依赖。
深挖 poll 等待:厘清 I/O 事件源与就绪条件
poll/epoll/pselect 等调用卡住,说明指定的 fd 集合中没有任何一个满足触发条件(如可读、可写、异常)。问题往往出在事件源未就绪或 fd 被误关闭/重复使用。
- 用
lsof -p PID或ls -la /proc/PID/fd/列出所有打开 fd,重点关注:
• socket fd:看协议(TCP/UDP)、状态(ESTABLISHED、CLOSE_WAIT)、对端地址;
• pipe/fifo:是否有读/写端关闭;
• eventfd/timerfd:是否被正确 write/read;
• epoll fd 本身:确认是否已通过epoll_ctl添加了目标 fd。 - 结合
ss -tulnp或netstat -tulnp检查监听端口、连接状态,确认对端是否存活、是否发送了数据;若等待的是本地 pipe/eventfd,用strace -e trace=write,read -p PID单独跟踪读写行为,看是否有 write 发生但未被 read 消费。 - 注意超时值:若
poll第三个参数为-1(无限等待),则必须有外部事件触发;若为 0(非阻塞),则不应卡住 —— 此时卡住大概率是 strace 误判或进程实际卡在其他地方(如信号处理中被暂停)。
辅助诊断与规避技巧
有些场景下 strace 自身会干扰行为(如信号屏蔽、时间戳精度),或难以持续观察,需配合其他工具。
- 用
perf record -e 'syscalls:sys_enter_futex,syscalls:sys_enter_poll' -p PID抓取系统调用入口,再用perf script分析调用上下文和参数(如 futex 的 uaddr、op、val)。 - 对多线程程序,优先用
strace -f -p PID跟踪所有线程,避免只盯主线程而错过持有锁的子线程。 - 若进程对 strace 敏感(如反调试、自修改代码),改用
gdb附加后设置断点:
•break syscall或具体函数如pthread_mutex_lock;
•catch syscall futex监控 futex 调用细节(需内核支持)。 - 生成 core dump(
gcore PID)后离线分析,避免线上反复附加影响服务。









