机器在干什么:top / vmstat / iostat / strace 的实战分工
这是 Linux 系列的第 21 篇,进入性能与调试章节。前面教你怎么"用"Linux——这一篇讲怎么"看穿"它的状态。
0. 别再"先 top 看一眼"了
很多人遇到"机器慢"第一反应是 top —— 然后看半天不知道找什么。
性能工程师 Brendan Gregg 提出过一个简单的方法叫 USE(Utilization / Saturation / Errors):
对每一类资源,问 3 个问题:
- 它用了多少?(utilization)
- 它排队 / 等待了吗?(saturation)
- 它有错吗?(errors)
四大资源:
| 资源 | utilization | saturation | errors |
|---|---|---|---|
| CPU | %us + %sy | runq(等运行的进程数) | thermal throttle |
| 内存 | used | swap 用量 / page in-out | OOM killed |
| 磁盘 | %util | await(队列) | I/O errors |
| 网络 | 带宽 % | TCP retrans / drops | NIC errors |
下面按资源讲怎么看。
1. CPU:用了多少 + 排队没
top / htop —— 综合视图
$ top
top - 14:22:33 up 5 days, load average: 2.34, 1.85, 1.42
Tasks: 234 total, 2 running, 232 sleeping
%Cpu(s): 35.2 us, 4.1 sy, 0.0 ni, 58.3 id, 2.1 wa, 0.0 hi, 0.3 si
MiB Mem : 2007.4 total, 102.1 free, 1234.5 used, 670.8 buff/cache
读 %Cpu(s) 那行(按 1 显示每核):
| 字段 | 含义 |
|---|---|
| us (user) | 用户态进程吃 CPU |
| sy (system) | 内核态(syscall / 中断处理)吃 CPU |
| ni (nice) | 低优先级进程 |
| id (idle) | 空闲 |
| wa (iowait) | CPU 在等磁盘(idle 但不算 idle) |
| hi | 硬中断 |
| si | 软中断 |
| st (steal) | 虚拟机被宿主机偷走的时间 |
关键诊断点:
us高 +sy低 → 业务进程算东西多,是 CPU bound 工作负载us低 +sy高 → 大量 syscall(fork / context switch / 锁竞争),可能写法有问题wa高 → 磁盘瓶颈(CPU 在等 I/O 不能动)st高 → 云上 CPU 被超卖
Load average:等运行的进程数
load average: 2.34, 1.85, 1.42
1min 5min 15min
load = 在 CPU 上跑 + 在 ready queue 等 CPU + 在 D 状态等 I/O 的进程数(1/5/15 分钟平均)。
经验法则:
- load < CPU 核数 → 资源充裕
- load ≈ 核数 → 满载(接近最佳吞吐)
- load > 核数 × 2 → 在排队,要扩
细节:Linux 的 load 跟其他 Unix 不同,包含 D 状态进程(uninterruptible I/O 等待)。所以 load 高有时是磁盘卡了不是 CPU 忙。
# 看 1 分钟 load
$ cat /proc/loadavg
2.34 1.85 1.42 2/234 12345
↑ ↑ ↑ ↑ ↑
1m 5m 15m running/total 最近 PID
# uptime 也显示
$ uptime
14:22:33 up 5 days, load average: 2.34, 1.85, 1.42
看每个进程的 CPU
$ ps -eo pid,user,%cpu,cmd --sort=-%cpu | head
# 或 htop 交互式
$ htop
看 CPU 干啥用的 syscall(深入)
# 单进程:什么 syscall 最多
$ sudo strace -c -p <pid>
(让它跑一会儿 Ctrl+C)
% time seconds usecs/call calls syscall
89.45 1.234567 5000 247 epoll_wait
4.20 0.058091 12 4841 read
...
epoll_wait 占 89% → 程序大部分时间在等事件(正常的 server)。
read / write 占大头 → I/O 密集。
某个奇怪 syscall 占大头 → 可能是 bug。
2. 内存:物理 + swap + cache
free -h
$ free -h
total used free shared buff/cache available
Mem: 2.0Gi 1.2Gi 102Mi 16Mi 670Mi 700Mi
Swap: 1.0Gi 0B 1.0Gi
新手最容易看错:used 看起来很高 → 但 Linux 故意把"剩下的"全用作 disk cache(buff/cache),需要时立刻让出来。
真正能用的内存看 available——不是 free。
total总物理内存used进程 + 内核 + 不可回收的(buffers)free真的空闲(很少有)buff/cache文件 cache + 元数据 cache(可回收)available≈free+buff/cache中可回收的部分 → 这才是"差不多能给新进程用的"Swap used→ 已经在 swap 的页数;> 0 不一定坏,但持续涨 = 内存压力大
vmstat:实时刷新
$ vmstat 1 # 每 1 秒刷一次
procs ---memory------ -swap- -io-- -system-- ----cpu----
r b swpd free buff cache si so bi bo in cs us sy id wa
1 0 0 18632 10024 580120 0 0 2 8 45 120 8 2 90 0
2 0 0 18504 10024 580120 0 0 0 24 88 211 25 4 71 0
关键列:
| 列 | 含义 |
|---|---|
r |
在运行 / ready 的进程数(> CPU 核数 = 在排队) |
b |
D 状态阻塞进程数(通常等磁盘) |
si / so |
swap in / out KB/s(应该是 0;持续非 0 = 物理内存不够) |
bi / bo |
block in / out(磁盘读写 KB/s) |
in / cs |
每秒中断数 / context switch 数(异常高 = 锁竞争或频繁 fork) |
wa |
iowait |
vmstat 1 看 30 秒 → 你对机器"现在大致在干什么"就有判断。
谁吃了内存
$ ps -eo pid,user,%mem,rss,cmd --sort=-rss | head
PID USER %MEM RSS CMD
12345 me 23.4 478752 python big_data_job.py
891 www-data 3.4 68000 nginx: worker
...
# 加权重看:RSS 是物理常驻内存
$ smem -tk # 比 ps 更准(区分 USS / PSS / RSS),要装
RSS 是该进程实际占的物理内存(包含共享库占的部分会重复算)。PSS / USS 更精确,看每个进程"独占"了多少。
OOM Kill 留下的痕迹
机器内存撑爆,内核会挑一个进程 SIGKILL:
$ dmesg -T | grep -i 'killed process'
[Tue May 25 14:22:33] Out of memory: Killed process 12345 (java) total-vm:8GB...
$ journalctl -k | grep -i 'oom'
或者更现代:
$ journalctl --since today | grep -i oom-kill
OOM 来过 → 你机器内存严重不足,要加 / 减负 / 加 swap。
3. 磁盘 I/O:用了多少 + 排队没
iostat:磁盘吞吐 + 延迟
$ iostat -xz 1 # -x 详细 -z 跳过 0 利用率的
Device r/s w/s rkB/s wkB/s await %util
sda 12.0 34.0 480 1360 2.3 5.2
nvme0n1 3.0 124.0 200 8120 0.8 92.1
↑ ↑
延迟 利用率
关键列:
%util:磁盘有多少时间在忙 —— 持续 > 80% = 磁盘瓶颈await:每个 I/O 平均等待时间(ms)—— HDD > 50ms / SSD > 10ms = 不健康r/s w/s:每秒读 / 写次数(IOPS)rkB/s wkB/s:每秒读 / 写 KB
iotop:哪个进程在 I/O
$ sudo iotop -oP # 只看有 I/O 的进程
Total DISK READ : 0.00 B/s | Total DISK WRITE : 24.50 M/s
PID PRIO USER DISK READ DISK WRITE COMMAND
12345 be/4 pg 0.00 B/s 18.5 M/s postgres: walwriter
...
跟 top 类似,但按 I/O 排序。
磁盘队列深度长度
$ iostat -x 1 | awk 'NR>3{print $1, $9}' # avgqu-sz
sda 0.12 # 平均队列长度
nvme0n1 4.5 # ← 排队 4.5 个,磁盘吃力
队列长 = 应用发起 I/O 比磁盘处理快。
4. 网络:流量 + 包率 + 错误
ifstat / sar -n DEV
$ sar -n DEV 1
Linux ...
14:22:33 IFACE rxpck/s txpck/s rxkB/s txkB/s rxerr/s
14:22:34 eth0 1234.0 2345.0 2048.0 8192.0 0.00
14:22:34 lo 12.0 12.0 1.5 1.5 0.00
rxkB/stxkB/s:流量rxpck/stxpck/s:包率(很高时 CPU 处理中断会成为瓶颈)rxerr/stxerr/s:应该是 0——非 0 表示物理层或驱动问题
ss -s:socket 统计
$ ss -s
Total: 245 (kernel 0)
TCP: 189 (estab 87, closed 90, orphaned 0, synrecv 0, timewait 89/0)
Transport Total IP IPv6
RAW 0 0 0
UDP 12 8 4
TCP 99 95 4
INET 111 103 8
timewait 89多 = 频繁的短连接estab高 = 长连接多
TCP retransmission(异常重传)
$ ss -s -i # 加 -i 看每个连接的 RTT / cwnd
$ ss -tinp 'state established' | head
# 全局统计
$ netstat -s | grep -iE 'retrans|drop|reject'
1234 segments retransmitted
56 packets pruned from out-of-order queue
23 retransmits in slow start
retrans 占总 segments > 1% 就有网络问题。
当前最忙的连接
$ ss -tn -o state established '( dport = :443 or sport = :443 )' | head
5. strace:跟踪单进程的 syscall
# 跟着一个跑着的进程
$ sudo strace -p 12345
# 看哪类 syscall 最多(统计模式)
$ sudo strace -c -p 12345
# 跟随 fork 出去的子进程
$ sudo strace -f -p 12345
# 看 OS 在干什么 I/O(filter 文件相关)
$ sudo strace -e trace=openat,read,write -p 12345
# 看时间戳
$ sudo strace -tt -p 12345
# 写到文件不刷屏
$ sudo strace -o /tmp/strace.log -p 12345
典型用法:
- "应用慢但 CPU/mem 都不高" → strace 看它卡在哪个 syscall
- "应用启动失败" → strace 看 open 哪个文件 ENOENT
- "应用读不出某配置" → strace -e openat 看它去哪找
性能代价:strace 会让进程慢 5-50x(每个 syscall 都被 ptrace 拦截)。生产环境慎用,要看更快的工具用 perf 或 bpftrace。
6. perf:内核级性能采样(高阶)
# 看哪个函数占 CPU 最多(30 秒采样)
$ sudo perf top
# 抓 30 秒 + 生成报告
$ sudo perf record -ag sleep 30
$ sudo perf report
# 火焰图(要装 FlameGraph 工具)
$ sudo perf script | flamegraph.pl > flame.svg
perf 是终极 CPU 调试器,但学习曲线陡。先掌握上面 5 个工具 + 偶尔翻 perf。
7. bpftrace / eBPF 一句话扫盲
eBPF 是 Linux 4.x+ 的"内核内安全脚本引擎"——可以在不重启的情况下,往内核插小程序观察一切:
# 看哪些进程在调 open()
$ sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s\n", comm); }'
# 看哪些进程在 fork
$ sudo bpftrace -e 'tracepoint:syscalls:sys_enter_clone { printf("%s\n", comm); }'
bpftrace 命令的开销几乎为 0,生产环境可用,是 strace 的现代替代。
太大的话题,留给以后专门讲。这里只让你知道有这条路。
8. 一份"5 分钟诊断清单"
机器慢的时候,按这个顺序敲:
# 1. uptime + load + 进程数(30 秒判断"忙不忙")
$ uptime
$ ps aux | wc -l
# 2. CPU 角度
$ top -bn1 | head -15
# 或更全
$ vmstat 1 3
# 3. 内存
$ free -h
$ dmesg -T | grep -i kill | tail
# 4. 磁盘
$ iostat -xz 1 3
$ df -h
$ df -i
# 5. 网络
$ ss -s
$ ip -s link
# 6. 谁占了 CPU / 内存
$ ps -eo pid,user,%cpu,%mem,rss,cmd --sort=-%cpu | head
$ ps -eo pid,user,%cpu,%mem,rss,cmd --sort=-rss | head
# 7. 看现在 I/O 在等的进程
$ ps -eo state,pid,cmd | awk '$1 ~ /D/'
# 8. 最近的内核错误
$ dmesg -T | tail -30
5 分钟内你应该能定位到瓶颈在哪个维度(CPU / mem / disk / net)。
9. 工具速查总图
┌─ CPU ────────────────────────────────────────┐
│ 概览:top / htop │
│ 长时间趋势:sar -u │
│ 按进程:ps --sort=-%cpu / top -p │
│ syscall:strace / perf / bpftrace │
│ 上下文切换:vmstat 看 cs │
└──────────────────────────────────────────────┘
┌─ 内存 ────────────────────────────────────────┐
│ 概览:free -h │
│ 趋势:sar -r │
│ 按进程:ps --sort=-rss / smem │
│ OOM:dmesg / journalctl -k │
│ swap:vmstat 看 si/so │
└──────────────────────────────────────────────┘
┌─ 磁盘 ────────────────────────────────────────┐
│ IOPS / 延迟:iostat -xz 1 │
│ 按进程:iotop │
│ 空间:df -h / df -i / du -sh │
│ 错误:dmesg / smartctl │
└──────────────────────────────────────────────┘
┌─ 网络 ────────────────────────────────────────┐
│ 流量:sar -n DEV / ifstat │
│ socket:ss -s / ss -tlnp │
│ 错误:netstat -s / ip -s link │
│ 抓包:tcpdump / wireshark │
└──────────────────────────────────────────────┘
10. 现在做一件事
# 1. 装 htop(如果没装)
$ sudo apt install htop iotop sysstat
# 2. 跑一遍上面"5 分钟清单",看你机器现在什么样
$ uptime
$ vmstat 1 3
$ iostat -xz 1 3
$ free -h
$ ss -s
# 3. 看你机器 5 天来最忙的时段(要装 sysstat / sa)
$ sar -u | head -20
# CPU
$ sar -r # 内存
$ sar -d # 磁盘
$ sar -n DEV # 网络
# 4. 抓 30 秒 strace 看你 shell 在干什么
$ strace -c -p $$ &
$ sleep 30; kill %1
练熟这套工具,你看任何"机器卡了"都能在几分钟内有答案。
下一篇:logs——
journalctl / syslog / logrotate,日志去哪了、怎么不爆磁盘、出问题怎么 5 秒找到关键 log。
$ ls related/
-
进程管理:ps / top / kill / jobs / nohup 全套基本动作 2026-06-22
Linux 系列第 9 篇。进程是 Linux 一切的基本单位——shell、网页、AI 推理、数据库都跑在进程里。这一篇拆"看进程、杀进程、把进程放后台、让进程脱离终端继续跑"四组日常动作,并把 SIGKILL / SIGTERM 这种信号术语彻底讲清。
-
Linux 防火墙全栈:iptables / nftables / ufw / firewalld 几层 2026-07-02
Linux 系列第 19 篇。新手第一次看到 `iptables -nvL` 输出几百行就懵——而且还有 nftables / ufw / firewalld / Docker 加的规则一锅端。这一篇拆 netfilter 的核心 5 个钩子点、iptables → nftables 的迁移现状、ufw 和 firewalld 各自适用谁、Docker 怎么搅动这一锅。
-
现代 Linux 网络工具集:ip / ss / dig / curl / nc 2026-07-01
Linux 系列第 18 篇。`ifconfig` / `netstat` / `route` / `nslookup` 这套上世纪的工具今天还能用,但已经被 `ip` / `ss` / `dig` 替代。这一篇拆 5 件现代工具的常用语法,并给一份"网络出问题怎么 5 分钟排查到根因"的清单。