最近在做连续数据流的缓冲系统,C语言代码实现后,粗略测试了下,功能上应该没有问题。那么,接下来就该测试性能了。输入 top 命令,的确可以看到一系列 cpu 使用率,其中一个值得注意的子项就是 io 使用率了,如下图:
上图中 io 前面的数字是什么意思呢?是指 CPU 有 63% 的时间花费在 io 上了吗?在 Linux 中输入 man 命令查看相关手册,发现 io(wait) 被解释为“等待 I/O 完成的时间”。
如果按照手册对 iowait 字面上的解释,是很容易陷入误区的。因为就上例而言,似乎 CPU 有 63% 的时间消耗在等待 I/O 操作完成,导致 CPU 的性能白白浪费这么多。
其实不是的,Linux 是一个成熟的操作系统,它才不会让 CPU 宝贵的性能白白浪费在耗时的 I/O 等待上,实际上,如果当前系统还有其他任务需要使用 CPU,Linux 会将等待 I/O 完成的任务暂时挂起,将 CPU 使用权暂时交给有需要的任务。
那么 iowait 到底是什么意思呢?
有人认为,iowait 只是 CPU 空闲(idle)时间的一个子集,也就是说 iowait 其实可以归类到 idle 状态,本质上表示 CPU 是空闲的,只不过 iowait 表示任务中有等待 I/O 操作完成的时间。
这样认为有一定的道理,毕竟哪怕 iowait 的数值是 100%,也仅仅是说明是 CPU 把时间都花在“等待”上了,这样的情况一般只有在当前系统没有其他任务需要使用 CPU 时才会发生。因为一旦有其他任务需要使用 CPU,Linux 内核会立刻将 CPU 提供给该任务使用,CPU 时间就不再全部是“等待I/O”花费的了(不再是 100% io 了)。
不过读者应该明白的是,“idle”是 CPU 的状态,而 “iowait”则是任务的状态。对于单核 CPU 来说,同时只能有一个任务运行,上述说法可以认为是准确的。但是对于多核 CPU 来说,情况就有些不同了。
例如,某个系统拥有 4 个 CPU,有一个 I/O 超密集型任务正在运行,那么,iowait 会是 100% 呢,还是 25% 呢?换句话说,iowait 会是在其中一个 CPU 上 100%,其他CPU 上 0% 呢,还是会在 4 个 CPU 上平均 25 % 呢?
简单做一个实验就可以了。我们使用 Linux 中的 dd 命令模拟高密集 I/O 任务,这一过程可以通过输入以下命令实现:
dd if=/dev/sda of=/dev/null bs=1MB
这条命令可能需要 root 权限,/dev/sda 是我的磁盘,读者可能需要换成自己的节点名。
此时,通过 top 命令可以查看到下面这样的结果:
图中的 “wa”表示 I/O 等待时间(它和 io、iowait 是一个意思,名字不同而已)。可见,Linux 此时采用单个 CPU 处理 I/O 任务。如果读者细心的话,应该能够发现,I/O 任务只是偶尔的切换到其他 CPU 上运行,这是为了保证 CPU 缓冲的命中率,Linux 内核尽量让任务在单个 CPU 中运行。
在其他一些系统中,I/O 任务可能会在各个CPU中频繁的切换,此时会产生下面这样的结果:
假设 dd 命令是系统中执行 I/O 的唯一任务,那么在同一时刻,最多只会有一个 CPU 处理 I/O 等待任务。因此,实际上 34.8+20.9+26.7+3.7=86.1,接近但低于100。
进一步实验
为了让实验更可重现,我们可以使用 taskset 命令为任务指定 CPU:
taskset 1 dd if=/dev/sda of=/dev/null bs=1MB
应该注意,taskset 后的数字 1 并不是 CPU 的编号,而是一种掩码。
此时通过 top 命令查看 CPU 使用请看,应该能够发现 CPU0 的 wa 项接近 100,这说明 CPU0 几乎所有的时间都花在等待 I/O 操作完成上。那么,是不是此时 CPU 就没有精力处理其他任务了呢?我们再输入下面这条命令:
taskset 1 sh -c "while true; do true; done"
上面这条命令是在相同的 CPU 上执行一个死循环,用于模拟计算密集型任务,它是不是就没有机会执行了呢?输入 top 命令,得到如下结果:
CPU0 的 wa 降低为 0 了,与此同时,用户态和系统态的 CPU 时间接近 100% 了。这是意料之中的,因为 I/O 等待时间只是 idle 时间的子项,本质上 CPU 是空闲的,Linux 内核当然可以把 CPU 交给第二个任务使用。原本用于等待 I/O 完成的 CPU 时间,现在用于处理第二个任务了。此时通过 top 命令查看 wa,自然得到接近 0 的结果。
小结
现在基本就清楚 top 命令中 % io 的含义了:对于指定的 CPU 来说,iowait 表示在此时间内,CPU 其实是空闲的,不过 CPU 并不是严格意义上的“空闲”,毕竟它还需要等待 I/O 操作完成。对于产生 I/O 操作请求的线程来说,它会阻塞等待 I/O 操作完成。理解这一点,对于我们开发I/O操作密集的C语言程序是非常有帮助的。
很赞,终于明白了iowait,感谢博主分享
感谢博主的分享,这里有两个问题想请教博主,或和大家讨论:
1.为什么 dd if=/dev/sda of=/dev/null bs=1MB在centos7中运行并没有形成iowait,反而是直接占用了sy的CPU使用率,即内核态的CPU。
2.一般情况下,iowait会包含“D”或“I”状态的进程,I状态姑且不论,D状态是“Uninterruptible Sleep”,一般情况都是和硬件交互时等待I/O的状态。且不允许被其他进程或中断打断。那么如果1核CPU,一个D状态进程占用该CPU并由于某些原因处于等待I/O的状态,iowait是不是就达到100%了?如果是,那么这时候,系统中再运行其他任务,内核会把被D进程占用的CPU资源切换出来分给其他任务么?iowait会降低么?
1. 可能你的机器比较强,我做实验时,是在一个io速度比较慢,缓冲区比较小的嵌入式平台做的。如果io速度很快,或者说缓冲区很大,基本拷贝可以在“瞬间”完成,自然不会有io等待时间。一般io等待时间会出现在系统将缓冲区数据写入硬盘期间,硬盘写入速度越慢,iowait 的显示值越大。
2. 你都已经说“且不允许被其他进程或中断打断”了,对不?
现在才看到这篇文章,对于你的第二点问题我也是有疑惑的。
我是这么想的:
1、如果系统完全只有一个进程,并且是 sleep 状态(D 对于 CPU 来说就是一种 idle 而已,只不过对于进程来说,直到它想要的操作完成,是不可以被打断的,即使 kill 也不可以),那么 iowait 确实是 100%。一个是从进程角度,一个是从 CPU 角度。
2、但是 iowait 是一种 sleep,所以这时候如果有另一个进程,会用 100% CPU,那么 CPU 就会变成 100% busy,0% iowait;如果有另一个进程,会使用 70% 的 CPU,那么 CPU 的状态就是 70% busy,30% iowait。CPU 是一种类似时间的资源,70% 的意思是 70% 的时间。iowait 100% 意味着 CPU 100% 的时间都是 iowait,即 idle。如果有其他进程想使用 CPU 是可以使用的。
又研究了下,不可中断指的并不是CPU不响应外部硬件的中断,而是指进程不响应异步信号。
D状态也是在sleep的进程,所以CPU行为应该和博主说的一致