通用IO模型

本文摘录自《UNIX网络编程卷1》,主要对Unix下常用的I/O模型进行介绍。主要的I/O模型有:

  • 阻塞式I/O(blocking I/O);
  • 非阻塞式I/O(noblocking I/O);
  • I/O复用(I/O multiplexing);
  • 信号驱动式I/O(singal-driven I/O);
  • 异步I/O(asynchronous I/O)

本文中的例子都是以数据报套接字为例,进行的系统调用以读操作为例。

阻塞式I/O模型

阻塞式I/O(blocking I/O)模型,是最为流行的I/O模型。阻塞式I/O是指当进程进行系统调用时,如果需要进行I/O操作而不能立即返回时,那么这个系统调用会一直到数据报到达且被复制到应用进程的缓冲区或者发生错误时才会返回。阻塞式I/O在系统调用和发生返回这两个时间点之间的所有时间都是被阻塞的。系统调用返回后,进程才开始处理数据。

阻塞式I/O模型的整个过程如下图所示:

01-阻塞式I/O模型

非阻塞式I/O模型

非阻塞式I/O(noblocking I/O)模型,和阻塞式I/O模型的应用场景相似。不过与阻塞式I/O模型不同的是,当需要进行I/O操作时,进程会反复进行系统调用,如果没有成功,则直接返回一个错误,直到系统调用成功为止。整个过程叫做轮询(polling)。

非阻塞式I/O模型的整个过程如下图所示:

02-非阻塞式I/O模型

I/O复用模型

I/O复用(I/O multiplexing)模型的一个主要特点是可以同时用来检查多个文件描述符的状态。I/O复用模型和非阻塞式I/O模型十分相似,不过区别在于I/O复用模型改进了非阻塞式I/O模型中的轮询过程。非阻塞式I/O模型中的轮询过程需要进程持续轮询内核状态,以检查某个操作是否处于就绪状态,这往往会耗费大量的CPU时间,而且效率不高。I/O复用模型中可以使用select、poll、epoll等函数,改进了轮询的过程。

I/O复用模型的整个过程如下图所示:

03-I/O复用模型

信号驱动式I/O

信号驱动式I/O(singal-driven I/O)模型,是指可以使用信号,让内核在文件描述符处于就绪状态时发送信号通知进程。这种模式首先要建立信号处理程序,当I/O操作完成,内核就会为进程产生一个信号。随后进程就可以进行系统调用处理数据。这种模式的优势在于在等待数据到达期间(不是内核将数据交给进程)进程不被阻塞。

信号驱动式I/O的整个过程如下图所示:

04-信号驱动式I/O模型

注意: 以上几个模型中均涉及到系统调用,在系统调用时发生了阻塞。我们的例子中都是以读操作为例的,还有其他的操作也会导致阻塞。例如,读操作时,若套接字的接收缓冲区内没有数据可读,则读操作会阻塞;在写操作时,若套接字的发送缓冲区内没有空间写入数据,则写操作也会阻塞。另外还需要注意的一点是,非阻塞I/O模型中的轮询、I/O复用模型中的select/poll、信号驱动I/O模型,虽然不同于阻塞式I/O模型,但是在涉及系统调用(读写操作等)时,都会阻塞。

异步I/O模型

异步I/O(asynchronous I/O)模型的工作机制是:告知内核启动某个操作,并让内核在整个操作(包括将数据从内核复制到进程的缓冲区)完成后通知进程。它和信号驱动I/O模型的主要区别在于:信号驱动I/O是由内核告知进程何时可以 启动 一个操作,而异步I/O模型则是由内核通知进程何时 完成 一个操作。

异步I/O的整个过程如下图所示:

05-异步I/O模型

各种模型的比较

前四种模型的主要区别在于第一阶段(等待数据的过程),第二阶段(内核将数据复制到进程的缓冲区)是一样的。根据POSIX对于同步I/O和异步I/O的定义:

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

由上面的定义可知,前四种模型(阻塞式I/O、非阻塞式I/O,I/O复用,信号驱动I/O)属于同步I/O,最后一种属于异步I/O。

关于五种通用模型的主要区别,见下图:

五种I/O模型的比较


版权声明

Learn Python by Fan Chunke is licensed under a Creative Commons BY-NC-ND 4.0 International License.

Fan Chunke创作并维护的Learn Python博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证

本文首发于Learn Python 博客( http://fanchunke.me ),版权所有,侵权必究。


本文永久链接:http://fanchunke.me/Web编程/通用IO模型/

坚持原创技术分享,您的支持将鼓励我继续创作!