pidstat —— 按进程/线程精准拆分 CPU、IO、上下文切换
三轨导读:本页是「深度实战与原理」版;同命令其他版本:5 分钟上手入门。
这个工具是做什么的
pidstat 是 sysstat 套件的一员,性能排查标配。相比 top,它的优势是按固定间隔持续采样、把 CPU 精确拆成用户态/内核态/IO 等待,并能下钻到线程,输出是纯文本行,天然适合记录和脚本解析。
可以回答什么问题
| 问题 | 怎么用 |
|---|---|
| 某个进程的 CPU 主要耗在用户态还是内核态? | pidstat -u -p <PID> 1 看 %usr vs %system |
| 多线程程序中哪个线程在烧 CPU? | pidstat -ut -p <PID> 1 展开到各个 TID |
| 进程在频繁做 IO 操作吗? | pidstat -d -p <PID> 1 看 kB_wr/s、iodelay |
| 上下文切换多不多?是等资源还是被抢占? | pidstat -w -p <PID> 1 区分 cswch/s(自愿)和 nvcswch/s(非自愿) |
| 有没有发生主缺页(触发磁盘 IO)? | pidstat -r -p <PID> 1 看 majflt/s |
| 线程在不同核心间迁移了吗? | pidstat -u -p <PID> 1 看 CPU 列是否频繁跳变 |
数据来源
- 来源文件:CPU 拆分取自
/proc/<pid>/stat,线程级取自/proc/<pid>/task/<tid>/stat,磁盘读写取自/proc/<pid>/io,上下文切换与内存取自/proc/<pid>/status。 - 采集方式:按你指定的间隔(如
1秒)把这些文件读两次,用累计值做差再除以间隔,得到速率(如kB_wr/s、cswch/s)和百分比(%usr/%system)。 - 由此决定的特性:不给间隔只读一次时,输出的是开机以来的平均值,没有实时意义——所以排查时务必带上间隔参数。
# 安装
yum install sysstat # CentOS/RHEL
apt install sysstat # Debian/Ubuntu
pidstat -V # 查看版本(不同版本列名略有差异)一、命令结构
pidstat [选项] [间隔] [次数]- 不给间隔:只输出一次自开机以来的平均值(参考意义小)。
pidstat 1:每 1 秒输出一次增量,一直刷。pidstat 1 5:每秒一次,共 5 次后退出。-p <PID>:只看指定进程;-p ALL看全部(含空闲);-p SELF看 pidstat 自己。- 支持小数间隔:
pidstat -u -p <PID> 0.4(每 400ms,观察更细腻)。
二、CPU:区分用户态 / 内核态(-u)
pidstat -u -p $(pgrep cpu_demo) 1真实输出(CentOS 7 / sysstat 10.1.5,跑 cpu_demo 场景2):
Linux 3.10.0-1160.el7.x86_64 (demo-centos) 2026年07月04日 _x86_64_ (4 CPU)
00时04分11秒 UID PID %usr %system %guest %CPU CPU Command
00时04分12秒 1001 4043 34.00 66.00 0.00 100.00 1 cpu_demo
00时04分13秒 1001 4043 31.00 68.00 0.00 99.00 2 cpu_demo
00时04分14秒 1001 4043 34.00 66.00 0.00 100.00 2 cpu_demo这段怎么读:%system 稳定 66% 左右(内核态占大头)、%usr 34%、%CPU 接近 100% —— 典型的**场景2(频繁 open/write/close 系统调用)**特征,内核态远高于用户态。CPU 列在 1/2/3 之间跳,说明该线程被调度器在不同核间迁移。
| 字段 | 含义 | 判读 |
|---|---|---|
%usr | 用户态 CPU | 业务计算、循环、加解密、序列化 → cpu_demo 场景1 拉满 |
%system | 内核态 CPU | read/write/open、锁竞争、malloc、缺页 → cpu_demo 场景2 偏高(如上例 66%) |
%guest | 运行虚拟机 vCPU 的时间 | 宿主机上跑 VM 才有,普通程序为 0 |
%wait | 进程在运行队列里等 CPU 的时间 | 仅新版有此列(见下方版本说明)。高 = CPU 不够/被抢占激烈;注意这不是 iowait |
%CPU | 该进程 CPU 总占用 | 多核可 >100%(按核累加) |
CPU | 最后运行在哪个逻辑核 | 频繁跳核(如上例 1→2→3)说明线程在核间迁移,可能影响 cache 命中 |
⚠️ 版本差异——
%wait列可能没有:%wait是 sysstat 11.5.5(约 2016 年)之后才加入的。CentOS 7 自带的 sysstat 10.1.5 输出里没有%wait(如上面真实输出),只有%usr/%system/%guest/%CPU。若需要看进程等 CPU 调度的时间,得升级 sysstat,或改用 top(%wa是整机 iowait,含义不同) / vmstat 的r队列间接判断。用pidstat -V查本机版本。
⚠️ 别混两个"wait":pidstat 的
%wait(新版才有)是等 CPU 调度;top/mpstat/iostat 里的%iowait是CPU 等 IO。二者完全不同。
三、线程级下钻(-t)
pidstat -ut -p $(pgrep cpu_demo) 115:22:10 UID TGID TID %usr %system %CPU CPU Command
15:22:11 1000 8921 - 0.00 0.00 99.00 3 cpu_demo
15:22:11 1000 - 8922 99.00 0.00 99.00 3 |__cpu_demoTGID是进程号,TID是线程号;TGID行是进程汇总,TID行(|__缩进)是各线程。- cpu_demo 的负载跑在
detach出来的子线程里,只有加-t才能看到是那个 TID 在烧 CPU,进程汇总行看不出线程分布。 - 定位到 TID 后,可用
perf top -t <TID>或在 gdb 里thread apply进一步看这个线程在干嘛。
四、磁盘 IO(-d)
pidstat -d -p <PID> 1| 字段 | 含义 |
|---|---|
kB_rd/s | 每秒从磁盘实际读取 KB(已扣除页缓存命中的部分) |
kB_wr/s | 每秒写入磁盘 KB |
kB_ccwr/s | 每秒被取消的写(如写了又被 truncate) |
iodelay | 该进程的块 IO 延迟(单位 jiffies,越大越卡) |
cpu_demo 场景2 疯狂
ofstream写文件,这里kB_wr/s会明显非 0;配合iostat -x看是不是把盘写满了。
五、上下文切换(-w)
pidstat -w -p <PID> 1| 字段 | 含义 | 判读 |
|---|---|---|
cswch/s | 自愿上下文切换/秒 | 主动让出 CPU:等锁、等 IO、等条件变量。高 = 频繁阻塞在资源上 |
nvcswch/s | 非自愿切换/秒 | 时间片耗尽被内核抢占。高 = CPU 竞争激烈、可运行线程过多 |
锁竞争严重的程序
cswch/s会飙高;CPU 超卖、线程数远超核数时nvcswch/s高。
六、缺页与内存(-r)
pidstat -r -p <PID> 1| 字段 | 含义 |
|---|---|
minflt/s | 次缺页/秒(在内存中,无需读盘,如首次访问已分配页) |
majflt/s | 主缺页/秒(需从磁盘加载,如换页回来、mmap 文件首次访问)。持续非 0 = 可能在 swap 或频繁 mmap IO |
VSZ | 虚拟内存 KB |
RSS | 常驻物理内存 KB |
%MEM | 物理内存占比 |
七、常用组合与技巧
pidstat -urd -p <PID> 1 # CPU + 内存 + 磁盘 一次看全
pidstat -ut 1 5 # 全系统所有线程,采 5 次(找出最耗 CPU 的线程)
pidstat -u 1 | awk '$8>50' # 只看 %CPU>50 的行(脚本过滤)
pidstat -u -p <PID> 1 > cpu.log & # 后台持续记录到文件八、关键指标速查
| 指标 | 缩写/英文 | 选项 | 正常值 | 异常信号 |
|---|---|---|---|---|
| 用户态 CPU | %usr / %User | -u | 视业务 | 高 + 低 system → 纯计算;需 perf 定位函数 |
| 内核态 CPU | %system / %System | -u | < 10% | > 30% → 大量 syscall,用 strace 定位 |
| CPU 总占用 | %CPU / Total CPU | -u | 视业务 | 多核累加可 > 100%;注意 Irix 模式 |
| CPU 等待 | %wait / CPU Wait | -u | ≈ 0 | 高 → CPU 不够,任务在运行队列排队(新版才有) |
| 核间迁移 | CPU 列 | -u | 稳定在某核 | 频繁跳核 → 缓存变冷,考虑绑核 |
| 自愿切换 | cswch/s | -w | 视业务 | 很高 → 频繁阻塞(IO/锁/条件变量) |
| 非自愿切换 | nvcswch/s | -w | < 100/s | > 几千/s → CPU 竞争激烈、线程过多 |
| 磁盘读速率 | kB_rd/s | -d | 视业务 | 突然暴涨 + await 高 → 读盘瓶颈 |
| 磁盘写速率 | kB_wr/s | -d | 视业务 | 突然暴涨 + w_await 高 → 写盘瓶颈 |
| IO 延迟 | iodelay | -d | ≈ 0 | 持续 > 几十 tick → 进程卡在等磁盘 |
| 次缺页 | minflt/s | -r | 视业务 | 极高 → 频繁分配新页或 COW |
| 主缺页 | majflt/s | -r | 0 | 持续 > 0 → swap/频繁 mmap,触发磁盘 IO |
| 常驻内存 | RSS / Resident | -r | 视业务 | 单调增长 → 内存泄漏 |
九、常见排查方法与分析
9.1 stime/内核态高 → 定位系统调用
pidstat -u -p <PID> 1 → 确认 %system 高
→ strace -c -p <PID> → 看哪个 syscall 次数多/耗时长
→ 若是 openat/write/close 次数异常 → 加缓冲或复用 fd
→ 若是 futex 耗时高 → 锁竞争,用 perf lock 分析9.2 上下文切换定位
pidstat -w -p <PID> 1 → 区分 cswch 和 nvcswch
→ cswch 高 → 任务在等什么
→ strace -p <PID> 看卡在哪个 syscall(read/futex/poll)
→ futex → 锁竞争;read → IO 阻塞
→ nvcswch 高 → CPU 被抢占
→ 线程数 > 核数 → 减少线程或加核
→ 线程数 ≈ 核数但仍高 → 检查调度策略(chrt -p)9.3 磁盘 IO 进程定位
iostat -x 1 → 确认某块盘 await 高
→ pidstat -d 1 → 看 kB_wr/s, kB_rd/s 最高的进程
→ pidstat -d -p <PID> 1 → 确认 IO 速率稳定
→ iotop -o → 看 IO> 等待占比(需 root)
→ lsof -p <PID> → 看在操作哪些文件9.4 内存泄漏排查
pidstat -r -p <PID> 1 → 持续观察 RSS 是否只增不减
→ 若 RSS 持续增长 → 内存泄漏嫌疑
→ cat /proc/<PID>/maps → 看哪个内存段在涨
→ 引入 valgrind / AddressSanitizer 复现9.5 核间迁移分析
pidstat -u -p <PID> 1 | awk '{print $NF}' → 连续观察 CPU 列
→ 若 PID 每秒在不同核上 → 迁移频繁
→ taskset -cp <PID> → 查看当前亲和性
→ taskset -cp 0-3 <PID> → 绑核减少迁移
→ 但 bind 后可能失去负载均衡的好处,需实测对比十、交叉引用
- 整机概览:top(先确定哪个进程 CPU 高再看 pidstat)
- 逐核分析:mpstat(看负载是否均匀/软中断分布)
- 调度观测:scheduling-observation(从 vmstat r/cs 到 pidstat -w 的完整链路)
- CPU 函数级:perf(%usr 高时定位热点函数)
- 系统调用:strace(%system 高时定位 syscall)
- 磁盘 IO:iostat(设备级)→ iotop(进程级 IO> 占比)
- 内存:free(整机)→ pidstat -r(进程级 RSS/缺页)
- 数据源:procfs §二(/proc/<pid>/stat、status、io)
十一、常见坑
- 只给
-p不给间隔:只打印一次开机以来的平均,看不到实时波动——记得加间隔如1。 - 不同版本列名不同:老版本可能没有
%guest/%wait,或%system叫别的;以本机pidstat -V和实际表头为准。 %CPU与 top 对不上:pidstat 默认按逻辑核累加(和 top 的 Irix 模式一致),核数多时数值大是正常的。- 进程重启后 PID 变了:用
-p $(pgrep name)动态取,或直接-C <命令名>按名字匹配(新版支持-C)。
总结
pidstat 是 top 之外最可脚本化的进程级工具:按固定间隔读 /proc/<pid>/stat、io、status 两次做差,把 CPU 精确拆成 usr/system/wait、下钻到具体线程(-ut),并用 -w 区分自愿/非自愿切换、-d 看磁盘速率、-r 抓主缺页;关键纪律是必须带间隔参数,否则只输出开机以来的平均值。