ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Linux CPU性能分析:top、mpstat、pidstat、perf实战

Linux CPU性能分析:top、mpstat、pidstat、perf实战

CPU 是服务器上最容易被「冤枉」也最容易被「放过」的资源。明明top显示 CPU 才 30%,应用却慢得不行——很可能是单核打满、IO 等待,或者锁竞争在偷走时间。这一篇把 CPU 分析的四层工具讲透:top看全局、mpstat看核心、pidstat看进程、perf看函数,层层下钻,直到找出真正的瓶颈。

一、CPU 分析的四个层次

排 CPU 问题,最忌讳一上来就盯着top的进程榜。正确的思路是层层下钻:

层次工具回答什么问题
系统概览top/uptime整体忙不忙、负载高不高
每核视角mpstat -P ALL是所有核忙,还是单核打满
进程视角pidstat哪个进程、哪个线程在吃 CPU
函数视角perf到底哪行代码、哪个系统调用在耗 CPU

先看全局,再看核心分布,再看进程,最后才用perf钻到函数。跳步容易误判。

二、top:先分清 us、sy、wa

top的第一行%Cpu(s)是最容易读错的地方。先把这几个字段记住:

字段含义高了说明什么
us用户态 CPU 时间应用本身在算,正常
sy内核态 CPU 时间系统调用/中断多,或内核开销大
ni低优先级(nice)用户态手动调过 nice 的进程占的
id空闲越低越忙
wa等待 IO 的时间磁盘/网络是瓶颈
hi硬中断设备中断多,常见于网络/磁盘风暴
si软中断网络收包处理多,常见于高流量服务器
st被虚拟化偷走的时间虚拟机被宿主机抢占,越高越慢

关键判断规则:

  • us高:应用逻辑重,去看代码热点。
  • sy高:系统调用频繁、上下文切换多,去看vmstat的cs。
  • wa高:这不是 CPU 问题,是 IO 问题,去查磁盘(见第 66 篇)。
  • si高:软中断处理多,多半是网络包量太大。
  • st高:你在虚拟机上,被隔壁邻居抢了,只能找云厂商。

一个高频误区:load average 高 ≠ CPU 忙

uptime和top顶部的load average,统计的是「运行队列 + 不可中断睡眠(D 状态)的进程数」的滑动平均。所以:

  • 大量进程卡在 IO(D 状态),load 也会很高,但 CPU 其实很闲。
  • 判断 CPU 是否真的紧张,要看 load 是否由R状态贡献,以及是否超过 CPU 核心数。

用一行命令区分:

vmstat 1 3

看r列(运行队列)和b列(不可中断睡眠)。r大才是 CPU 排队,b大是 IO 在拖。

三、mpstat:每个核心的独立视角

top默认显示的是所有核的平均使用率,这会掩盖「单核打满」的问题。一个 16 核的机器,一核 100%、其余空闲,平均值只有 6.25%,看起来一切正常。mpstat就是来解决这个的。

mpstat属于sysstat包,先装:

# Ubuntu / Debian sudo apt install sysstat -y # CentOS / Rocky sudo dnf install sysstat -y

语法格式

mpstat [选项] [间隔] [次数]

常用参数

参数说明
-P ALL显示每个核心,关键参数
-P 0只看 0 号核
1 5每秒采样,共 5 次
-u显示 CPU 使用率(默认)
-I SUM显示中断统计

示例与预期输出

mpstat -P ALL 1 3

预期输出:

Linux 5.15.0 (web01) 01/15/2026 _x86_64_ (4 CPU) 10:30:01 AM CPU %usr %nice %sys %iowait %irq %soft %steal %idle 10:30:02 AM all 6.25 0.00 1.50 0.25 0.00 0.25 0.00 91.75 10:30:02 AM 0 99.00 0.00 1.00 0.00 0.00 0.00 0.00 0.00 10:30:02 AM 1 0.00 0.00 0.50 0.00 0.00 1.00 0.00 98.50 10:30:02 AM 2 0.00 0.00 2.00 0.50 0.00 0.00 0.00 97.50 10:30:02 AM 3 0.00 0.00 1.00 0.50 0.00 0.00 0.00 98.00

一眼看出问题:all行看起来只用了 6%,但0 号核 99% 打满。典型的单线程程序只吃一个核。这种问题只有mpstat -P ALL才能暴露。

什么时候该怀疑单核瓶颈

  • 应用是单线程或单线程池,跑在一个核上。
  • 应用有全局锁,多线程也没法并行。
  • 中断亲和性设置把所有网卡中断都绑到一个核。

单核打满时,横向扩容进程数(多实例)往往比优化单进程更有效。

四、pidstat:定位到进程和线程

知道是哪个核忙了,还要知道是谁在忙。pidstat是sysstat里的进程级采样工具,比top更适合做数据记录。

语法格式

pidstat [选项] [间隔] [次数]

常用参数

参数说明
-uCPU 使用率(默认)
-t显示线程级统计
-p PID指定进程
-d磁盘 IO 统计
-r内存统计
-w上下文切换统计

示例与预期输出

所有进程按 CPU 排序:

pidstat -u 1 3 | sort -k8 -nr | head

看某进程内部的线程谁在吃 CPU:

pidstat -t -p 1234 1 3

预期输出:

Average: UID TGID TID %usr %system %CPU CPU Command Average: 1000 1234 - 2.00 1.00 3.00 - java Average: 1000 - 1235 95.00 1.00 96.00 2 |__java Average: 1000 - 1236 0.50 0.20 0.70 1 |__java

TGID是进程号,TID是线程号。一眼看出 1235 号线程吃了 96% CPU,拿着它去jstack或gdb定位就够了。

看上下文切换(排查「CPU 不高但很慢」):

pidstat -w -p 1234 1 3
Average: cswch/s nvcswch/s Command Average: 12500.00 850.00 java
  • cswch/s:自愿上下文切换(进程主动让出,如等锁、等 IO)
  • nvcswch/s:非自愿切换(时间片用完被抢占)

自愿切换极高,通常是锁竞争或频繁 IO;非自愿切换高,说明 CPU 争抢激烈。

五、perf:钻到函数级

前三层告诉你「哪个进程、哪个线程」,perf告诉你「哪个函数」。它是 Linux 官方性能分析工具,基于内核的 PMU(性能监控单元)采样。

安装

sudo apt install linux-tools-common linux-tools-$(uname -r) # Ubuntu sudo dnf install perf # CentOS/Rocky

常用子命令

命令作用
perf top实时显示热点函数(类似 top 但到函数级)
perf stat统计程序整体指标(指令数、缓存命中)
perf record采样记录到文件
perf report分析 record 生成的 perf.data
perf script把记录导出成文本,供火焰图工具用

示例与预期输出

实时看系统热点函数:

sudo perf top
Overhead Shared Object Symbol 35.20% libc-2.31.so [.] __memmove_avx_unaligned 18.40% [kernel] [k] __schedule 9.70% app [.] parse_json

__memmove占 35%,说明大量内存拷贝;__schedule高说明调度频繁。这些线索直接指向优化方向。

统计程序整体性能:

sudo perf stat ./app
1,204,582,391 instructions # 0.85 insn per cycle 1,417,155,724 cycles 412,036,588 cache-references 88,201,004 cache-misses # 21.40% of all cache refs

关键看两个:IPC(insn per cycle)和cache-misses 比例。IPC < 1 说明 CPU 在等(多为缓存未命中或依赖停顿);cache-misses 高说明内存访问模式差。

生成火焰图(最常用):

# 采样 30 秒 sudo perf record -F 99 -a -g -- sleep 30 sudo perf script > out.perf # 用 FlameGraph 工具生成 ./stackcollapse-perf.pl out.perf | ./flamegraph.pl > flame.svg

火焰图第 69 篇会专门讲,这里先记住perf record -g是入口。

六、⚠️ 常见错误

  1. 只看平均值错过单核瓶颈。top的百分比是所有核平均,单核打满会被稀释。用mpstat -P ALL。
  2. 把 wa 高当成 CPU 问题。wa是「CPU 在等 IO」,根因在磁盘/网络,去查第 66 篇,别死磕 CPU。
  3. load 高就加 CPU。如果 load 是被D状态(IO 阻塞)推高的,加 CPU 没用。先用vmstat区分r和b。
  4. 忽视 st(steal)。云主机st高是被宿主机邻居抢资源,优化自己没用,要么换实例规格,要么换可用区。
  5. perf 采样被内核限制。默认perf_event_paranoid可能禁止普通用户采样,报Permission denied就加sudo或调sysctl kernel.perf_event_paranoid。
  6. sy 高就怪内核。sy高往往是应用频繁系统调用或锁竞争导致,未必是内核本身的问题。

七、原理探究:上下文切换、中断与 CPU 缓存

CPU 的速度比内存快两个数量级,比磁盘快五六个数量级。理解 CPU 性能,核心是理解「它在等什么」。

上下文切换(context switch)

CPU 核心只有一个,多个进程/线程要轮着用。切换时要保存当前任务的寄存器、程序计数器、栈指针,再加载下一个任务的——这一套动作本身就要花时间,更糟的是会污染 CPU 缓存:新任务的数据不在缓存里,得重新从内存拉,这就是cache miss。

所以:

  • 上下文切换次数(vmstat的cs、pidstat -w)过高,CPU 大量时间花在「切换」而不是「干活」上。
  • 自愿切换多→ 等锁、等 IO;非自愿切换多→ 被迫让出,CPU 争抢。

中断

设备(网卡、磁盘)完成工作时,会发中断通知 CPU。中断分两种:

  • 硬中断(hi):设备直接打断 CPU,处理要快、要短。
  • 软中断(si):硬中断的下半部,延后处理。网络收包大量走软中断。

高流量服务器si常年偏高很正常,但如果si高到挤占业务 CPU,就要上多队列网卡、开启 RPS/RSS、调中断亲和性来分摊。

CPU 缓存层级

现代 CPU 有三层缓存:

层级特点典型延迟
L1每个核私有,最小最快~1ns
L2每个核私有~4ns
L3多核共享~10ns
内存所有核共享~100ns

程序的数据局部性越好,命中缓存越多,CPU 利用率越高(IPC 越大)。perf stat的 cache-misses 就是看这个。优化数据结构和访问顺序,有时比换更快的 CPU 更有效。

一句话串起来

  • CPU 忙 → 看us(应用)/sy(内核)
  • CPU 不忙但慢 → 看wa(IO)、steal(虚拟化)、上下文切换
  • 平均正常但应用慢 → 看单核(mpstat -P ALL)、看锁(pidstat -w)
  • 定位到进程后 →perf钻到函数

八、CPU 排查速查表

# 1. 整体概览 uptime && top -bn1 | head -5 # 2. 每核分布 mpstat -P ALL 1 3 # 3. 找吃 CPU 的进程(用户态) ps -eo pid,cmd,%cpu --sort=-%cpu | head # 4. 找吃 CPU 的进程(内核态/上下文切换) pidstat -w 1 3 # 5. 函数级热点 sudo perf top # 6. 程序整体性能 sudo perf stat -d ./app

记住排查的主线:全局 → 核心 → 进程 → 线程 → 函数,一步一层,别急着下结论。多数「CPU 高」的锅,最后都落在单核瓶颈、锁竞争或 IO 等待上,而不是真的 CPU 算不过来。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进