首先我们来看一下这五种I/O分别是什么:阻塞I/O,非阻塞I/O,I/O复用,信号驱动I/O,异步I/O

   在来看下它们的执行过程(除异步I/O):都是分为两步,首先是等待数据准备(除信号驱动IO外,其它几种都是自己在等待),当数据准备好之后再有内核拷贝到我们的用户空间。

   1. 阻塞I/O模型  

应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好。

如果数据没有准备好,一直等待。 

数据准备好了,从内核拷贝到用户空间  

IO函数返回成功指示

wKiom1eZ0N-AD_vbAACtZSCarkE727.png-wh_50

   2. 非阻塞I/O模型  

我们把一个套接口设置为非阻塞就是告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠(或挂起),而是返回一个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间。其中红色圈出来的过程就是不断在测试数据是否准备好了,当我们看到数据准备好,则从内核拷贝到用户,然后返回成功。 

 wKiom1eZ0Xby_vNSAAEe93ytXSs803.png-wh_50

   3. I/O复用  

    I/O复用模型会用到select或者poll函数,这两个函数也会使进程阻塞,但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。

   上层应用程序调用select(该机制由Linux内核支持,避免了Application3忙等待),进行轮询文件描述符的状态变化。当select管理的文件描述符没有数据(或者状态没有变化时),上层应用程序也会阻塞;select机制可以管理多个文件描述符;select可以看成一个管理者,用select来管理多个IO。

一旦检测到的一个I/O或者多个IO,有我们监视的事件发生时,select函数将返回,返回值为检测到的事件个数,进而可以利用select相关API函数,操作具体事件;select函数可以设置等待时间,避免了上层应用程序长期僵死;和阻塞IO模型相比,selectI/O复用模型相当于提前阻塞了。等到有数据到来时,再调用recv就不会发生阻塞。



wKiom1eZ0rmDLBvDAAC5jAzCcv8419.png-wh_50

    4. 信号驱动I/O模型

    首先我们允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。、

    这种用于模型用的比较少,属于典型的“拉模式(上层应用被动的去Linux内核空间中拉数据)”。即:上层应用需要调用recv函数把数据拉进来,会有时间延迟,我们无法避免在延迟时,会又有新的信号的产生。

wKiom1eZ1LWD7g3KAADm6kwMOeI455.png    5. 异步I/O模型  

    调用aio_read函数,告诉内核描述字,缓冲区指针,缓冲区大小,文件偏移以及通知的方式,然后立即返回。当内核将数据拷贝到缓冲区后,再通知应用程序。 

说明1:上层应用程序调用aio_read函数,同时提交一个应用层的缓冲区buf;调用完毕后,不会阻塞。上层应用程序可以继续其他任务。

说明2:当TCP/IP协议缓冲区有数据时,Linux主动的把内核数据copy到用户空间。然后再给上层应用发送信号;告诉上层应用数据到来,需要处理!

说明3:典型的“推模式”

说明4: 效率最高的一种模式,上层应用程序Application5有异步处理的能力(在Linux内核的支持下,处理其他任务的同时,也可支持IO通讯)。

wKioL1eZ1ofDktOQAAC7QcGwpYc652.png


下来我们来了解一些关于阻塞与非阻塞的,及同步与异步的相关知识。

    1. 阻塞与非阻塞

    阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态,阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。 

    2.同步与异步 

    同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)所谓同步,就是在发出一个*调用*时,在没有得到结果之前,该*调用* 就不返回。但是一旦调用返回,就得到返回值了。 换句话说,就是由*调用者*主动等待这个*调用*的结果。而异步则是相反,*调用*在发出之后,这个调用就直接返回了,所以没有返回结果。 换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在*调用*发 出后,*被调用者*通过状态、通知来通知调用者,或通过回调函数处理这个调用。

a. 同步I/O操作引起请求进程阻塞,直到I/O操作完成。 
   异步I/O操作不引起请求进程阻塞。 
b. 我们的前四个模型都是同步I/O,只有最后一个异步I/O模型是异步I/O。