pstack不能直接查看卡死,需连续多次执行以锁定卡点;它只显示用户态栈顶帧,通过epoll_wait()、read()等libc函数可判断内核态阻塞,而__lll_lock_wait()等则提示用户态死锁风险。

pstack 不能直接“查看卡死”,它只打印当前瞬时状态;但连续多次执行,能帮你锁定卡在哪儿——这是最轻量、最快速的现场诊断方式。
怎么用 sudo pstack PID 抓到有效堆栈
必须由进程属主或 root 执行,普通用户会报错 Permission denied。常见错误是漏掉 sudo,尤其当进程由 systemd 或其他用户启动时。
- 先用
ps aux | grep my_app或pidof my_app确认 PID,别凭记忆硬输 - 执行
sudo pstack <pid></pid>,输出里每段以Thread X (LWP YYY)开头,对应一个线程 - 如果输出为空或只有
Cannot attach to process,大概率是进程已退出、权限不足,或被 seccomp / ptrace_scope 限制(可临时设echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope)
看到哪些函数名说明大概率卡在内核态
pstack 只显示用户态栈顶帧,但它暴露的 libc 封装函数就是关键线索。只要栈顶是这些,基本可判定线程正阻塞在内核中,未返回用户空间:
-
epoll_wait()、poll()、select():事件循环线程正常等待,但若所有工作线程都停在这,说明无新请求进来或前端断连 -
read()、recvfrom()、accept():socket 阻塞读/连接,可能对端未发数据、网络中断或防火墙拦截 -
pthread_cond_wait()、sem_wait():背后调用futex(),但阻塞根源可能是条件未满足或通知丢失 -
nanosleep()、clock_nanosleep():主动休眠,一般没问题;但如果休眠时间异常长(比如几小时),需查定时器逻辑
怎么从输出识别死锁或用户态卡死
死锁不会让 CPU 占满,反而常表现为所有线程都停在锁相关调用上,且互相持有对方需要的锁。重点看栈底几层是否出现以下模式:
-
__lll_lock_wait()→pthread_mutex_lock():线程在等一个 mutex,而该 mutex 很可能被另一个线程持有着 - 两个线程分别卡在
pthread_mutex_lock(&lock_a)和pthread_mutex_lock(&lock_b),且各自已持有另一个锁(需结合源码或符号信息判断) - 栈里反复出现
std::mutex::lock()、std::unique_lock、std::condition_variable::wait(),但没有业务函数调用,说明锁竞争激烈或逻辑有误 - 若某线程栈顶是
__GI___libc_read()但 fd 是 pipe/fifo,而另一端没写入,也属于用户态“假死”,非死锁但效果类似
为什么单次 pstack 不够,要连续执行 3–5 次
因为线程状态是动态的:一次快照可能刚好拍到 epoll_wait 的健康等待,另一次可能拍到锁争抢的临界点。真正卡死的线程,其栈顶函数在多次采样中会高度一致。
- 用
for i in {1..5}; do sudo pstack <pid>; sleep 0.5; done > pstack.log</pid>保存连续输出 - 用
grep '#0' pstack.log | sort | uniq -c | sort -nr统计栈顶函数出现频次,高频项就是瓶颈点 - 注意区分“所有线程都卡在同一个函数”(如全卡
epoll_wait)和“多个线程卡在不同锁上互等”(典型死锁特征)
真正容易被忽略的是:pstack 看不到内核路径,也看不到锁的持有者是谁。它只告诉你“谁在等”,不告诉你“谁拿着”。要定位持有者,得配合 gdb -p <pid></pid> + info threads + thread apply all bt,或者用 /proc/<pid>/stack</pid>(需内核开启 CONFIG_PROC_KCORE)看内核栈。但绝大多数线上卡顿问题,靠 pstack 连续采样就能快速圈定范围——别一上来就上 gdb,那会拖慢服务甚至触发更多竞争。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











