彻底搞懂 IO 模型

181 阅读3分钟

Unix系统中有五种IO 模型:

  1. BIO
  2. NIO
  3. 多路复用
  4. 信号驱动 IO
  5. AIO

IO 操作发生在用户程序空间和内核空间之间。用户程序发起 IO 请求后,操作系统内核态完成真正的数据读取,读取分为两个阶段,分别是等待数据阶段、复制数据阶段。

IO 模型有两个维度,同步/异步和阻塞/非阻塞

同步描述了应用程序与内核进行 IO 操作时的协作方式,着重体现了操作流程的顺序和对应用程序结果的等待。同步意味着,应用程序需要等待该操作完成、获取到操作结果之后,才能进行后面的操作。 那么在等待期间,应用程序是否可以执行其他任务呢?这就是阻塞和非阻塞。程序挂起、不允许执行其他任务就是阻塞,反之为非阻塞。

回到 IO 操作本身,我们定义了三个操作:

A: 应用程序发起读请求

B:等待数据

C:复制数据

我们从同步和阻塞两个维度去讨论:

  1. 应用程序发起读请求后挂起,不再处理其他任务(阻塞);等待数据准备完成之后,应用线程复制数据。操作有严格的顺序,因此为同步。这就是同步阻塞模型。
  2. 应用程序发起读请求后不挂起,转而处理其他任务(非阻塞),每隔一段时间询问请求结果(轮询);等待数据准备完成后,应用程序复制数据。这就是同步非阻塞模型。
  3. 应用程序发起读请求后不挂起,转而处理其他任务(非阻塞),且不再轮询结果。这时有一个线程,可以同时检测多个 IO 通道的状态,当任何一个通道有事件发生(可读、可写)时,通知应用程序复制数据。这就是多路复用模型。
  4. 应用程序发起读请求后不挂起,向内核注册一个信号处理函数,转而处理其他任务(非阻塞),且不再轮询结果;数据准备完成之后,内核向应用程序发送信号,应用程序复制数据。这就是信号驱动 IO。
  5. 应用程序发起读请求后不挂起,向内核注册一个信号处理函数,转而处理其他任务(非阻塞),且不再轮询结果。内核会在 I/O 操作全部完成(包括数据从外部设备传输到内核空间,再从内核空间复制到用户空间)后,通过回调函数等方式通知应用程序操作结果。从定义上看,应用程序发起读请求后,可以跳过等待数据和复制数据过程,直接进行其他操作,改变了操作的顺序,因此为异步。这就是异步IO 模型。

多路复用 IO 和信号 IO 对比:

  • 不同点:多路复用是通过其他线程实现的,而信号 IO 是通过内核信号实现的。

  • 相同点:两者都是同步模型;都避免了应用程序轮询状态,节省了 cpu 资源。

多路复用 IO 和 AIO 对比:

  • 不同点:复制数据的处理方不同,AIO 是内核,多路复用 IO 是应用程序。