flame

路漫漫其修远兮 吾将上下而求索

0%

I/O模型

前言

本文是UNIX领域5种常见的I/O模型的总结,这5种I/O模型是:

  • 阻塞式I/O模型

  • ⾮阻塞式I/O模型

  • I/O复⽤模型

  • 信号驱动式I/O模型

  • 异步I/O模型

函数recvfrom约定

为了方便描述, 把recvfrom函数抽象为某种执行I/O操作的系统调⽤api , 视为应用程序和内核的交互 (系统调用会从在应⽤进程空间中运⾏切换到在内核空间中运⾏,⼀段时间之 后再切换回来)


阻塞式I/O模型

阻塞式I/O模型

进程调⽤recvfrom ,数据报到达且被复制到应⽤进程的缓冲区中或者发⽣错误时, 系统调⽤才返回。


非阻塞式I/O模型


非阻塞式I/O模型

进程会把⼀个套接字设置成⾮阻塞, 是在通知内核:当所请求的I/O操作没有完成时,不要把本进程投⼊睡眠,⽽是返回⼀个错误。

前三次recvfrom调⽤是应⽤进程轮询(polling)调用, 因为没有数据可返回,因此内核转⽽⽴即返回⼀个错误。第四次调⽤recvfrom时已有⼀个数据报准备好, 它被复制到应⽤进程缓冲区,于是recvfrom成功返回, 进程可以接着处理数据


I/O复⽤模型

I/O复用模型

进程阻塞于select调⽤,等待数据报套接字变为可读。 当select返回套接字可读这⼀条件时,进程调⽤recvfrom把所读数据报复制到应⽤进程缓冲区。

对比阻塞式I/O模型,I/O复用模型的劣势是使⽤select需要两个⽽不是单个系统调⽤,优势在于我们可以等待多个描述符就绪。

另外,类似于select的系统调用还有poll、epoll

信号驱动式I/O模型

使⽤信号让内核在描述符就绪时发送信号通知进程,这种模型被称为信号驱动式I/O模型

信号驱动式I/O模型

程序⾸先开启套接字的信号驱动式I/O功能,并通过系统调⽤安装⼀个信号处理函数,该系统调⽤将⽴即返回,进程无阻塞地继续⼯作。

内核会在数据报准备好被读取时,为进程产⽣⼀个SIGIO信号,进程随后可以进行处理数据报的读取操作。这个操作既可以在信号处理函数中调⽤recvfrom读取数据报,并通知主循环数据已准备好待处理, 也可以⽴即通知主循环,让它读取数据报。

这种模型的优势在于等待数据报到达期间进程不被阻塞。

异步I/O模型

异步I/O模型

异步IO函数的⼯作机制是:告知内核启动某个操作,并让内核在整个操作(包括将数据从内核复制到我们⾃⼰的缓冲区)完成后通知进程

和信号驱动模型的主要区别在于: 信号驱动式I/O是由内核通知我们何时可以启动⼀个I/O操作,⽽异步I/O模型是由内核通知我们 I/O操作何时完成。

5种I/O模型的⽐较


5种I/O模型的抽象比较

POSIX定义

POSIX把这两个术语定义如下:

  • 同步I/O操作:导致请求进程阻塞,直到I/O 操作完成;

  • 异步I/O操作:不导致请求进程阻塞。

    那么, 根据POSIX的定义, 在阻塞式I/O模型⾮阻塞式I/O模型I/O复⽤模型信号驱动式I/O模型中, 真正的 I/O操作( recvfrom)阻塞了进程, 所以这4种模型是同步I/O。 在异步I/O模型中, 真正的 I/O操作( recvfrom)没有阻塞进程, 所以是异步I/O.

同步和阻塞的区别

一般而言,可以把IO操作看作两步, 第一步是发起IO的请求, 第二步是实际的IO操作

  • 阻塞IO和非阻塞IO的区别在于第一步操作是否阻塞 :

    如果发起的IO请求阻塞到一直到请求完成, 是传统的阻塞IO。

    如果发起的IO请求不阻塞,是非阻塞IO。

  • 同步IO和异步IO的区别在于第二步操作是否阻塞:

    如果实际的IO操作阻塞了进程,就是同步IO。阻塞式I/O模型⾮阻塞式I/O模型I/O复⽤模型信号驱动式I/O模型 都是同步IO.

    如果实际的IO操作不阻塞进程, 而是操作系统帮用户做完实际的IO操作后再将结果返回给用户, 那么就是异步IO。异步I/O模型属于异步I/O.