资讯动态

Linux磁盘I/O排查实战:用iotop定位进程级I/O瓶颈

发布时间:2026/10/21 5:29:18 来源:尧图企业网站定制
有一次生产环境出了问题业务侧反馈数据库没有扩容、也没有慢SQL可响应就是莫名变慢。我登录服务器看了下topCPU和内存都很空闲但系统负载却高得离谱再跑iostat磁盘 util 已经接近 100%。问题来了——iostat只告诉我磁盘很忙却没告诉我到底是谁在忙。这时候最该掏出来的工具就是我今天要聊的iotop。iotop是一款实时磁盘 I/O 监控工具核心能力是像top展示 CPU 占用那样按进程排列出每个进程实时的磁盘读、写带宽以及 I/O 等待占比。它解决的是 Linux 运维里最头疼的一类问题磁盘确实在被大量读写但无法确定肇事进程。适合系统运维、SRE、DBA、后端开发者以及所有需要排查性能瓶颈的人。这篇内容我会从原理、参数、实战场景到避坑技巧完整梳理一遍都是自己长期用过之后的经验。1. 为什么排障时我总先打开 iotop它能解决什么问题1.1 top 与 iostat 的盲区在哪里top是大家最熟悉的性能工具但它对磁盘 I/O 的感知非常有限。传统top里只有waCPU 等待 I/O 的时间占比这样一个间接指标很多新版top虽然加了 I/O 列显示的是进程的“已读/已写字节数”累积值但既不是实时速率也很难直接反映线程正在承受的 I/O 压力。换句话说top能告诉你系统哪个进程在占用 CPU却很难告诉你哪个进程在疯狂写盘。iostat则站在物理设备视角。它能给出每块磁盘的吞吐量、每秒读写请求数、等待队列长度、util 使用率是判断磁盘是否打满的权威工具。但问题在于iostat的输出粒度是设备它不会告诉你这些吞吐量来自哪个进程、哪条业务链路。把这两个工具结合起来思考就能看到清晰的盲区磁盘层知道“有多忙”进程层看不到“谁在忙”中间缺少一个能把进程和磁盘 I/O 关联起来的桥梁。iotop补的正是这个缺。它把内核记录的进程 I/O 计数按照实时速率展示出来让你一眼看到“罪魁祸首”的 PID 和命令行。1.2 iotop 的采数原理/proc 里的计数器才是关键很多人以为iotop是通过 hook 系统调用或者解析磁盘请求队列来统计的其实不是。它的数据来源是 Linux 内核的 TASK I/O accounting 机制核心入口是/proc/pid/io这个虚拟文件。打开任意一个进程的/proc/pid/io你会看到这样的原始数据rchar: 1844225359 wchar: 73182208 syscr: 127923 syscw: 157256 read_bytes: 151392256 write_bytes: 50462720 cancelled_write_bytes: 16384几个字段要理解清楚rchar/wchar进程通过读写系统调用产生的字节数包含缓存命中部分。read_bytes/write_bytes进程真正发起磁盘读写的字节数是会计子系统在块设备层统计的值。syscr/syscw读、写系统调用的次数。cancelled_write_bytes进程写入后又被截断丢弃的字节数。iotop做的核心事情就是周期性读取这些计数器的差值除以采样间隔算出进程当前每秒的 I/O 带宽。这就是为什么它展示的是一个估算的实时速率而不是某个瞬间的瞬时值。为了能拿到全系统的进程数据iotop需要内核开启CONFIG_TASK_DELAY_ACCT和CONFIG_TASK_IO_ACCOUNTING绝大多数发行版默认内核都已启用所以开箱即用。1.3 认清楚 iotop 的边界它监控的是计划 I/O 不是真实落盘这是使用iotop时最容易产生的误解。iotop展示的DISK READ/DISK WRITE并不是磁盘设备在物理层面完成的吞吐而是进程在 I/O 调度器层面的“计划 I/O”。内核会计子系统在进程提交读写请求、进入块设备层时就把字节数记下了这并不等于数据已经落盘。举个例子你写一个文件数据先进入 page cache内核会计可能已经把这个写请求计入了进程的write_bytes但真正的落盘是后台回写线程如flusher完成的。反过来如果数据在 page cache 里被命中读取完全不经过块设备rchar会增加但read_bytes不增加iotop也就不会显示对应的磁盘读速率。所以iotop适合判断“哪个进程发起了大量磁盘请求”但不适合回答“磁盘设备到底完成了多少物理 I/O”。后者应该交给iostat的rKB/s、wKB/s、util来回答。两者配合使用才是完整的定位链路。这一点在后面的实战案例里还会反复出现。2. 安装、启动与界面速览上手 iotop 需要知道的基础操作2.1 不同发行版的安装方式iotop是 Python 写的工具依赖很少安装很轻量。不同系列的 Linux 安装命令略有区别# Debian / Ubuntu apt update apt install -y iotop # CentOS / Rocky Linux / AlmaLinux yum install -y iotop # 或新版 dnf dnf install -y iotop # 需要校验版本时 iotop --version如果你是在内网环境无法直接走软件源可以找一台同发行版的机器下载 rpm 或 deb 包离线安装。iotop的依赖基本只有 Python 环境和ncurses即使手动部署也不费劲。装完随手敲一下iotop就能进入交互界面。2.2 命令行参数速查与使用习惯iotop参数不多但每个都有明确的使用场景。我最常用的参数组合整理成了一张表参数作用使用场景-o只显示有 I/O 行为的进程进交互界面第一件事就是按o过滤噪音-b批处理模式非交互脚本采集、日志记录-n NUM刷新次数上限配合-b跑固定次数后退出-d SEC刷新间隔默认 1 秒观察低频读写时调大间隔-p PID只监控指定 PID单独盯数据库或某应用进程-P只显示进程不展开线程避免刷屏-a显示累计 I/O 量而非实时速率判断谁在长期消耗-k使用千字节显示老版本默认字节看可读性用-t每行追加时间戳批处理输出里做时序分析-q不显示顶部统计栏精简脚本输出单说参数比较干我举一个实际的组合用法。比如你想每 5 秒采样一次共采样 12 次记录哪些进程有 I/O同时带时间戳可以这样写iotop -botq -n 12 -d 5 /var/log/iotop_$(date %F).log这个命令会以纯文本形式逐行输出每行带时间戳只显示有 I/O 的进程。脚本或 cron 用这种方式采集比进入交互界面人工盯可靠得多。2.3 交互界面每一列到底在表达什么进入iotop默认界面后上方有一段系统汇总信息和top类似包含当前时间和load average、进程总数、内核线程等信息。真正关键的是下面的进程列表每一列的含义可以对照看TID线程 ID。默认按线程展示如果看到两个同名的COMMAND但 TID 不同说明这是多线程应用的不同线程。PRIO线程的 I/O 优先级用ionice设置be/4表示最佳努力级别、优先级 4。USER进程属主。DISK READ/DISK WRITE当前采样周期内进程每秒实际磁盘读/写速率单位默认是 B/s加-k后变为 KB/s。SWAPIN线程在交换分区等待上花的时间占比。这个值升高说明内存吃紧发生了换页。IO线程等待 I/O 完成的时间占比。这个值高说明进程在等磁盘响应。COMMAND进程命令行。这里最见功力的是看懂IO这一列。一个进程显示IO很高可能有两种情况一是它自己发起了大量读写请求正在同步等待结果二是它并没有主动发起 I/O而是在等待锁、等待某个 tc 系统调用完成比如文件锁、pipeline 阻塞。不管哪种都说明这个进程和 I/O 子系统之间有很强的阻塞关系是排查的首要对象。2.4 交互界面支持的按键操作iotop既然是全屏交互工具就有一组快捷键。和老运维的习惯一样我进界面后通常会先按o过滤出活跃进程再按左右方向键切换排序列。按键作用o切换只显示有 I/O 的进程p切换线程模式/进程模式a切换实时速率/累计 I/O 模式r反向排序左右方向键切换排序字段常用IO列q退出i切换 I/O 优先级展示方式用多了之后我基本不看DISK READ排序而是直接按IO降序排列因为等待占比最能反映“谁被磁盘拖住了”。当然如果想排查带宽大户就按DISK WRITE排序各有所长。3. 核心玩法与高级用法从“会看”到“会用”3.1 用 -o 参数过滤噪音瞬间锁定嫌疑进程服务器上运行的进程动辄几十上百但真正持续产生磁盘 I/O 的通常只有几个。刚启动iotop不按o你会被一堆0.00 B/s刷屏。按o或者启动时带-o直接只显示有 I/O 行为的进程是效率最高的切入方式。实际操作里按下o之后Top 区域会显示类似Total DISK READ: ... | Total DISK WRITE: ...的汇总列表只剩下了有 I/O 的进程。这时如果某个进程的DISK WRITE是几百 MB/s大概率就是它了。再用p切换到进程模式确认是哪个业务进程在干这件事基本就把范围缩小到很小。-o也有一点需要注意它过滤的是“当前采样周期内有 I/O 行为”的进程。如果你的采样间隔是 1 秒一个每 10 秒才写一次的小进程可能会被漏掉。这种低频 I/O 场景建议把-d调大到 5 秒甚至 10 秒或者观察时间长一点。3.2 批处理模式是实现自动化监控的基础交互界面适合人工排查但真正的生产监控必须靠批处理模式。-b模式下iotop放弃全屏渲染把每一行记录以标准输出打印相当于一个低成本的系统日志采集器。我比较推荐的一个玩法是采样间隔设置成 2-3 秒配合时间戳输出到独立文件等故障发生时回溯。比如iotop -botq -d 3 -n 200 -t /var/log/iotop.log每行会输出类似这样的内容10:23:45 Total DISK READ: 0.00 B/s | Total DISK WRITE: 120.36 M/s 10:23:45 TID PRIO USER DISK READ DISK WRITE SWAPIN IO COMMAND 10:23:45 1384 be/4 mysql 0.00 B/s 56.21 M/s 0.00% 12.50% mysqld --datadir/var/lib/mysql这种带时间戳的文本日志后续用grep、awk都很好处理。-q参数用来压缩输出让每个刷新周期只输出核心数据行日志会清爽很多。3.3 累计 I/O 模式找“长期消耗大户”而不是“瞬时疯狂者”默认的实时速率有时候会掩盖问题的真相。一个进程每 5 秒突发写一次 500MB实时模式看到的瞬间带宽很高但平均下来并不惊人。反过来一个进程一直以 5MB/s 的速度写如果只看表面带宽可能会低估它。这种场景需要用-a参数开启累计模式。这时DISK READ和DISK WRITE两列不再显示实时速率而是显示自进程启动以来或者自iotop启动以来的累计读写字节数。按下a键切换后你会看到真正的累计消耗TID PRIO USER DISK READ DISK WRITE SWAPIN IO COMMAND 2223 be/4 appuser 12.30 G 345.10 G 0.00% 23.50% java -jar service.jar这种“总量视角”特别适合用来排查容量规划问题。比如某块数据盘空间持续增长但又说不出哪些文件在变大时用累计模式排序找到写入量最大的进程再结合lsof看它打开了哪些文件很快就能定位。3.4 单独盯一个进程-p 与 -P 的用法细节生产环境最常见的需求是“我就想知道数据库这个进程的磁盘 I/O 情况。”此时不需要-o、也不需要全量列表直接用-p指定 PID 即可iotop -p $(pgrep -x mysqld)这里有两个细节值得留神。第一-p参数支持多个 PID用逗号分隔即可iotop -p 1001,1002。第二iotop默认是线程视角多线程应用比如 Java 应用默认展示所有线程如果你只关心进程维度的总和加-P参数iotop会把进程的所有线程的 I/O 汇总显示列表会清爽得多。在某些数据库场景里我更倾向于不加-P因为数据库的多个后台线程中可能只有一个线程负责刷日志。看到具体的线程名比如logwriter、pagecleaner可以作为初步判断依据再配合数据库自己的监控视图做交叉验证。3.5 多工具联动iotop 不应该单打独斗单一工具的信息始终有限排查 I/O 问题时我通常把iotop放进一个完整的工具链里。第一步iostat -x 1确认磁盘层的整体压力看util、await、svctm。如果util接近 100%说明磁盘确实饱和了有继续查的价值。如果util不高但业务依然慢重点可能不在磁盘硬件层要去查锁、网卡、文件系统甚至应用逻辑。第二步iotop定位到具体进程。找到是谁在产生这些 I/O。第三步如果还需要进一步分析文件级别的读写路径我会结合lsof -p pid查看进程打开了哪些文件或者用strace -p pid -e traceread,write,fsync观察它实际在操作什么。如果 I/O 过于密集strace会带来明显开销需要谨慎使用。更高级的场景可以考虑bpftrace或perf这类工具。这套组合拳比只用iotop一条路走到黑要可靠得多因为它从系统层到进程层、再到系统调用层层层递进。4. 实战场景拆解用 iotop 定位四个典型问题4.1 场景一数据库实例响应变慢元凶竟然是日志刷盘有一次排查某套数据库实例的抖动问题业务表现是偶尔出现几十毫秒到几百毫秒的延迟尖刺。top里 CPU 占用不高iostat看到那块数据盘的util在尖刺时冲到 85%-95%await也明显升高但没有超过 30ms不像是硬件故障级的问题。进入iotop后我按o过滤再按IO排序排在最前面的果然是数据库进程。但有意思的是数据库进程整体 IO 占比并不算高展开线程后看到几个和日志落盘相关的后台线程在疯狂刷盘DISK WRITE维持在较高水平。这就给我们指了一条很明确的路问题不在数据库查询层而在于日志提交策略和磁盘 fsync 频率。后来调整了事务提交策略把组提交打开抖动明显缓解。回头复盘时如果没有iotop的线程级视角我们大概率会去优化 SQL方向就偏了。4.2 场景二定时任务引发的 I/O 风暴另一类高发问题是定时备份、定时批处理任务撞在同一台服务器上。一次客户反馈每天凌晨 1 点左右应用接口响应变慢。检查监控发现凌晨期间磁盘读带宽突然暴涨但系统没有其他人操作。我写了一个简单的采样脚本用iotop -botq -d 5 -t跑了一整晚第二天看日志时凌晨 1 点准时出现了一个备份相关的进程名磁盘读速率几百 MB/s持续了接近 20 分钟。这就是典型的定时任务风暴。定位到进程之后方案通常有三个调整定时任务的执行时间错开业务高峰给备份任务设置ionice低优先级让出 I/O 带宽如果备份本身必须跑考虑限速工具或者错峰分片。ionice -p pid可以在不中断任务的情况下调整 I/O 优先级实战中很实用。4.3 场景三D 状态进程与 SWAPIN 的纠缠iotop的输出里有个SWAPIN列很多新手不太理解。它和IO一样是百分比表示进程在等待换页Swap In上花费的时间比例。这个值如果长期不是 0%基本可以断定内存存在压力。有一回排查一个 Java 服务的卡顿top里有大量 D 状态的线程进程处于不可中断睡眠kill 不掉。我当时第一反应是磁盘故障但iostat显示磁盘负载并不高。打开iotop后看到大量线程的SWAPIN高达 30%-50%IO也不算低。这让我意识到问题根源在内存——物理内存不足导致频繁换页线程在等待换页时进入了不可中断状态。顺着这个方向查调整了 JVM 堆内存参数并增加物理内存后D 状态消失。这个案例给了一个重要启发SWAPIN和IO同时高时不要只盯着磁盘内存换页可能是真正的幕后黑手。4.4 场景四容器和云主机里看不到有效数据怎么办容器环境里使用iotop是很多人的痛点。你会遇到两种情况一是进程列表为空只能看到宿主机的一堆进程二是能启动但数据全是 0或者看到的是宿主机的全局统计。原因是容器隔离的边界问题。iotop依赖/proc/pid/io统计如果容器跑在宿主机共享内核环境里/proc能看到的内容取决于容器的 PID namespace 和权限设置。在非特权容器里通常无法看到其他容器的进程甚至看不到自己的 I/O 统计即使看到了统计的计数也是基于内核全局的无法按容器精确拆分。如果你确实需要在容器里监控进程 I/O建议优先在宿主机上用iotop按进程维度观察再通过 cgroup 目录下的io.stat或io.pressure来定位具体容器。iotop并不适合作为容器内的一等监控工具这个认知能帮你少走很多弯路。5. 常见问题与排查技巧实录5.1 权限不足与 Netlink 报错新手运行iotop最常见的报错大概长这样Failed to open /proc/self/task/xxx/io: Operation not permitted或者NETLINK ERROR: Cannot allocate memory前者是实打实的权限问题。iotop需要读取所有进程的/proc/pid/io这要求足够权限。直接sudo iotop通常能解决。如果使用了容器或安全加固策略还需要额外检查是否赋予了CAP_SYS_ADMIN、CAP_NET_ADMIN等能力或 seccomp 是否拦截了所需系统调用。后者在排查时需要多考虑一层。NETLINK ERROR是iotop试图通过 netlink socket 获取进程 I/O 优先级信息时出现的常见于系统文件句柄耗尽或容器网络受限。可以先用ulimit -n看一下进程的文件描述符上限同时确认容器是否允许建立 netlink socket。5.2 数据全为 0、看不到进程列表如果你启动iotop看到进程列表是空的或者所有进程的读写都是 0先不要怀疑工具坏了。最可能的原因是内核配置缺少相应选项。iotop依赖CONFIG_TASK_IO_ACCOUNTING和CONFIG_TASK_DELAY_ACCT某些精简内核或云厂商定制内核会裁剪掉这些配置。检查方式很简单cat /boot/config-$(uname -r) | grep -E TASK_IO_ACCOUNTING|TASK_DELAY_ACCT如果输出is not set说明这个内核不支持你需要换用支持的内核或者接受无法使用iotop的现实退回iostat pidstat组合。另一个常见原因是容器环境前面已经提过不再重复。5.3 iotop 数据与 iostat 对不上是不是工具不准很多人在实践里发现一个现象iotop显示的进程读写之和和iostat的磁盘吞吐对不上。这不代表iotop不准而是统计口径本质不同。iostat统计的是块设备层实际处理的 I/O包括文件系统元数据、日志、回写、预读等开销iotop统计的是进程层提交的 I/O 请求不含部分文件系统内部开销同时也可能计入尚未真正落盘的数据。另外像pdflush、kworker这类内核线程承担的实际落盘工作会计在对应内核进程名下而不是业务进程名下。所以当你把iotop所有进程加起来跟iostat比较时差距大是正常的。正确的用法是用iostat判断磁盘压力是否异常用iotop判断压力主要由哪个用户态进程触发。这两个数值天然不是等号关系不必纠结。5.4 长时间采集时的注意事项如果你打算用iotop做长时间的定时采集有几个小问题要提前想好不然日志会让你头疼。第一日志轮转。iotop -b模式下输出量不算大但一天 24 小时连续记录仍然会产生可观的文本量。用logrotate做按天或按大小轮转保留适当份数避免磁盘被监控日志写满。第二输出格式的稳定性。不同版本的iotop输出列宽、单位有细微差别如果你的脚本依赖位置解析建议固定iotop版本并在升级后重新测试解析脚本。第三如果你要同时监控多个指标CPU、内存、I/O不要只依赖iotop一个命令。可以用sar归档系统级数据用pidstat记录进程 CPU 和内存再把iotop输出的 I/O 数据一起汇总到统一时序库。这样故障回溯时不用同时打开好几份日志对时间轴。6. 结合真实经验我的 iotop 使用习惯与几个扩展想法回到开头那个故障排场上来我如今的排查节奏已经固定成了三步。先iostat -x 1看磁盘硬件层是否饱和再iotop找具体肇事进程最后用lsof或strace做纵深定位。整个过程不超过五分钟比过去一上来就改代码可靠得多。几个小的使用习惯也分享一下。第一进iotop交互界面后我会立刻按o过滤然后按右方向键把排序切换到IO列。DISK READ/DISK WRITE是带宽视角IO是阻塞视角多数时候阻塞视角更能反映业务感受。第二如果想看趋势而不是瞬时值把刷新间隔-d调到 3 秒到 5 秒数据曲线会平滑很多避免被瞬时毛刺误导。第三在给开发团队复盘时我通常会把iotop -botq -d 5 -t采样得到的文本日志直接贴出来比截图更有说服力也方便在工单里归档。iotop还能向前走一步。比如把它包一层 Shell 脚本检测到某个 PID 的持续写带宽超过阈值时自动触发告警或者用-p参数单独监控业务进程配合计算容器卷的容量增长。工具本身是固定的思路可以很灵活。希望大家下次再遇到“系统很慢但不知道谁在写盘”的情况时能想起这篇文章然后更快地找到答案。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑