iotop —— 进程级 I/O 实时监控
一句话定义
iotop 类似 top 但是显示哪个进程在做磁盘 I/O——按读写量排序。当 iostat 告诉你"磁盘忙"、用 iotop 找"是谁的"。
典型场景
top看到 %iowait 高、找哪个进程在烧 I/O- 节点磁盘满了、找谁在猛写
- 数据库慢、看是不是另一个进程在抢盘
- K8s 节点排错"是 etcd 还是 containerd 还是用户 pod"
装
apt install -y iotop # Ubuntu / Debian
yum install -y iotop # CentOS / RHEL
需要 root 才能完整跑
$ iotop
Could not run iotop as some of the requirements are not met:
- Linux >= 2.6.20 ... [OK]
- ... [OK]
- Running as root [FAIL]
普通用户跑 iotop 报错 / 看不到别人进程的 I/O。必须 root:
sudo iotop
或者给 binary 加 capability(不推荐):
setcap 'cap_net_admin,cap_sys_ptrace=eip' $(which iotop)
1. 基本界面
$ sudo iotop
Total DISK READ: 1.5 M/s | Total DISK WRITE: 8.2 M/s
Current DISK READ: 1.2 M/s | Current DISK WRITE: 5.8 M/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
1234 be/4 mysql 500 K/s 5.0 M/s 0.00 % 45.00 % mysqld
5678 be/4 postgres 300 K/s 2.0 M/s 0.00 % 15.00 % postgres
9012 be/4 root 50 K/s 100 K/s 0.00 % 2.00 % containerd
逐字段:
| 字段 | 含义 |
|---|---|
| Total DISK READ/WRITE | 节点总 I/O 吞吐 |
| Current | 当前刷新周期的 I/O |
| TID | 线程 ID(不是 PID——iotop 显示线程级) |
| PRIO | I/O 调度优先级 |
| USER | 进程所属用户 |
| DISK READ/WRITE | 该线程的 I/O 吞吐 |
| SWAPIN | 该线程因 swap 引起的 I/O 比例 |
| IO> | I/O wait 时间百分比(关键指标) |
| COMMAND | 命令 |
IO> 列 —— 关键指标
IO> = 该线程在 iowait 状态的时间百分比。
IO> 0—— 没等过 I/OIO> 50—— 一半时间在等 I/OIO> 99—— 几乎全在等 I/O = 严重 I/O 瓶颈
比 DISK READ/WRITE 更能反映"这个进程被 I/O 拖累"——量大但 IO> 低 = I/O 很快、不卡;量小但 IO> 高 = I/O 慢、卡得这进程做不了事。
2. 核心 flag
sudo iotop -o # only 只显示有 I/O 的进程
sudo iotop -P # process(线程合并成进程)
sudo iotop -b # batch 模式(脚本用)
sudo iotop -n 5 # 跑 5 次就退
sudo iotop -d 5 # 5 秒刷新(默认 1 秒)
sudo iotop -a # accumulated 累计模式
sudo iotop -k # KB 单位
sudo iotop -u <user> # 只看某用户
sudo iotop -p <PID> # 只看某 PID
-o —— 必加
不加 -o 看到一堆没 I/O 的进程、找有 I/O 的费劲。
sudo iotop -o # 干净的输出
-P —— 进程级(vs 线程级)
sudo iotop # 默认线程级(**TID**)
sudo iotop -P # 进程级(PID)
线程级有时太细——Java 应用上百线程都在做 I/O 看不过来。-P 合并成进程视角。
-a —— 累计
默认 iotop 显示当前采样周期的 I/O。-a 改成"程序启动以来的累计"。
sudo iotop -ao
# 找"总共 I/O 最多的进程"
适合长期观察"谁是 I/O 大户"。
3. 交互按键
iotop 跑着时:
| 键 | 作用 |
|---|---|
q | 退出 |
o | 切换 -o(只显示有 I/O) |
p | 切换 -P(进程级) |
a | 切换累计 / 当前 |
r | 反转排序 |
← / → | 改排序列 |
? / h | 帮助 |
4. Batch 模式(脚本 / 监控)
sudo iotop -bo -n 1 # 跑一次、跳过 0 I/O、退出
适合脚本周期采样:
#!/bin/bash
# 每 30 秒采样一次 top 10 I/O 进程
while true; do
date >> /var/log/iotop.log
sudo iotop -bo -n 1 -P | head -15 >> /var/log/iotop.log
sleep 30
done
或者用更现代的 eBPF 工具(biotop from bcc)。
5. 实战场景
场景 1:top 看 wa 高、找元凶
$ top
%Cpu(s): 3.0 us, 1.0 sy, 0.0 ni, 50.0 id, 45.0 wa, ...
^^^^
I/O wait 45%
$ sudo iotop -o
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
1234 be/4 mysql 0 B/s 50 M/s 0% 80% mysqld -- ...
^^^^^^^^^
元凶
→ MySQL 在猛写。看应用为什么:是慢查询、批量 insert、还是 fsync 风暴。
场景 2:K8s 节点 I/O 飙
$ ssh m4 'sudo iotop -bo -n 3 -P | head -20'
PID PRIO USER DISK READ DISK WRITE IO> COMMAND
5678 be/4 root 20 M/s 500 M/s 30% containerd-shim ...
9012 be/4 root 5 M/s 100 M/s 10% etcd
containerd-shim 500 MB/s 写 → 某 pod 在猛写。
找具体 pod:
$ ssh m4 'cat /proc/5678/cgroup | head -3'
0::/kubepods/burstable/pod-xxx-yyy/abc...
^^^^^^^^^^^^^^^^^^^^
pod uid → 反查 K8s pod
$ kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.uid} {.metadata.namespace}/{.metadata.name}{"\n"}{end}' | grep "^xxx"
default/noisy-app
→ 找到了:default/noisy-app 这个 pod 在猛写。
场景 3:找 deleted 文件还在写
$ sudo iotop -o
TID USER DISK READ DISK WRITE IO> COMMAND
1234 root 0 B/s 30 M/s 50% rsyslogd
$ lsof -p 1234 | grep deleted
rsyslogd 1234 root 8w REG ... /var/log/syslog (deleted)
^^^^^^^^^^^
还在写已删的文件!
rsyslogd 还在写已删的 /var/log/syslog → 重启 rsyslog 释放空间。
场景 4:监控某用户的 I/O
# 看 mysql 用户的 I/O 总量
sudo iotop -ouP -u mysql
或者按 cgroup(K8s 场景):
# 在某个 pod 的 cgroup 下监控
sudo iotop -bo -n 1 | awk '{ ... 按 cgroup 过滤 ... }'
iotop 不直接支持 cgroup 过滤、要手动处理。新版 bcc-tools 的 biotop 更适合容器场景。
6. 替代 / 更现代工具
biotop (bcc / eBPF)
apt install -y bpfcc-tools # Ubuntu
yum install -y bcc-tools # CentOS
biotop 5 # 5 秒刷新
输出和 iotop 类似但基于 eBPF、没 setuid 安全风险、容器感知。
pidstat -d
$ pidstat -d 1
Linux ... (m4) 2026-05-27 _x86_64_ (4 CPU)
14:30:01 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
14:30:02 0 1234 0.00 5000.00 0.00 10 mysqld
14:30:02 27 5678 500.00 2000.00 0.00 5 postgres
pidstat -d 来自 sysstat 包(和 iostat 同包)。iodelay 列类似 IO>。
更轻量、更稳定、适合脚本。
atop
apt install -y atop
atop -d # disk 视图
atop 显示更全(CPU + 内存 + 磁盘 + 网络 + 进程)、按 d 切到磁盘视图。
iotop-c
iotop-c 是 iotop 的 C 重写版(社区)—— 不需要 root capability、更现代。但不普及、用 iotop 原版即可。
7. 反面教材
反面 1:iotop 默认看到的不是进程是线程
$ sudo iotop
TID ... COMMAND
1234 java
1235 java ← 同 java 不同线程
1236 java
1237 java
每个线程一行 → Java 应用上百行看不过来。加 -P 进程级。
反面 2:以为 IO> 100% 一定瓶颈
IO> 100% 单进程意味着"这个进程在用全部 CPU 等 I/O"——可能是单进程吞吐瓶颈。
但节点级可能没问题(其它进程跑得正常)。看 top 的全局 %iowait:
%iowait 5% ← 节点级不忙
但某个 java 进程 IO> 95% ← 单进程在等
= 这个进程对它要做的事来说 I/O 慢、但节点磁盘有富余。可能:
- 这个进程的 I/O 模式不友好(同步小写 / fsync 多)
- 还可以并发更多进程榨干磁盘性能
反面 3:iotop 看不到 buffer cache I/O
$ kubectl exec my-pod -- dd if=/dev/zero of=/data/test bs=1M count=1000
$ sudo iotop -o
# 看不到这个进程!
dd 通过 page cache 写、还没真落盘。iotop 只看到实际块设备 I/O。
要看应用层的 I/O 加 oflag=direct:
dd if=/dev/zero of=/data/test bs=1M count=1000 oflag=direct
# 跳过 page cache、直接写盘
反面 4:用 iotop 监控不要持续跑
iotop 自身有 CPU 开销(轮询 /proc)。生产环境别 24x7 跑——用 sar / Prometheus 长期监控、iotop 只是排错时临时跑。
反面 5:iotop 在容器里完全不工作
$ kubectl exec my-pod -- iotop
# Could not run iotop ...
K8s pod 默认没 CAP_SYS_PTRACE / 看不到节点全局 I/O。
节点上跑而不是 pod 里。要看 pod 视角的 I/O:
- pod cgroup
io.stat:cat /sys/fs/cgroup/io.stat - Prometheus container_fs_*