iowait

简单来说,iowait 是指CPU处于idle状态,而且至少有一个进行中的I/O操作。 iowait的值不够准确(或者说跟直觉不相符),比如:

  1. CPU不会等IO结束,而是会调度到可以运行的其他任务。 例如:单核CPU,只有一个任务A,50%的时间等待IO,50%的时间使用CPU计算,那么iowait是50%; 如果另外还有一个任务B,任务B一直使用CPU,没有IO操作,那么CPU会在任务AB间切换,iowait变成了0,但实际上任务A还是有一半的时间在等待IO。
  2. 多核CPU系统中,等待IO的任务没有在任何CPU上执行,等IO ready之后它可能会被调度到不同于之前的CPU上运行,所以单个CPU的iowait不太有用。
  3. 在某些情况下iowait的值会变小。(不太清楚具体情况,可能在多核下有race等情况,例如https://lkml.indiana.edu/hypermail/linux/kernel/1303.2/01398.html)
  4. iowait高也不意味这系统一定有问题。例如执行dd等主要做IO的程序时,可能看到的iowait非常高。

kernel如何获取CPU状态

linux的进程调度器需要管理CPU和进程状态。task_struct包含in_iowait字段,指示进程的iowait状态,进程调度时更新相关字段。 cpu_usage_stat记录CPU的使用情况,内核每次执行时钟中断处理中,会对CPU的使用情况进行统计。

cpu_usage_stat的相关统计是从开机累加的,所以像vmstat, iostat等工具一定间隔获取并计算可以得到过去一段时间的使用统计。

进程具体在什么情况下会被认为是等待IO完成,可能无法一概而论。大致来说,iowait指的是文件IO,包括对文件系统的读写,对磁盘块的读写等。一般来说,这些进程会处于D状态。 一般情况下网络(socket),pipe, tty等IO不计入iowait,但是访问网络文件系统,如nfs是会计入的。

系统负载

简单来说,Linux下,系统负载不止包含可运行的任务,也包含处于uninterruptible sleep状态(D)的任务。Linux试图给出整体系统负载而不只是CPU负载。 可以使用uptime等命令查看1, 5, 15分钟的平均负载。

反过来,当系统负载高时,也不必然意味着CPU不够用,需要结合其他指标综合来看。

Linux Uninterruptible Tasks

kernel中使用不可中断状态(TASK_UNINTERRUPTIBLE)来避免响应signal,也就是处于D状态的进程无法通过kill等命令杀死,只能等任务满足条件后回到可运行状态。 使用不可中断状态的主要是disk IO(例如direct IO 以及buffered IO中与disk交互的部分)和一些lock。

Linux IO - 阻塞/非阻塞 同步/异步

这几个词在不同的上下文/领域中的意思是不一致的。

阻塞/非阻塞用于IO的情况比较多。比如read()系统调用,如果fd没有设置O_NONBLOCKflag,就会阻塞当前执行的进程,否则直接返回EAGAGIN/EWOULDBLOCK。 而同步本质上是指多个实体之前有互相协调,异步反之。从单一实体的角度来看,异步调用后直接返回,不等待结果。因此异步通常有相关的机制来获取结果(达到同步的效果)。 例如在Linux IO中,异步读是指调用立刻返回,而不等数据实际读取到,即使当时就有数据可读。

Linux的异步IO可能特指aio或者io_uring

IO 多路复用与io_uring

各种讲select,poll, epoll机制与实现的文章有很多,本文不再赘述。他们都是使用类似的思路解决如何实现高性能IO的问题。解决思路就是通过事件通知机制。 当进程需要同时访问多个IO时,可以不必阻塞/同步地挨个等待每个IO,而是当某个IO可用时,再去访问。比如服务器应答多个客户端等场景。 本质上是通过设置NONBLOCK flag + 内核事件通知来实现。

io_uring是linux在aio之后引入内核,提出/实现了一个通用且优雅的异步系统调用方案,而不只是像epoll用于网络,aio用于文件的受限方案。

Reference: