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 [选项] [间隔] [次数]

常用参数

参数 说明
-u CPU 使用率(默认)
-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 算不过来。

相关推荐
IpdataCloud1 小时前
高精度IP定位服务怎么评估?从数据源、精度层级到实测验证的完整框架
服务器·网络·tcp/ip·ip
William.csj1 小时前
Bitvise SSH Client——配置跳板机:通过中转服务器连接内网服务器
运维·服务器·ssh
小诗懂技术1 小时前
Linux设备驱动并发控制总结_CSDN
linux·运维·服务器
colinu1 小时前
Docker 容器端口未发布问题快速排查文档
运维·docker·容器
迪康coolmu1 小时前
迪康Defender网络准入控制系统落地实战:部署、配置与问题排查记录
运维·开发语言·网络·经验分享·php
Dovis(誓平步青云)1 小时前
一年1956亿行:智能电表场景下的时序数据存储实践
运维·前端·自动化·音视频·prometheus
Y3815326621 小时前
SerpBase + RAG 实战:用搜索 API 给知识库接上实时数据,回答不再过期
java·服务器·数据库
北风toto1 小时前
银河麒麟 V10 内网 YUM 源修复实战教程(U盘中转版)
linux·服务器
韩曙亮1 小时前
【错误记录】网络配置问题处理 ( 获取服务器的 出口 和 入口 IP | 定位网络变化 )
服务器·网络·tcp/ip