白嫖的 Oracle Cloud ARM 实例,top 里常年挂着 40%+ 的 iowait,一度以为自己踩到了磁盘性能的坑。直到有天认真查了一下,才发现事情没那么简单。

事情要从一次例行巡检说起。登上服务器跑了个 top,iowait 优雅地停在 45% 左右。第一反应是——磁盘是不是快不行了?毕竟免费实例嘛,IO 性能缩水也说得过去。于是 iostat、iotop 一顿操作,结果磁盘读写响应时间正常得很,完全没有瓶颈的迹象。

这就奇怪了。iowait 的定义是"CPU 空闲但有待完成的 IO 请求",可实际上并没有什么 IO 压力,那这 40% 是从哪冒出来的?

顺着这个线索,我翻了翻 Linux 内核的账本。内核源码 sched/cputime.c 里写得明明白白:每个时钟 tick,内核先检查硬中断、软中断,再算用户态、系统态时间,剩下的归 idle。而 idle 里又做了个区分——如果当前 CPU 的运行队列里有进程在等 IO(nr_iowait > 0),这笔时间就记到 iowait 账上,否则记到 idle 账上。

换句话说,iowait 不是"IO 多慢",而是"CPU 闲着的时候刚好有人在等 IO"。这个区分在物理机上没问题,因为时钟 tick 和 CPU 调度是一体的。但在虚拟机里,事情就变味了。

虚拟机的"时间感知"依赖宿主机的调度。当宿主机把 CPU 时间片分给别的虚拟机时,当前虚拟机内核的时间计数器可能产生偏差。这个现象在 OpenVZ、Xen 等虚拟化平台上都有记录——/proc/stat 里的各项时间累加值有时甚至对不上,总量超过一个 CPU 应有的 tick 数。

Oracle Cloud 的 ARM 实例用的是 Ampere Altra / Neoverse 处理器,底层虚拟化方案虽然官方没有公开太多细节,但从社区反馈来看,这类共享型实例(尤其是 free tier 的 VM.Standard.A1.Flex)普遍存在时钟统计偏差的问题。表现就是:iowait 居高不下,但实际 IO 性能完全正常。

这其实和 steal time(top 里的 st 列)是同一类问题。steal time 是宿主机明确告诉你"我拿走了你的 CPU 时间",而 iowait 偏高则是宿主机没打招呼,悄悄把时间差混进了 idle 和 iowait 的统计里。本质都是虚拟化层的调度行为,只是统计口径不同。

验证方法也很简单。在 iowait 高企的时候,跑一个 CPU 密集型任务(比如 sha1sum /dev/urandom),如果 iowait 瞬间降下来、user 飙上去,说明那部分时间本来就是空闲的,只是被错误归类了。真正的 IO 瓶颈不会因为你跑个计算任务就消失。

还有个佐证:用 vmstat 1 观察,bi(blocks in)和 bo(blocks out)几乎为零的时候,wa 依然很高。这就像银行账户显示你每月在某个品类上花了一半的工资,但去查消费记录却找不到对应的账单。

所以结论是:别被 top 里的 iowait 吓到。如果你的 OCI ARM 实例 IO 性能实测正常,那这个高 iowait 大概率只是虚拟化环境下的统计假象,不影响实际使用。想看真实负载,盯 us + sy 和实际 IO 延迟比看 iowait 靠谱得多。