Linux iotop:工业边缘 I/O 高级分析实战

工业边缘现场的"设备卡顿"经常最后落到 I/O 上:日志同步写爆了 eMMC,时序数据库在做 compaction,容器内应用频繁写小文件,或者脏页回写造成周期性抖动。iotop 是一个很好的入口,但它只回答"哪个进程或线程在做 I/O",不能单独回答"设备是否已经饱和、延迟卡在块层还是硬件、容器是否被 cgroup 限流"。

这篇文章按工程排查顺序整理一套工具链:iostat 看设备层,iotop / pidstat 看进程与线程,blktrace / btt 拆块层延迟,fio 做受控压测,再结合日志、数据库和容器场景定位根因。

一、先建立 I/O 排查框架

推荐按四层收集证据:

层次 关注问题 常用工具 能回答
块设备层 设备吞吐、延迟、队列、繁忙程度 iostat /dev/sda/dev/nvme0n1 是否异常
进程 / 线程层 谁在读写在做什么 iotoppidstat 哪个进程或线程贡献了主要 I/O
系统与文件层 缓存、脏页、容量、文件句柄 dfdu/proc/meminfolsof 是否为空间、回写或删除文件未释放
块层细节 队列、调度、设备完成耗时 blktraceblkparsebtt 延迟主要花在排队还是设备完成

不要只看单个工具。一个常见误判是:iotop 里没有明显业务进程,但 iostat 显示设备很忙,这时要考虑回写线程、镜像构建、备份任务、虚拟设备聚合,或容器 PID 命名空间导致的信息缺失。

二、先留一份现场基线

排障前先确认路径、设备和调度器。同一个目录在根盘、独立 SSD、eMMC 或 NFS 上,结论完全不同。

bash 复制代码
df -hT /var
findmnt -T /var
lsblk -o NAME,TYPE,SIZE,ROTA,MOUNTPOINTS

DEV=$(findmnt -n -o SOURCE -T /var)
echo "I/O device: $DEV"

如果 DEV 是分区,例如 nvme0n1p2,可以先找底层盘,再查看调度器:

bash 复制代码
DISK=$(lsblk -ns -o PKNAME "$DEV" | tail -n 1)
cat "/sys/block/$DISK/queue/scheduler"

再记录缓存和脏页状态:

bash 复制代码
free -h
grep -E '^(Dirty|Writeback):' /proc/meminfo
sysctl vm.dirty_ratio vm.dirty_background_ratio

第一轮数据至少保存 3 到 5 分钟。工业现场负载往往有周期性,只看 10 秒容易把正常批处理误判为故障。

三、iotop:看进程和线程的实际 I/O

1. 运行前提

iotop 依赖 Linux 内核的任务 I/O 统计能力,通常要求内核开启 CONFIG_TASK_DELAY_ACCTCONFIG_TASK_IO_ACCOUNTINGCONFIG_TASKSTATS 等选项。较新的内核中,task_delayacct 可能默认关闭:

bash 复制代码
sudo sysctl kernel.task_delayacct=1

iotop 一般用 root 运行。非 root 场景可以通过 CAP_NET_ADMIN 授权,但要遵循安全基线,不要无条件放大能力。

2. 常用命令

bash 复制代码
# 只显示正在做 I/O 的进程,按进程聚合线程
sudo iotop -o -P -d 2

# 累计模式:显示自进程或 iotop 启动以来的累计量
sudo iotop -a -o -P -d 2

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

# 指定进程或线程
sudo iotop -p 12345 -d 2

# 批处理落盘,便于和 iostat、应用日志按时间对齐
sudo iotop -b -o -P -t -k -n 10 -d 2 | tee iotop.log

参数含义:

参数 含义 注意点
-o 只显示有 I/O 的任务 排障时通常先加
-P 按进程聚合线程 查线程级问题时去掉 -P
-a 显示累计 I/O 不是系统启动以来的总流量
-b 批处理模式 适合采集和归档
-d 采样间隔 默认 1 秒
-n 采样次数 常与 -b 配合
-t 输出时间戳 隐含批处理模式
-k 固定用 KB 显示 便于脚本解析

3. 输出怎么读

iotopDISK READ / DISK WRITE 表示进程或线程与内核块设备子系统之间的 I/O。由于 page cache、请求合并和内核重排的存在,进程侧速率与磁盘硬件瞬时速率不一定相同。Total DISK READ/WRITECurrent DISK READ/WRITE 在同一时刻也可能不同。

因此要看三类信息的组合:

  • 进程读写量:定位主要来源;
  • IOGRAPH 列:任务等待 I/O 的时间比例;
  • SWAPIN:是否存在换入干扰。

iotop 不知道业务语义,也不知道具体文件路径。如果只看到 kworkerjbd2 活跃,还要继续查脏页、文件系统日志、容器、镜像构建和后台任务。

四、iostat:先判断设备层状态

iostat 适合先看设备,再回头找进程:

bash 复制代码
iostat -xz 2 5

第一份报告通常是系统启动以来的平均值,后续报告才是近实时采样。分析时建议跳过第一份,并记录同一时间窗口的 iostatiotop 输出。

关键字段

字段 含义 工程判断
r/sw/s 每秒完成读 / 写请求数 小块高频写对 eMMC 伤害更大
rkB/swkB/s 每秒读 / 写数据量 与业务上传、归档节奏对齐
rrqm/swrqm/s 每秒合并请求数 合并多说明请求相邻或碎片化
areq-sz / 旧版 avgrq-sz 平均请求大小 新旧版本单位可能不同
awaitr_awaitw_await 平均 I/O 等待 + 服务耗时 读延迟和写延迟应分开看
aqu-sz / 旧版 avgqu-sz 平均队列长度 队列高通常说明请求在堆积
%util 设备有 I/O 请求的时间比例 对并行 SSD / NVMe 不能单独下结论

特别注意:svctm 在新版本 sysstat 中已经废弃,不能作为服务时间或容量判断依据。现代设备可以并行处理请求,%util 接近 100% 不一定代表已经达到性能极限;反过来,低 %util 也不排除个别请求延迟很高。

常见模式

现象 可能解释 下一步
await 高、aqu-sz 请求排队,设备或上游路径饱和 查进程、cgroup、调度器和设备健康
await 高、队列低 单次设备完成耗时长 查盘健康、固件、虚拟化底层或远程存储
写入呈周期性突发 脏页回写或批量任务 Dirty/Writeback 和任务计划
小块写为主 日志、数据库 WAL、指标碎片化 查落盘策略、批量大小和 compaction
读高且缓存命中率低 冷数据反复读取 查查询模式、缓存和文件布局

阈值必须来自自己的设备基线。同样的 await=20ms,在机械盘、SATA SSD、NVMe、eMMC 和 NFS 上意义完全不同。

五、pidstat:适合采集进程与线程趋势

pidstat 的 I/O 输出没有 iotop 的交互视图,但更适合定时采集和告警归因:

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

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

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

重点字段:

  • kB_rd/s:任务每秒从磁盘读取的 KiB;
  • kB_wr/s:任务每秒造成或将写入磁盘的 KiB;
  • kB_ccwr/s:被取消写入的 KiB;
  • iodelay:任务等待块设备 I/O 完成的延迟,单位为 clock ticks。

iotop 适合现场交互定位,pidstat 适合长期采集。两者与 iostat 的时间戳必须一致,否则很难区分"谁先触发"和"谁被拖慢"。

六、blktrace:拆块层延迟

iostat 只能告诉你"设备慢",但不知道慢在排队、调度还是设备完成阶段时,可以使用 blktrace

bash 复制代码
# 采集 30 秒;生产环境先评估 CPU 与 trace 文件体积
sudo blktrace -d /dev/sda -w 30 -o edge-trace

# 合并各 CPU 的原始 trace,输出二进制文件
sudo blkparse -i edge-trace -d edge-trace.bin -q

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

不要只拿 edge-trace.blktrace.0 分析单核数据就下结论,多 CPU 系统上完整 trace 会分布在多个文件里。blkparse 负责解析与合并,btt 再输出各阶段延迟统计。

常见阶段可以粗略理解为:

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

blktrace 采样本身有开销,不要在已经接近饱和的产线控制器上长时间盲跑。先限定设备、时间窗口和过滤条件,采集前后同步保存 iostat 与应用指标。

七、fio:做受控基准,不要先压生产盘

fio 用来验证设备在特定块大小、队列深度和读写混合比下的能力。必须使用专用测试目录和可删除文件,不要直接压根分区或生产数据盘。

bash 复制代码
mkdir -p /mnt/io-bench

# 顺序读
fio --name=seq-read \
    --filename=/mnt/io-bench/seq-read \
    --size=2G \
    --bs=1M \
    --rw=read \
    --direct=1 \
    --iodepth=32 \
    --runtime=60 \
    --time_based \
    --group_reporting

# 70% 读 + 30% 写的随机混合负载
fio --name=rand-rw \
    --filename=/mnt/io-bench/rand-rw \
    --size=2G \
    --bs=4k \
    --rw=randrw \
    --rwmixread=70 \
    --direct=1 \
    --iodepth=64 \
    --runtime=60 \
    --time_based \
    --group_reporting

压测时同步采集:

bash 复制代码
iostat -xz 2 60 | tee fio-iostat.log
pidstat -d 2 60 | tee fio-pidstat.log

几个注意点:

  1. --direct=1 绕过 page cache,否则测到的是缓存和回写混合行为;
  2. 测试文件大小要小于可用空间,避免把生产盘写满;
  3. eMMC / SD 卡有写入寿命,随机写压测要控制时长;
  4. 测试结果必须与生产观测值对比,不能直接照搬厂商峰值;
  5. 测试后清理文件:rm /mnt/io-bench/seq-read /mnt/io-bench/rand-rw

八、完整排障流程

第一步:确认设备是否异常

bash 复制代码
iostat -xz 2 5

记录设备名、r/sw/srkB/swkB/sawaitaqu-sz%util。如果所有设备都正常,而应用仍感觉慢,应转向 CPU、锁、网络、数据库等待或调度问题。

第二步:定位进程和线程

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

如果业务进程写入量不高,但 kworkerjbd2、数据库后台进程或容器运行时活跃,要继续查脏页、文件系统日志、镜像构建、备份和数据库维护任务。

第三步:确认文件与容量

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 按目录可能找不到。

第四步:下探系统调用和代码路径

bash 复制代码
sudo strace -c -f -p "$PID" -e trace=read,write,fsync,fdatasync

sudo perf record -F 99 -g -p "$PID" -- sleep 30
sudo perf report --stdio

strace 对高吞吐进程有明显开销,只适合短窗口确认行为,不适合长期挂在产线关键进程上。重点看是否出现频繁 fsync、小块 write、同步网络存储访问或重复读同一批文件。

第五步:确认设备健康

bash 复制代码
sudo dmesg -T | grep -iE 'i/o error|ata error|nvme|mmc|reset|timeout'
sudo smartctl -a /dev/sda
sudo nvme smart-log /dev/nvme0

eMMC / SD 设备未必完整支持 SMART,需要借助厂商工具或 mmc 工具读取 extCSD 信息。设备错误、重置、超时通常比单一性能指标更有定性价值。

九、三个典型场景

场景 1:日志把边缘盘写满

典型表现:

  • df 快速上涨;
  • wkB/s 持续偏高;
  • iotop 中业务进程或日志 agent 写入量大;
  • 设备 w_await 抖动。

排查:

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

处理方向:

  1. 降低无效 debug 日志等级;
  2. 控制日志大小、保留天数和压缩策略;
  3. 用异步缓冲替代每条日志 fsync
  4. 检查 logrotate 是否生效:
bash 复制代码
logrotate -d /etc/logrotate.conf

如果必须收紧 systemd journal,先确认留存要求,再执行:

bash 复制代码
sudo journalctl --vacuum-size=500M

场景 2:数据库写入慢

先分清是数据库自己慢、磁盘慢,还是等待其他资源:

bash 复制代码
iostat -xz 2 5
sudo iotop -p "$(pgrep -o postgres)" -P -d 2

PostgreSQL 可以查看当前等待事件:

sql 复制代码
SELECT
  pid,
  state,
  wait_event_type,
  wait_event,
  now() - query_start AS query_age,
  query
FROM pg_stat_activity
WHERE state <> 'idle'
ORDER BY query_start;

如果启用了 pg_stat_statements,再看 Top SQL:

sql 复制代码
SELECT
  query,
  calls,
  mean_exec_time,
  total_exec_time
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;

处理方向包括减少无效查询、调整批量写入、检查 WAL 与检查点策略、确认表空间所在设备,以及避免把数据库与高频日志放在同一张 eMMC 上。

场景 3:容器 I/O 归因

只拿容器主进程 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 常见字段包括 rbyteswbytesrioswios,具体依赖内核版本。io.max 能看出是否配置了读写带宽或 IOPS 限制。Kubernetes、containerd、podman 的 cgroup 路径不同,但思路一致:先定位 cgroup,再看统计与限制。

十、工业边缘特有的判断点

1. 小块随机写比大流量更伤 eMMC

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

优化方向:

  • 日志按大小或时间批量刷盘;
  • 指标先内存聚合,再批量落盘;
  • 数据库合理设置 checkpoint 和 WAL 策略;
  • 把高频写数据与系统盘分离;
  • 监控设备健康指标和预留空间。

2. 脏页回写会造成周期性抖动

应用写入 page cache 时看似很快,真正的磁盘写入可能延后发生。观察:

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

如果 Dirty 长期抬升、随后出现写爆发,要结合 vm.dirty_ratiovm.dirty_background_ratio、业务批量策略和设备写入能力评估。调内核参数前先做可回滚测试,不要直接改产线配置。

3. 远程与虚拟存储要区分层次

NFS、iSCSI、overlay、device mapper、RAID 和虚拟化磁盘都会改变 I/O 路径。上层看到的 /dev/vda 可能不是真正承担擦写和排队的物理设备。排查时要记录映射关系,避免只对虚拟设备下结论。

4. CPU iowait 不是唯一证据

iowait 受时钟节拍、多核和空闲统计方式影响,只能作为辅助线索。设备延迟、队列深度、进程 I/O 等待和应用请求完成时间要一起看。

十一、长期监控建议

指标 采集建议 告警思路
设备读写速率 iostat / node exporter 与同时间业务周期对比
awaitaqu-sz 按设备和读写方向拆分 持续高于基线才告警
进程读写速率 pidstat 或 taskstats Top 进程异常增长
脏页与回写 /proc/meminfo 周期性爆发和持续累积
文件系统水位 df 预留安全余量
设备错误 dmesg / SMART /厂商工具 任何持续错误都高危
容器 I/O cgroup 统计 超额或被限流
日志增长 日志目录大小 增长率异常

告警不要只设"绝对值超标",要设"相对基线偏离 + 持续时间 + 业务影响"。例如写速率升高本身不是故障,若同时数据上传积压、请求延迟上升或磁盘水位快速上涨,才需要升级处理。

十二、常见坑

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

并行 SSD / NVMe 可以同时处理多个请求,%util 高不一定代表性能耗尽。应结合 awaitaqu-sz、吞吐、IOPS 和应用延迟。

坑 2:继续使用 svctm

svctm 已废弃且不可靠。服务阶段耗时应优先用 blktrace / btt 的阶段数据分析。

坑 3:容器里只看容器主 PID

主 PID 不等于容器内所有进程和线程。需要结合进程树、cgroup I/O 统计或进入正确的命名空间采集。

坑 4:误解 iotop -a

-a 是自进程或 iotop 启动以来的累计值,不是系统启动以来的累计值,也不适合直接当实时带宽看。

坑 5:fio 压测生产系统盘

fio 会制造真实写入,可能占满容量、放大延迟并消耗 eMMC 寿命。必须在专用测试目录和变更窗口执行。

坑 6:只看 du 不看已删除文件

du 找不到已被删除但仍被进程持有的文件空间,需要用 lsof +L1 交叉验证。

坑 7:没有基线

没有正常负载曲线时,很难判断 20ms、50ms 或 100ms 是异常还是设备特性。上线初期就应采集不同班次、不同工况和不同采集周期的 I/O 基线。

十三、落地清单

  1. iostat -xz 2 5 确认设备和队列状态;
  2. iotop -oP -d 2 找主要进程;
  3. 需要线程归因时用 iotop -o -d 2pidstat -d -t
  4. dfdu -xjournalctl --disk-usage 排查容量;
  5. /proc/meminfo 判断脏页回写;
  6. blktrace / btt 拆排队与设备耗时;
  7. 用受控 fio 建立设备基线;
  8. 容器场景必须核对 cgroup 统计与限制;
  9. 所有输出统一时间戳并归档;
  10. 将结论回写为阈值、巡检项和应急 SOP。

在 Zenova EdgeOS 的边缘运维链路中,这类 I/O 证据通常被纳入统一的运行时观测与告警体系,让现场人员不只看到"磁盘忙",而能同时看到设备、进程、容器、容量和业务指标之间的关联。

TL;DR

iotop 负责回答"谁在做 I/O",iostat 回答"设备层发生了什么",pidstat 适合长期采集,blktrace / btt 拆解排队与设备完成耗时,fio 用于建立受控基线。工业边缘场景要额外关注 eMMC 寿命、小块随机写、脏页回写、容器 cgroup、日志容量和远程存储映射。判断饱和不要只看 %util,也不要再依赖已废弃的 svctm;最终要以同一时间窗口内的设备、进程、容量、健康状态和应用影响共同定位。

相关推荐
Black蜡笔小新1 小时前
运维提效!视频汇聚平台EasyCVR视频质量诊断,掉线花屏自动预警
运维·安全·音视频
又見山1 小时前
Codex 实战:用 AI 写运维脚本
运维·人工智能
Elastic 中国社区官方博客1 小时前
Elasticsearch 向量数据库:几分钟内完成部署,以经济高效的方式扩展至数千亿规模
大数据·运维·数据库·elasticsearch·搜索引擎·ai·全文检索
新兴AI民工1 小时前
【Linux内核三十九】进程管理模块:CFS负载均衡(四):find_busiest_group决策矩阵与calculate_imbalance
linux
LongRunning1 小时前
【linux】RK3568(五)云服务器-视频推流
linux
赵民勇2 小时前
resolvectl命令详解
linux·运维
新时代牛马2 小时前
Linux 日志管理:journald、rsyslog 与 logrotate
linux·运维·服务器
あ-2 小时前
企业 DevOps + 协同办公 + 可观测性 + 网络基础设施平台
运维·网络·devops
资深电气设计2 小时前
技术解析:ABB变频器ACH580/ACS180在CDU循环泵系统的应用技术框架
linux·运维·服务器