I/O 模型有哪几种?

107 阅读5分钟

1、背景

从TCP发送数据的流程说起

要深入的理解各种IO模型,那么必须先了解下产生各种IO的原因是什么,要知道这其中的本质问题那么我们就必须要知一条消息是如何从过一个人发送到另外一个人的;

以两个应用程序通讯为例,我们来了解一下当“A”向"B" 发送一条消息,简单来说会经过如下流程:

  • 第一步:应用A把消息发送到 TCP发送缓冲区。
  • 第二步: TCP发送缓冲区再把消息发送出去,经过网络传递后,消息会发送到B服务器得TCP接收缓冲区。
  • 第三步:B再从TCP接收缓冲区去读取属于自己的数据。

2、IO模型

  • 1、阻塞式IO(blocking IO)
  • 2、非阻塞式IO(nonblocking IO)
  • 3、 多路复用IO(IO multiplexing)
  • 4、信号驱动IO(signal driven IO)
  • 5、异步IO( asynchronous IO)

2.1、阻塞式IO(blocking IO)

我们把视角切换到上面图中的第三步, 也就是应用B从TCP缓冲区中读取数据。

流程:

  • 1、应用进程向内核发起recfrom读取数据。
  • 2、准备数据报(应用进程阻塞)。
  • 3、将数据从内核负责到应用空间。
  • 4、复制完成后,返回成功提示。

2.2、非阻塞式IO(nonblocking IO)

按照上面的思路,所谓非阻塞IO就是当应用B发起读取数据申请时,如果内核数据没有准备好会即刻告诉应用B,不会让B在这里等待。

定义

  • 非阻塞IO是在应用调用recvfrom读取数据时,如果该缓冲区没有数据的话,就会直接返回一个EWOULDBLOCK错误,不会让应用一直等待中。
  • 在没有数据的时候会即刻返回错误标识,那也意味着如果应用要读取数据就需要不断的调用recvfrom请求,直到读取到它数据要的数据为止。

流程:

  • 1、应用进程向内核发起recvfrom读取数据。
  • 2、没有数据报准备好,即刻返回EWOULDBLOCK错误码。
  • 3、应用进程向内核发起recvfrom读取数据。
  • 4、已有数据包准备好就进行一下 步骤,否则还是返回错误码。
  • 5、将数据从内核拷贝到用户空间。
  • 6、完成后,返回成功提示。
  • 2.3、多路复用IO(IO multiplexing)

定义

进程通过将一个或多个fd传递给select(或者其他IO复用API),阻塞在select操作上,select帮我们侦测多个fd是否准备就绪,当有fd准备就绪时,select返回数据可读状态,应用程序再调用recvfrom读取数据。

2.4、信号驱动IO(signal driven IO)

复用IO模型解决了一个线程可以监控多个fd的问题,但是select是采用轮询的方式来监控多个fd的,通过不断的轮询fd的可读状态来知道是否就可读的数据,而无脑的轮询就显得有点暴力,因为大部分情况下的轮询都是无效的,所以有人就想,能不能不要我总是去问你是否数据准备就绪,能不能我发出请求后等你数据准备好了就通知我,所以就衍生了信号驱动IO模型。

于是信号驱动IO不是用循环请求询问的方式去监控数据就绪状态,具体如下:

  • 1、调用sigaction时候建立一个SIGIO的信号联系,
  • 2、当内核数据准备好之后再通过SIGIO信号通知线程数据准备好后的可读状态
  • 3、当线程收到可读状态的信号后,此时再向内核发起recvfrom读取数据的请求,因为信号驱动IO的模型下应用线程在发出信号监控后即可返回,不会阻塞
  • 4、所以这样的方式下,一个应用线程也可以同时监控多个fd。

定义
首先开启套接口信号驱动IO功能,并通过系统调用sigaction执行一个信号处理函数,此时请求即刻返回,当数据准备就绪时,就生成对应进程的SIGIO信号,通过信号回调通知映应用线程调用recvfrom来读取数据。

2.5、异步IO( asynchronous IO)

通过观察我们发现,不管是IO复用还是信号驱动,我们要读取一个数据总是要发起两阶段的请求,第一次发送select请求,询问数据状态是否准备好,第二次发送recevform请求读取数据。(这也就是为什么上面四种都是同步IO

  • 在IO模型里面如果请求方从发起请求到数据最后完成的这一段过程中都需要自己参与,那么这种我们称为同步
  • 如果应用发送完指令后就不再参与过程了,只需要等待最终完成结果的通知,那么这就属于异步

定义
应用告知内核启动某个操作,并让内核在整个操作完成之后,通知应用,这种模型与信号驱动模型的主要区别在于,信号驱动IO只是由内核通知我们合适可以开始下一个IO操作,而异步IO模型是由内核通知我们操作什么时候完成。

总结:
异步IO的优化思路是解决了应用程序需要先后发送询问请求、发送接收数据请求两个阶段的模式,在异步IO的模式下,只需要向内核发送一次请求就可以完成状态询问和数拷贝的所有操作。