性能分析:Linux 常用性能工具
整理 Linux 系统性能分析的常用工具:60 秒检查清单、time、gprof、perf 与火焰图、fio、iperf3,以及各工具对应的分析场景。
性能问题通常表现为 CPU 高、内存不足、磁盘变慢或网络吞吐异常。与其凭感觉猜测,不如用系统提供的工具先采集数据,再定位瓶颈。
本文整理一套从“整体负载”到“单项指标”的工具链:
- 系统级:
uptime、dmesg、vmstat、mpstat、pidstat、iostat、free、sar、top - 程序级:
/usr/bin/time、gprof - 专项测试:
fio(磁盘)、iperf3(网络)
以下命令基于 Linux 环境,部分工具来自 sysstat 软件包:
sudo apt install sysstat iperf3 fio gprofng gcc
一、性能分析思路
性能分析通常遵循“先整体、后局部”的顺序:
- 确认问题表现:是响应慢、吞吐低,还是系统不稳定。
- 采集整体指标:负载、CPU、内存、磁盘、网络。
- 缩小范围:按进程、按 CPU、按磁盘设备逐步定位。
- 深入分析:使用剖析工具或专项测试复现。
Brendan Gregg 在《Systems Performance》中提出的 USE 方法也很常用:
对于每个资源,检查使用率(Utilization)、饱和度(Saturation)和错误(Errors)。
本文的 60 秒检查清单正是这套思路的命令化实现。
二、Linux 60 秒检查清单
下面的命令按顺序执行,可以在 60 秒内快速了解一台机器的健康状态。
2.1 uptime:负载均值
uptime
load average 显示 1、5、15 分钟的平均负载。负载可以简单理解为可运行进程与不可中断进程的平均数量,需要结合 CPU 核数判断:
- 负载持续接近 CPU 核数,说明 CPU 接近饱和。
- 1 分钟负载明显高于 15 分钟,说明负载正在上升。
2.2 dmesg:内核错误与 OOM
dmesg -T | tail
-T 将时间戳转换为可读时间。重点查看:
Out of memory:内存耗尽导致进程被杀。blocked for more than 120 seconds:进程长时间等待 IO。- 硬件错误、驱动异常等内核日志。
2.3 vmstat:系统整体指标
vmstat -SM 1
每秒输出一行,-SM 以 MB 为单位。关键字段:
| 字段 | 含义 |
|---|---|
r |
等待运行的进程数 |
b |
不可中断睡眠的进程数(通常是磁盘 IO) |
swpd |
已使用的交换空间 |
si / so |
每秒从磁盘换入 / 换出到磁盘的内存 |
bi / bo |
块设备每秒接收 / 发送的块数 |
in / cs |
每秒中断数 / 上下文切换数 |
us / sy |
用户态 / 内核态 CPU 时间占比 |
wa |
等待 IO 的 CPU 时间占比 |
st |
虚拟机被宿主机抢占的时间占比 |
如果 si、so 持续非零,说明内存紧张,系统正在频繁交换。
2.4 mpstat:各 CPU 使用情况
mpstat -P ALL 1
查看每个 CPU 的使用率,检查程序是否只跑在一个核上。单个 CPU 饱和而其他 CPU 空闲,说明程序扩展性差,可能是单线程热点或锁竞争。
2.5 pidstat:按进程统计 CPU
pidstat 1
每秒输出各进程的 CPU 占用,找出意外的 CPU 消费者。也可以按进程名过滤:
pidstat -p $(pgrep -f app_name | head -1) 1
2.6 iostat:磁盘 IO
iostat -sxz 1
-s 省略设备名重复显示,-x 显示扩展信息,-z 忽略空闲设备。关键字段:
| 字段 | 含义 |
|---|---|
tps |
每秒传输次数(IOPS) |
rqm/s |
每秒合并的请求数 |
await |
IO 请求平均等待时间(毫秒) |
%util |
设备忙占比,接近 100% 表示设备接近饱和 |
注意 %util 高不等于设备慢,还要结合 await 判断单次请求耗时。
2.7 free:内存
free -m
Linux 会把空闲内存用于页缓存,因此 free 列很小不代表内存不足。更值得关注的是:
available:可用于新程序的内存。- swap 使用量:持续增长说明内存压力大。
2.8 sar:网络与 TCP
sar -n DEV 1
sar -n TCP,ETCP 1
-n DEV 显示各网卡的收发字节与数据包速率,-n TCP,ETCP 显示 TCP 连接状态和错误:
retrans重传次数持续增长,说明网络存在丢包或拥塞。active/s、passive/s是每秒主动 / 被动建立的连接数。
2.9 top:总览
top
top 整合了进程 CPU、内存、负载和运行队列,适合交互式查看。批处理模式下可以单次采样:
top -bn1 | head -30
三、程序耗时与资源统计:time
/usr/bin/time 可以统计程序运行时间、内存和系统资源。注意 shell 内置的 time 输出格式有限,这里使用外部程序版本:
/usr/bin/time -v ./my_program
输出示例与关键字段:
User time (seconds): 0.00 # 用户态耗时
System time (seconds): 0.00 # 内核态耗时
Percent of CPU this job got: 18% # CPU 占比
Elapsed (wall clock) time: 0:00.04 # 实际经过时间
Maximum resident set size (kbytes): 920 # 峰值内存(RSS)
Major (requiring I/O) page faults: 0 # 主缺页:需要从磁盘读回
Minor (reclaiming a frame) page faults: 139 # 次缺页:可复用内存页
Voluntary context switches: 292 # 自愿上下文切换
Involuntary context switches: 0 # 非自愿上下文切换
- User time 远大于 System time:CPU 密集,优化方向是算法和缓存。
- System time 占比高:大量系统调用或内核工作。
- Major page faults 多:内存访问模式差或内存不足。
- RSS 用于判断程序峰值内存需求。
四、CPU 剖析:gprof
gprof 通过编译期插桩统计函数调用次数和耗时,适合开发阶段定位热点函数。
g++ -pg -O0 -g -o my_program my_program.cpp
./my_program
gprof my_program gmon.out > analysis.txt
要点:
- 必须使用
-pg重新编译,并在函数入口插入计数代码。 - 运行结束后生成
gmon.out,用gprof生成报告。 - 优化级别会影响统计结果,建议先用
-O0分析。 gprof只能统计被插桩覆盖的函数,动态加载的库无法完整统计。
新版本的 GCC 还提供 gprofng 作为替代方案:
gprofng collect app
gprofng display text app.er.1
五、采样剖析:perf 与火焰图
gprof 需要重新编译,不适合分析线上程序。perf 是 Linux 内核自带的采样型剖析工具,通过周期采样记录调用栈,不需要修改程序,生产环境也可使用。
5.1 安装与权限
sudo apt install linux-tools-common linux-tools-generic
# 或按内核版本安装
sudo apt install linux-tools-$(uname -r)
采样依赖性能事件,权限由内核参数 perf_event_paranoid 控制:
cat /proc/sys/kernel/perf_event_paranoid
值为 2 时只能采样用户态事件,需要内核事件时可以临时放宽:
sudo sysctl kernel.perf_event_paranoid=1
5.2 采集 CPU 热点
# 以 99Hz 采样全部 CPU 10 秒
perf record -g -a -F 99 sleep 10
# 采样指定进程
perf record -g -p <pid> sleep 10
# 查看报告
perf report
常用参数:
-g:记录调用栈。-F 99:采样频率,99Hz 是常用值。-a:采集所有 CPU。-p <pid>:只采集指定进程。
perf top 可以实时查看热点函数:
perf top
5.3 生成火焰图
火焰图(Flame Graph)由 Brendan Gregg 提出,把采样到的调用栈按堆叠方式可视化。x 轴方向越宽说明该函数占用 CPU 时间越多,y 轴是调用栈深度。
使用 FlameGraph 工具生成 SVG:
git clone https://github.com/brendangregg/FlameGraph
cd FlameGraph
perf script > out.perf
./stackcollapse-perf.pl out.perf > out.folded
./flamegraph.pl out.folded > flamegraph.svg
也可以一条命令完成:
perf script -i perf.data | ./stackcollapse-perf.pl | ./flamegraph.pl > flamegraph.svg
用浏览器打开 flamegraph.svg,宽大的顶部方块通常就是优化重点。
5.4 perf 与 gprof 对比
| 特性 | perf | gprof |
|---|---|---|
| 原理 | 采样 | 编译插桩 |
| 是否重新编译 | 不需要 | 必须加 -pg |
| 适合环境 | 生产环境 | 开发环境 |
| 运行时开销 | 较低 | 较高 |
| 函数调用次数 | 不统计 | 统计 |
5.5 其他常用功能
# 统计系统调用次数
perf stat -e syscalls:sys_enter_* -a sleep 5
# 采集缓存缺失热点
perf record -a -g -e cache-misses sleep 10
# 查看可用事件列表
perf list
perf stat 适合对单次运行做整体计数,perf record 适合定位具体热点。
六、磁盘 IO 基准:fio
fio 用于生成可控的磁盘 IO 负载,测试磁盘的 IOPS 和吞吐。
5.1 顺序写
fio --name=seq_write \
--filename=/data/fio/testfile \
--size=100G --rw=write --bs=1m \
--direct=1 --ioengine=libaio \
--numjobs=4 --runtime=600 --group_reporting
5.2 顺序读
fio --name=seq_read \
--filename=/data/fio/testfile \
--rw=read --bs=1m \
--direct=1 --ioengine=libaio \
--numjobs=4 --runtime=600 --group_reporting
参数含义:
| 参数 | 含义 |
|---|---|
--rw |
IO 模式:read、write、randread、randwrite 等 |
--bs |
单次 IO 块大小 |
--direct=1 |
绕过页缓存直接读写磁盘 |
--ioengine |
IO 引擎,如 libaio、sync |
--numjobs |
并发作业数 |
--runtime |
测试时长(秒) |
--group_reporting |
汇总所有作业的结果 |
测试大文件时注意磁盘剩余空间,--size=100G 会实际分配文件。
七、网络吞吐:iperf3
iperf3 用于测量两台主机之间的 TCP/UDP 带宽。
6.1 服务端
sudo apt install iperf3 -y
iperf3 -s
默认监听 5201 端口,也可以指定端口:
iperf3 -s -p 5202
6.2 客户端
iperf3 -c x.x.x.x -p 5201 -t 60 -i 5
-c:服务端 IP。-p:服务端端口,默认 5201。-t:测试时长(秒)。-i:结果打印间隔(秒)。
测试结果中重点看 Bitrate 和 Retr(重传次数)。带宽远低于网卡上限或重传较多时,需要继续检查瓶颈是 CPU、驱动还是物理链路。
八、工具选择总结
| 场景 | 工具 | 关注点 |
|---|---|---|
| 快速检查整机健康 | 60 秒清单 | 负载、CPU、内存、磁盘、网络 |
| 程序耗时与峰值内存 | /usr/bin/time -v |
User/System time、RSS、页错误 |
| 定位函数热点(开发期) | gprof / gprofng |
函数耗时占比、调用次数 |
| 定位函数热点(免编译) | perf / 火焰图 |
调用栈采样、热点函数 |
| 磁盘性能测试 | fio |
IOPS、吞吐、延迟 |
| 网络带宽测试 | iperf3 |
Bitrate、重传 |
| 制造压力负载 | stress-ng |
CPU、内存、磁盘、网络压测 |
九、总结
- 先用 60 秒检查清单快速定位瓶颈方向,再按进程、磁盘、网络逐层深入。
- 分析内存时注意区分页缓存和可用内存,swap 持续增长才是内存压力信号。
/usr/bin/time -v可以快速获取程序的耗时、RSS 和页错误。gprof适合开发阶段的函数热点分析,生产环境优先考虑采样型工具。perf无需重新编译即可采样调用栈,配合火焰图可以直观定位 CPU 热点。fio和iperf3用于可控的磁盘、网络基准测试,结果要结合硬件规格解读。- 压测和监控配合使用:先用
stress-ng制造负载,再用本文工具观察系统表现。
相关脚本
整理自 cyy-tag/bash-example 仓库: