Linux IO 监测:iotop 工业边缘排障实战

工业边缘设备出现卡顿时,I/O 往往是重要线索:日志刷盘、离线数据补传、数据库 compaction、容器镜像构建、备份任务,都可能让 eMMC、SD 卡或 SSD 出现突发压力。iotop 能回答"哪个进程或线程正在产生块设备 I/O",但它不能单独回答"设备是否饱和、延迟来自哪里、文件是哪一个、容器是否被限流"。

本文按工程排查顺序整理 iotop 的使用方法、字段口径、I/O 优先级边界,以及与 iostat、pidstat、blktrace、cgroup 的配合方式。

一、先建立排查框架

层次 关注问题 常用工具 能回答
块设备层 吞吐、延迟、队列、繁忙程度 iostat /dev/sda、/dev/nvme0n1 是否异常
进程 / 线程层 谁在读写在做什么 iotop、pidstat 哪个进程或线程贡献主要 I/O
系统与文件层 缓存、脏页、容量、文件句柄 df、du、/proc/meminfo、lsof 是否为空间、回写或已删除文件未释放
块层细节 排队、调度、设备完成耗时 blktrace、blkparse、btt 延迟主要花在排队还是设备完成
容器层 容器级 I/O 与限流 cgroup io.stat / io.max 容器用量是否异常或被限制

不要把 iotop 当唯一入口。一个常见误判是:iotop 中没有明显业务进程,但 iostat 显示设备很忙,这时要考虑脏页回写线程、文件系统日志线程、备份任务、虚拟设备聚合,或容器 PID 命名空间带来的视角偏差。

二、iotop 的运行前提

iotop 依赖 Linux taskstats / task I/O accounting 能力。部分发行版的较新内核默认没有开启 delay accounting,会导致 SWAPIN、IO 等字段不可用或不准确:

bash 复制代码
# 查看当前设置
sysctl kernel.task_delayacct

# 临时开启,重启后失效
sudo sysctl kernel.task_delayacct=1

若要长期开启,应通过发行版支持的内核参数或 sysctl 配置管理,并先在测试环境验证。iotop 通常需要 root 权限;通过 capability 授权时要遵循最小权限原则,不要给普通服务进程随意放大能力。

三、常用命令

bash 复制代码
# 交互模式:只显示有 I/O 的任务,并按进程聚合线程
sudo iotop -o -P -d 2

# 批处理模式:每 2 秒采样一次,共 10 次,输出时间和 KB
sudo iotop -b -o -P -t -k -n 10 -d 2 | tee iotop.log

# 累计 I/O
sudo iotop -a -o -P -d 2

# 指定用户
sudo iotop -u myapp -P -d 2

# 指定一个 PID
sudo iotop -p 12345 -P -d 2

# 指定多个 PID
sudo iotop -p 12345 -p 12346 -d 2

参数口径:

参数 含义 工程注意点
-o 只显示当前有 I/O 的任务 排障时通常先加
-P 按进程聚合线程 查线程热点时去掉 -P
-a 显示累计 I/O 是任务自身累计值,不是当前采样周期内的速率
-b 批处理模式 适合采集落盘和离线分析
-d 采样间隔 默认 1 秒,突发负载建议观察多个周期
-n 采样次数 与 -b 配合使用
-t 输出时间戳 便于和应用日志、iostat 对齐
-k 使用 KB 单位 输出更适合脚本和人工比对
-p 指定 PID 可重复指定
-u 指定用户 常用于按服务账号隔离

特别注意:iotop -a 展示的是任务累计 I/O,不是当前速率。判断瞬时压力应看默认速率模式;判断长时间任务贡献量时,累计模式才有意义。

四、字段怎么读

字段 含义 工程判断
PID 进程或线程 ID 默认可能显示线程,-P 才按进程聚合
PRIO I/O 调度优先级 来自 I/O scheduler / ionice,不是 cgroup 带宽限制
USER 任务所属用户 可用于快速识别服务账号
DISK READ 任务读取速率或累计读取量 受采样口径和内核统计能力影响
DISK WRITE 任务写入速率或累计写入量 写入可能被 page cache 延后
SWAPIN 任务换入等待比例 明显偏高时先排查内存压力
IO 任务等待 I/O 的时间比例 与设备延迟和队列一起看
COMMAND 命令行 需要继续确认文件路径和业务语义

iotop 的进程读写量是任务与块设备子系统之间的统计,不等于应用调用 write() 的字节数。应用写入可能先进入 page cache,真正的磁盘回写会延后发生;请求合并、日志文件系统、RAID、overlay 和远程存储也会改变最终落盘行为。

如果只看到 kworker、jbd2、数据库后台进程或容器运行时活跃,不要直接下结论,应继续查脏页、文件系统日志、备份任务、镜像构建和 cgroup。

五、I/O 优先级:ionice 不是带宽限制

ionice 用于设置 I/O 调度类别或优先级,不能提供确定性带宽上限,也不能替代 cgroup I/O controller。其效果还取决于内核和设备使用的 I/O scheduler;一些 NVMe 场景使用 none 或 mq-deadline,ionice 的可感知效果可能有限。

bash 复制代码
# 查看已有进程的 I/O 优先级
ionice -p 12345

# 将运行中的任务改为 best-effort 7,对其他任务更友好
sudo ionice -c 2 -n 7 -p 12345

# 将运行中的任务改为 idle,仅在没有其他 I/O 时服务
sudo ionice -c 3 -p 12345

# 以 best-effort 7 启动任务
ionice -c 2 -n 7 ./myapp
类别 名称 含义 使用建议
1 realtime 尽量优先处理 谨慎使用,可能让其他任务长期等待
2 best-effort 默认类别,带 0 到 7 级优先级 常用于降级备份、同步和批处理
3 idle 空闲时处理 适合旁路任务,但依赖调度器支持

如果需要明确的读写带宽或 IOPS 限制,应使用 cgroup v2 的 io.max、存储 QoS,或从应用层降低批量大小和并发。

六、完整排障流程

1. 确认设备层是否异常

bash 复制代码
iostat -xz 2 5

第一份报告通常是系统启动以来的平均值,后续报告才是近实时采样。重点记录:

  • r/s、w/s:每秒读 / 写请求数;
  • rkB/s、wkB/s:每秒读 / 写数据量;
  • await、r_await、w_await:平均 I/O 等待与服务耗时;
  • aqu-sz:平均队列长度;
  • %util:设备有 I/O 请求的时间比例。

%util 对并行 SSD / NVMe 不能单独作为饱和结论;它接近 100% 不一定代表设备耗尽,反过来低 %util 也不排除个别请求延迟很高。判断饱和要结合队列长度、延迟、吞吐、IOPS、设备类型和应用响应时间。

2. 用 iotop 定位主要任务

bash 复制代码
sudo iotop -b -o -P -t -k -n 5 -d 2 | tee iotop.log

如果业务进程不明显,但系统写入量高,继续观察:

bash 复制代码
watch -n 1 'grep -E "^(Dirty|Writeback):" /proc/meminfo'

Dirty 持续抬升后出现写爆发,通常说明脏页回写或批量任务造成周期性抖动。此时要结合 vm.dirty_ratio、vm.dirty_background_ratio、应用批量写入策略和设备写入能力评估。

3. 找文件与容量证据

bash 复制代码
df -hT
du -xhd 1 /var | sort -h
find /var/log -xdev -type f -printf '%s %p\n' | sort -nr | head -n 20
journalctl --disk-usage
sudo lsof +L1 | head -n 30

du 加 -x 可以避免跨文件系统统计。lsof +L1 用于发现已被删除但仍被进程打开的文件:这类文件在 df 中仍占空间,但按目录执行 du 时可能找不到。

4. 容器场景先看 cgroup

容器主进程 PID 不等于容器内所有进程和线程。先确认进程树:

bash 复制代码
CONTAINER_PID=$(docker inspect --format '{{.State.Pid}}' mycontainer)
pstree -ap "$CONTAINER_PID"

cgroup v2 下,更稳妥的是直接查看容器 cgroup 的 I/O 统计和限制:

bash 复制代码
CID=$(docker inspect --format '{{.Id}}' mycontainer)
CGROUP=$(find /sys/fs/cgroup -type d -name "docker-${CID}.scope" -print -quit)

cat "$CGROUP/io.stat"
cat "$CGROUP/io.max"

io.stat 常见字段包括 rbytes、wbytes、rios、wios,具体依赖内核版本;io.max 可以确认是否配置了读写带宽或 IOPS 限制。Kubernetes、containerd、podman 的 cgroup 路径不同,但思路一致:先定位 cgroup,再看统计与限制。

七、配合工具怎么用

pidstat:更适合长期采集

iotop 适合现场交互定位,pidstat 更适合周期采集和趋势分析:

bash 复制代码
# 每 2 秒输出一次,共 5 次
pidstat -d 2 5

# 指定进程
pidstat -d -p 12345 2 5

# 线程级统计
pidstat -d -t 2

不建议用 cron 长期执行 iotop 并把交互输出写入日志:输出体积大、字段格式不适合解析,也容易忽略多核和多进程上下文。长期监控应优先使用 pidstat、node_exporter、Prometheus 或运行时自身指标。

blktrace:下探块层延迟

当 iostat 只能说明设备慢,但不知道慢在排队还是设备完成阶段时,使用 blktrace:

bash 复制代码
# 限定设备并采集 30 秒
sudo blktrace -d /dev/sda -w 30 -o edge-trace

# 合并各 CPU 的原始 trace
sudo blkparse -i edge-trace -d edge-trace.bin -q

# 分析块层阶段耗时
sudo btt -i edge-trace.bin

常见阶段:

阶段 含义 异常时看什么
Q2I 请求进入块层到插入队列 请求合并、分配、调度器行为
I2D 插入队列到下发驱动 队列堆积、调度策略、cgroup 限制
D2C 下发设备到设备完成 设备响应、固件、底层虚拟化或远程存储
Q2C 块层总耗时 与 await 和应用延迟互相印证

blktrace 自身有开销,产线设备上要限定时间窗口和设备,采集前后同步保存 iostat、pidstat 和应用指标。

八、几个工程实践

实践 1:统一时间窗口

同一问题要保存同一时间段的 iostat、iotop、pidstat、应用日志和业务指标。只有时间戳对齐,才能区分"谁先触发"和"谁被拖慢"。

实践 2:先建立设备基线

机械盘、SATA SSD、NVMe、eMMC、SD 卡和 NFS 对同一延迟值的承受能力完全不同。没有正常班次和工况基线,就无法判断 20ms 或 50ms 是异常还是设备特性。

实践 3:区分速率、累计值和等待比例

速率回答当前压力,累计值回答任务贡献,IO / iodelay 回答任务等待。三者混用会导致误判。

实践 4:关注小块随机写

eMMC / SD 卡的寿命和写放大与擦除块相关。长期 4KB 以下碎片写、频繁 fsync、数据库小事务和日志刷盘,可能比短时间大文件顺序写更危险。

实践 5:把结论落成监控项

至少监控设备读写速率、IOPS、延迟、队列、进程读写、脏页、磁盘水位、设备错误、容器 I/O 和日志增长率。告警不要只设绝对值,应结合基线偏离、持续时间和业务影响。

九、常见的坑

坑 1:把 iotop 当唯一工具

iotop 只做进程 / 线程归因,不知道文件路径、业务语义和完整设备路径。必须与 iostat、pidstat、cgroup、文件系统和应用指标配合。

坑 2:误解 iotop -a

-a 是累计 I/O,不是实时速率,也不是采样周期内的增量。突发判断应使用默认速率模式。

坑 3:把 %util 当饱和唯一指标

现代 SSD / NVMe 可以并行处理请求。%util 只能作为辅助,应结合 await、aqu-sz、吞吐、IOPS 和应用延迟。

坑 4:以为 ionice 能限带宽

ionice 只设置调度类别和优先级,效果依赖 I/O scheduler;确定性限制应使用 cgroup v2 io.max 或存储 QoS。

坑 5:忽略 page cache 延后写入

应用 write() 返回快,不代表数据已经落盘。脏页回写可能在稍后出现,并被归属到内核回写线程或后台任务。

坑 6:容器内只看主 PID

主 PID 会遗漏子进程和线程。容器场景应结合进程树与 cgroup I/O 统计。

坑 7:长期用 cron 跑 iotop

iotop 输出偏交互且不利于结构化解析。长期采集优先使用 pidstat 或监控系统导出的指标。

十、TL;DR

iotop 负责回答"谁在做 I/O",iostat 回答设备层发生了什么,pidstat 更适合长期趋势,blktrace / btt 拆解排队与设备完成耗时,cgroup 用于容器归因和限制验证。工业边缘场景还要额外关注 eMMC 寿命、小块随机写、脏页回写、日志容量、远程存储映射和设备健康。不要把 iotop -a 当速率,不要把 %util 当唯一饱和指标,也不要把 ionice 当带宽限制。

如果要把分散的 I/O 证据沉淀为统一的边缘运行时观测能力,可以把 Zenova EdgeOS 的观测与告警设计作为协议运行时选型参考;具体阈值仍需结合实际设备、工况和数据链路验证。

相关推荐
Fcy6481 小时前
Linux下 传输层协议UDP详解(含底层源码解析)
linux·运维·udp
青梅味猪大肠2 小时前
【操作系统-24】双标志先检查
linux·运维·服务器
xiaoye-duck2 小时前
《Linux 网络编程》深入理解五种 IO 模型:从 IO 本质到非阻塞 IO 实战
linux·网络
程序员-Benothing2 小时前
Linux内核与模块管理:uname、lsmod、modprobe、sysct
linux·运维·数据库
嵌入式小能手2 小时前
飞凌嵌入式ElfBoard-Shell编程应用实例-使用WiFi拨号上网
linux·服务器·网络
..Dauntless..2 小时前
【Linux】环境变量
linux·运维·服务器
FED_AF2 小时前
Linux运维“邪修”功法之正则表达式
linux·运维·正则表达式
xiaoye-duck3 小时前
《Linux 网络编程·加餐》详解 listen 的 backlog 与 TCP 全连接队列
linux·tcp
Vcaker3 小时前
Linux学习33-HPA动态扩缩容及k8s调度策略
linux·运维·学习