五种IO模型

五种I/O模型

  • 阻塞I/O
  • 非阻塞I/O
  • 信号驱动I/O
  • 多路转接(多路复用) I/O
  • 异步I/O

前面四种属于同步IO模型。


预备知识
I/O的两个步骤:

  • (1)等待I/O事件就绪。
    就是等待内核将数据准备好
  • (2)执行I/O操作。
    将数据从内核缓冲区读入用户缓冲区,或者将数据从用户缓存区写入内核缓冲区。所谓的读入和写入本质是将数据拷贝过来、拷贝过去。

阻塞的文件描述符称为阻塞I/O;
I/O阻塞执行的系统调用,如果相关事件没有就绪,意味着不能被立即完成,此时就会被操作系统挂起,直到被等待事件就绪。
在这里插入图片描述

  • 如果内核还没有将数据准备好,系统调用仍然会直接返回,并且设置EWOULDBLOCK错误码。

所有的套接字创建的时候默认都是阻塞的,套接字API中,可能被阻塞的系统调用包括accept、send、recv、connect。


非阻塞的文件描述符称为非阻塞I/O;
非阻塞I/O执行的系统调用,不管相关事件是否发生,总是立即返回。往往需要程序员使用循环的方式返回尝试读写文件描述符,这个过程称为轮询。
在这里插入图片描述
如果等待的事件没有立即发生,这些系统调用会以出错的形式返回的。其实它不是真正地调用失败,事件未就绪真正的调用出错都是以出错的形式返回,区分这两个情况的关键在于错误码:

  • 非阻塞I/O事件未就绪设置错误码
    (1)accept、send、recv而言,错误码通常被设置为EAGAIN(“再来一次”)/EWOULDBLOCK(“期望阻塞”)。
    (2)connect而言,错误码通常被设置为EINPROGRESS(“在处理中”)。

显然,非阻塞I/O很容易浪费CPU资源,通常不单独使用,而是和其他I/O通知机制一起使用


信号驱动I/O
可以为目标文件描述符指定宿主进程,这个进程将捕获到SIGIO信号。当内核将该目标文件描述符上的IO事件准备就绪时,会使用SIGIO信号通知应用进程进行I/O操作,然后SIGIO信号的信号处理函数会被触发。我们可以在SIGIO信号处理函数内部对目标文件描述符执行非阻塞I/O操作。
在这里插入图片描述


多路复用I/O
应用进程通过I/O复用函数向内核注册一组事件,能够同时等待多个文件描述符的I/O事件就绪,内核通过I/O复用函数将其中就绪的事件通知给应用进程。
在这里插入图片描述
Linux上常用的I/O复用函数:select、poll、epoll_wait。

注意:

  • 程序阻塞于I/O复用系统调用,而对I/O本身的读写操作是非阻塞的。
  • 多路复用是同步I/O中最高效的I/O模型,原因在于它们具有同时监听多个I/O事件的能力。

异步I/O
内核在数据拷贝完成后,通知应用程序(信号驱动是告诉应用程序何时开始拷贝数据)。
在这里插入图片描述

同步I/O与异步I/O的区别:

  • 同步I/O要求用户自行执行I/O操作。内核将数据准备好后,由用户自己进行读写操作。
  • 异步I/O由内核执行I/O操作。用户直接执行I/O操作,会告诉内核用户读写缓冲区的位置,以及I/O操作完成后内核通知应用程序的方式,接下来的I/O操作就交由内核接管。

Linux的aio.h头文件中定义了支持异步I/O的函数。


I/O模型阻塞阶段
阻塞I/O阻塞于读写函数
非阻塞I/O无阻塞阶段
信号驱动I/O无阻塞阶段
多路复用I/O阻塞于I/O复用系统调用,对I/O本身的读写操作是非阻塞的
异步I/O无阻塞阶段

版权声明:本文为qq_56870066原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。