Linux 性能排查实战:用 perf、strace 与火焰图定位 CPU、内存与 IO 瓶颈

摘要:生产环境接口变慢、机器负载高,但瓶颈究竟在 CPU、内存还是 IO?本文把 perf(采样剖析)、strace(系统调用追踪)与火焰图(可视化)串成一套可复用的排查方法论:先粗判方向,再选工具,读图定位根因,最后避开开销与权限陷阱。所有命令均可直接复现。

导语

"服务又变慢了,但 CPU 没满啊?"------这是后端和运维同学最常被问到、也最难一句话答清的问题。

top%CPU 不高,不代表没有性能问题:延迟可能耗在等磁盘 IO、耗在被换页的内存压力上,也可能耗在某个卡住的网络 connect 上,而这部分时间根本不体现在 CPU 占用里。方向判错,后面再怎么调优都是白做。

本文不堆概念,聚焦一个目标:当系统"慢"下来,如何用 perf、strace 和火焰图,在最短时间内把瓶颈钉死在 CPU、内存还是 IO 上。文中所有命令都来自一手工具手册(strace(1)、perf_event_open(2)、内核 perf-security 文档),可放心复现。

一、背景:为什么需要 perf、strace 与火焰图

生产排障的真实困境:"慢"不等于"CPU 满"

一个接口 P99 从 50ms 涨到 800ms,登上去 top 一看:%CPU 可能只有 30%,iowait(IO 等待)却飙到 40%;或者 CPU 看着正常,但 free -h 显示可用内存被吃光、开始 swap。三种典型瓶颈表现完全不同:

  • CPU 饱和:用户态或内核态占满 CPU,热点函数需要被定位;
  • 内存压力:缺页风暴、缓存未命中率偏高、swap 换入换出;
  • IO 阻塞 :磁盘读写等待、或进程卡在某个网络/文件 syscall

三者解法完全不同,所以第一步永远是"定方向",而不是上来就盲调。

三大工具的分工边界

复制代码
+----------------+    +----------------+    +------------------+
|     perf       |    |    strace      |    |    火焰图        |
+----------------+    +----------------+    +------------------+
| 基于 PMU 的    |    | 系统调用级    |    | 把 perf 采样     |
| 采样剖析       |    | 追踪          |    | 结果可视化       |
| 定位 CPU 热点  |    | 看进程卡在    |    | 调用栈耗费       |
| 与内核态开销   |    | 哪个 syscall  |    | 一目了然         |
| (on-CPU)      |    | (IO/锁/信号)  |    |                  |
+----------------+    +----------------+    +------------------+
  • perf :基于 Linux perf_events 性能监控子系统,通过 PMU 硬件计数器与软件事件做采样,擅长定位 CPU 热点与内核态开销(on-CPU 时间)。
  • strace:系统调用级追踪,擅长看进程"卡在哪个 syscall"------文件 IO、网络 IO、锁、信号都能看到。
  • 火焰图:本身不是采集工具,而是把 perf 的采样结果可视化,直观看出"时间花在哪个调用栈"。

三者关系是:perf / strace 负责"采",火焰图负责"看"。

本文阅读路径与读者画像

面向后端开发、运维与 SRE。建议先通读第二节把工具链装好,再按"CPU→内存→IO"顺序理解三类瓶颈的打法,最后用第六节的综合实战串成完整排查剧本。

二、工具链总览与准备

工具来源与版本约束

perflinux-tools 提供,最好与运行内核版本一致 ,否则部分硬件事件可能不可用(这是内核 perf-security 文档明确提到的行为约束)。strace 是独立软件包;火焰图由 FlameGraph 脚本集(perf script 的下游工具)生成。

安装思路(注意版本匹配):

bash 复制代码
# Debian/Ubuntu:perf 版本需与当前内核对应
apt install -y linux-tools-common linux-tools-$(uname -r) strace

# 校验版本
perf --version
strace -V
uname -r

获取 FlameGraph 脚本集(Brendan Gregg 维护的 stackcollapse-perf.plflamegraph.pl)后,把脚本目录加入 PATH,后续即可直接调用。

端到端数据流

复制代码
perf record  (采样,产出 perf.data)
     |
     v
perf script  (导出带调用栈的事件流)
     |
     v
stackcollapse-perf.pl  (折叠相同调用栈,产出 folded 文本)
     |
     v
flamegraph.pl  (生成 SVG)
     |
     v
perf.svg  (火焰图,浏览器打开)

整条链路是"采样 → 导出 → 折叠 → 渲染",每一环产物都明确,排查时哪一步缺了产物一眼能看出来。

容器 / 虚拟化注意点

perf 在容器内需 CAP_PERFMON 或特权,否则多数事件会被内核拒绝;strace 同样受 ptrace 权限限制。在 K8s 等环境里,先确认 Pod 的 securityContext 是否放开相关能力,再决定能否直接 attach。

三、perf 采样原理与 CPU 瓶颈定位

perf 采样原理(一手来源:perf_event_open(2))

perf 基于 Linux perf_events 子系统工作,既支持硬件事件(PMU 计数器,如 CPU cycles),也支持软件事件(如上下文切换、缺页)。

采样的核心机制:通过 sample_period(每 N 个事件采样一次)或 sample_freq(每秒采样次数)控制频率。计数器溢出时,内核把一条样本写入 mmap 环形缓冲区(ring buffer),再经 perf 用户态解析。这就是为什么采样对目标进程侵入小------它只是"周期性记账",而非逐条拦截。

CPU 热点定位标准动作

最常用的采集命令:

bash 复制代码
# -F 99:每秒采样 99 次(99 Hz 是社区惯例频率,平衡开销与精度)
# -a:系统范围  -g:记录调用栈  -- sleep 30:采样 30 秒
perf record -F 99 -ag -- sleep 30

# 针对某个进程
perf record -F 99 -p $PID -g -- sleep 30

# 交互式查看热点
perf report

# 实时看热点函数
perf top -p $PID

# 看宏观计数器
perf stat -e cycles,instructions,cache-misses -p $PID -- sleep 10

-F 99 而非整百(如 100),是社区为了避免与某些周期性任务"对齐共振"而约定俗成的频率,既能覆盖热点又不会放大自身开销。

如何读 CPU 火焰图

把第三节的采样直接生成火焰图:

bash 复制代码
perf script | stackcollapse-perf.pl | flamegraph.pl > cpu.svg

火焰图的读法:

  • 横轴是采样总量(不是时间顺序),越宽代表占用 CPU 越多;
  • 纵轴是调用栈深度,从下往上是函数调用链;
  • 最宽的那个栈就是最耗 CPU 的路径,优先从这里下手优化。

验证要点

  • 采样时间要覆盖业务高峰,偶发抖动采样不到会误判;
  • -F 过高(如 999)开销大,还可能因 perf 自身占用污染结果,惯例用 99。

四、内存瓶颈定位

内存类瓶颈的常见形态

内存问题不像 CPU 那样直接"满",常见有:缺页风暴(major/minor faults 过多)、缓存未命中率高、swap 换入换出、以及 RSS 持续增长(疑似内存泄漏)。

用 perf 看内存相关事件(一手:perf_events 事件)

perf 可以直接量化内存与缓存行为:

bash 复制代码
# 量化缓存与缺页
perf stat -e cache-misses,cache-references,page-faults,minor-faults,major-faults \
  -p $PID -- sleep 10

# 定位是谁在触发缺页(分配热点)
perf record -e page-faults -ag -p $PID -- sleep 10

major-faults(需要从磁盘换入的缺页)显著偏高,往往意味着内存压力已经逼出了 IO;cache-misses 高则说明热点数据没留在 CPU 缓存里。

配合系统级观测

bash 复制代码
free -h && vmstat 1 5
cat /proc/meminfo

vmstatsi/so(swap 换入换出)和 free 的 available 列,能快速判断整机是否进入内存紧张状态;结合某进程 RSS 的增长趋势,可辅助判断是否存在泄漏。

说明边界

深度内存泄漏分析通常需要堆剖析工具(如 jemallocjeprof、Valgrind 等),它们不在本文 perf/strace 主线上,点到为止。

五、IO 瓶颈定位

IO 瓶颈的两种表现

一是磁盘 IO 等待(iowait 高),二是进程在 read/write/open 等 syscall 上阻塞;网络 IO 阻塞也归在这一类。单看 top%CPU 是发现不了这类问题的。

strace 系统调用追踪(一手来源:strace(1))

strace 跟踪进程的系统调用与信号,关键选项:

  • -c:汇总统计每个系统调用的次数、耗时与错误数,并抑制逐条输出;
  • -T:显示每次系统调用的耗时(默认微秒精度);
  • -tt:每行输出带微秒精度的绝对时间戳;
  • -e trace=file,network:只跟踪"带文件名的调用"和"网络相关调用";
  • -p PID:附加到已运行进程;
  • -f:跟踪由 fork/vfork/clone 创建的子进程(多线程进程下会附加到所有线程)。

定位"卡在哪个调用"

bash 复制代码
# 逐条看耗时,聚焦文件与网络调用
strace -Ttt -e trace=file,network -f -p $PID

# 汇总统计算法更快定位热点调用
strace -c -f -p $PID -- sleep 5

-T 标出的高耗时 read/write/connect 就是阻塞点:比如某 connect 每次都要几百毫秒,问题大概率在网络或下游服务,而不是本机 CPU。

系统级 IO 观测联动

bash 复制代码
iostat -x 1 && pidstat -d 1

iostat -x 看磁盘吞吐与 await(IO 平均等待),pidstat -d 看每进程 IO,与 strace 的结论相互印证,避免被单一工具误导。

六、综合实战:一次"接口变慢"的端到端排查

场景设定

某在线接口 P99 时延突增,目标是在 30 分钟内给出根因方向(改代码 / 调参数 / 扩容)。

排查决策树

复制代码
1. top / mpstat 看 CPU 是否饱和、iowait 是否高        --> 定方向
2. CPU 方向: perf top / perf record 抓热点,或出火焰图
3. syscall 可疑: strace -T 看是否卡在文件/网络调用
4. 内存疑点: perf stat 看 cache/page-fault,结合 free/vmstat
5. 汇总证据 --> 给出 改代码 / 调参数 / 扩容 结论

串联命令示例

把前面各段命令按决策树顺序组合成一次完整排查:

bash 复制代码
# 1) 定方向
top -b -n 1 | head -20
mpstat 1 3

# 2) CPU 热点 + 火焰图
perf record -F 99 -ag -p $PID -- sleep 20
perf script | stackcollapse-perf.pl | flamegraph.pl > cpu.svg

# 3) syscall 层
strace -Ttt -e trace=file,network -f -p $PID -- sleep 5

# 4) 内存 / 缓存
perf stat -e cache-misses,page-faults -p $PID -- sleep 10
free -h && vmstat 1 5

实战中典型结论:火焰图最宽栈是某个序列化函数 → 改代码优化;strace 显示 connect 高耗时 → 排查下游/网络;vmstatsi/so 不为 0 → 加内存或查泄漏。

七、避坑与最佳实践

性能开销红线

  • strace 通过 ptrace 显著拖慢目标进程 ,生产环境慎用、避免长时间 attach;统计优先用 -c 而非逐条跟踪,拿完数据立刻退出。
  • perf 采样频率并非越高越好-F 99 是惯例;过高会放大自身开销、干扰结果,甚至在高频率下影响业务吞吐。

火焰图读图陷阱

on-CPU 火焰图只能看"在 CPU 上"的时间 。如果进程实际卡在 IO 或锁(off-CPU 等待),它在 CPU 上几乎不占时间,火焰图会"看起来很干净",让你误以为没有瓶颈。这类场景需要 off-CPU 火焰图(基于 perf sched 延迟事件或 eBPF 工具),否则会漏判。

环境约束

bash 复制代码
# 检查 perf 权限是否受限
cat /proc/sys/kernel/perf_event_paranoid
# 权限检查
perf stat -a -- sleep 1

perf_event_paranoid 取值越高限制越严:内核文档定义 -1 无限制,>=0 允许整机+进程(排除原始 tracepoint),>=1 仅进程级,>=2 仅"本进程、用户态"采样(看不到内核态与整机数据)。常见发行版默认 2~4,此时普通用户只能做受限的 per-process 用户态采样,需要内核态或整机数据就得 CAP_PERFMON / CAP_SYS_ADMIN

此外,采样要覆盖真实业务高峰,避免把偶发抖动当成稳定根因。

可复用检查清单

复制代码
方向判定 --> 工具选择 --> 采样时长 --> 读图 --> 交叉验证 --> 结论
   CPU?       perf       覆盖高峰    最宽栈     iostat/     改代码/
   内存?      strace      不盲调     高耗时      vmstat/     调参数/
   IO?                  频率适度     syscall     free       扩容

总结

排查"慢"的核心不是工具多熟,而是先定方向、再选工具、读图定位、交叉验证 这条链路是否走稳:CPU 热点交给 perf + 火焰图,syscall 阻塞交给 strace,内存与 IO 用 perf 事件叠加 free/vmstat/iostat 互相印证。把本文的命令与决策树收进自己的排查剧本,下次告警来了就能少绕弯路。


参考资料

© 2026 | 转载请注明出处

结论:PASS

相关推荐
LuminousCPP1 小时前
Linux系统编程(二):从目录树到命令执行|路径、环境变量、alias 与文件操作入门
linux·经验分享·笔记
第十人i1 小时前
Linux 系统初始化配置清单:新服务器快速部署
linux·服务器
LongRunning1 小时前
【Linux】RK3568-OpenCV+YOLO(七)
linux
j7~1 小时前
【Linux】三十八.C++ 手写 HTTP 服务器:裸 socket + fork 多进程,从 HTTP 报文解析到浏览器打开网页
linux·网络·网络协议·学习·http·网络编程
贾伟康2 小时前
【HarmonyOS 7新能力|041】弱网直播优化工程封装:把接入逻辑放进可维护的分层结构
性能优化·harmonyos·arkts·音视频开发·直播
傲世仙尊2 小时前
设计者的取舍-路径fork的诞生与全局文件队列
linux
逐流人2 小时前
Containerd容器管理实战:从架构原理到nerdctlcrictl工具链
linux·运维·云原生·容器·云计算·containerd
Tairitsu_H2 小时前
[Linux] 编辑器vim、编译器和动静态链接
linux·vim·gcc·g++·动态链接·静态链接
lzx_0022 小时前
Linux指令(一) 简单了解部分指令
linux