pstack 可直接打印多线程程序卡顿时的实时调用堆栈,但需确保进程状态为r/s(非d状态)、当前用户有权限或sudo权限、系统已安装gdb且ptrace_scope允许attach,输出按线程分组,重点关注停留于epoll_wait等系统调用或重复出现的栈帧。

直接用 pstack <pid></pid> 就能打印多线程程序在卡顿时的实时调用堆栈,关键在于确保进程状态可抓、权限到位、符号可用。
确认目标进程处于可调试状态
卡顿进程不能是 D 状态(不可中断睡眠),否则 pstack 会报 Cannot attach to process。可用以下命令快速判断:
-
ps -o pid,comm,state -p <pid></pid>—— 查看 state 列,D 表示无法 attach,R/S 才能成功 -
cat /proc/<pid>/status | grep State</pid>—— 更明确识别状态 - 若为 D 状态,说明进程正等待 I/O 或内核资源,此时需结合
iotop、vmstat或/proc/<pid>/stack</pid>(仅限内核线程或部分 kernel)进一步分析
保证权限和依赖就绪
pstack 本质调用 gdb,所以必须满足:
- 当前用户是进程属主,或有
sudo权限(否则报Permission denied) - 系统已安装
gdb:sudo apt install gdb(Debian/Ubuntu)或sudo yum install gdb(RHEL/CentOS) - 检查 ptrace_scope 限制(常见于 Ubuntu/Debian):
cat /proc/sys/kernel/yama/ptrace_scope。值为 1 时普通用户不能 attach 非子进程,临时放开可运行:echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
执行并解读多线程堆栈输出
运行 pstack <pid></pid> 后,输出按线程分组,每组以 Thread N (LWP XXXX): 开头:
- 重点关注长时间停留在系统调用(如
epoll_wait、pthread_cond_wait、read)或自旋/循环函数中的线程 - 若某线程反复出现在同一行(比如多次 pstack 都停在
server.c:88),大概率是逻辑阻塞点 - 看到大量
??()或十六进制地址,说明缺少调试符号:编译时加-g,或确保libstdc++-debuginfo等配套 debuginfo 包已安装
补充技巧:快速定位卡点
单次 pstack 可能不够,建议连续采样比对:
- 间隔 2–3 秒执行多次:
for i in {1..5}; do pstack <pid> >> pstack.log; sleep 2; done</pid> - 用
grep -A 2 "Thread [0-9]" pstack.log | grep "#" | sort | uniq -c | sort -nr统计高频栈帧 - 若程序是 Go/Java/Rust 等语言,pstack 效果有限:Go 推荐
kill -SIGUSR1 <pid></pid>触发 runtime dump;Java 用jstack <pid></pid>











