linux环境下共有5种I/O模型:

        1.阻塞I/O

        2.非阻塞I/O

        3.I/O复用

        4.信号驱动I/O

        5.异步I/O

它们之间的关系如下:

Linux环境下5种I/O模型_Linux

        在说上面的5种I/O模型之前,先谈一下I/O的动作,简单的说,I/O分为2个动作。

                1.等待事件发生

                2.进行数据搬迁

        对于一个sockt上的输入操作,第一步一般是等待数据到达网络,当分组到达时,它被拷贝到内核中的某个缓冲区,第二步是将数据从内核缓冲区拷贝到应用程序缓冲区。

        对于上述两个动作而言,其实第二步都是相似的,即数据搬迁动作,5种I/O模型的主要区分在于第一种动作,即等待方式的不同。下面我们来分别介绍上面提到的5种I/O模型

1.阻塞I/O

Linux环境下5种I/O模型_Linux_02

        阻塞I/O模型是最常见的I/O模型了,简单的说,就是应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好。如果数据没有准备好,一直等待。数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。

2.非阻塞I/O

Linux环境下5种I/O模型_Linux_03

        非阻塞IO就是设置IO相关的系统调用为non-blocaking,随后进行的IO操作无论有没有可用数据都会立即返回,并设置errno为EWOULDBLOCK或者EAGAIN。我们可以通过主动check的方式(polling,轮询)确保IO有效时,随之进行相关的IO操作。 
这种方式有一个很大的缺点就是浪费太多的CPU时间用在做轮询上。 

3.I/O复用

     I/O复用能让一个或多个I/O条件满足(例如,输入已经准备好被读,或者描述字可以承接更多的输出)时,我们就被通知到。I/O复用由select和poll支持,较新的Posix.1g也支持(pselect)。
I/O复用典型地用在下列网络应用场合:
    1.当客户处理多个描述字时,必须使用。
    2.一个客户同时处理多个sockt.
    3.如果一个服务器既要处理监听sockt,又要处理连接sockt,一般也用到。
    4.如果一个服务器既要处理TCP,又要处理UDP,一般也用到。
    5.如果一个服务器要处理多个服务或多个协议(例如inetd守护进程),一般也用到。

    I/O复用并非限于网络编程,许多正是应用程序也需要使用这项技术。

    有了I/O复用,我们就可以调用select或poll,在这两个系统调用中的某一个上阻塞,而不阻塞于真正的I/O系统调用。下图是I/O复用模型的一个小结。



Linux环境下5种I/O模型_Linux_04


      我们阻塞于select调用,等待数据报socket可读,当select返回socket可读条件时,我们调用recvfrom将数据报拷贝到应用缓存区中。
    将上图与阻塞I/O比较,似乎没有显示什么优越性,实际上因使用了select,要求2此系统调用而不是一次,好像变的还有点差,但是select的好处在于我们可以等待多个描述字准备好。

多路复用是让阻塞发生在我们的多路复用IO操作的系统调用上面,而不是我们真正去执行IO的系统调用。使用这个方式的好处就是可以同时监控多个用于IO的文件描述符。 

4.信号驱动I/O 

Linux环境下5种I/O模型_Linux_05

所谓信号驱动,就是利用信号机制,安装信号SIGIO的处理函数(进行IO相关操作),通过监控文件描述符,当其就绪时,通知目标进程进行IO操作(signal handler)。 

 5.异步I/O

Linux环境下5种I/O模型_Linux_06

这个目前了解较少,暂不讨论,不过大家可以根据上面的示意图来大致了解。