本文是对《UNIX网络编程卷1》第6章的总结。
一、 什么是IO复用?
它是内核提供的一种同时监控多个文件描述符状态改变的一种能力;例如当进程需要操作多个IO相关描述符时(例如服务器程序要同时查看监听socket和大量业务socket是否有数据到来),需要内核能够监控这许多描述符,一旦这些描述符有就绪(或者状态改变了)就告诉主动告诉进程哪些描述符已经就绪,这样站在进程的角度,就不需要挨个的查看每个描述符是否就绪。
二、 IO操作分为两个阶段:
(1) 准备阶段,例如输入操作时要等待数据已经就绪,输出操作时缓冲区中有空间可供使用;
(2) 数据拷贝阶段,例如输入操作时将数据从内核复制到用户缓存,输出操作时将用户缓存复制到内核。
以读取系统调用recvfromfrom为例,每当用户进程发起一个recvfrom操作,站在用户进程的角度,其实它包含上述两个请求:第一个请求就是看看数据是不是准备好了,如果没有准备好就把我阻塞那里(对应阻塞IO)或者给我返回个没有准备好的标志(对应非阻塞IO);第二个请求就是在数据就绪的时候将数据拷贝给用户进程。
三、 Unix环境下有5中IO模型
(1)阻塞IO模型
(2)非阻塞IO模型
(3)IO复用模型
(4)信号驱动IO模型
(5)异步IO模型
其中(1)~(4)都属于同步IO,(5)属于异步IO,因为前4中模型在第一阶段的准备阶段有区别,在第二阶段的数据拷贝过程中都会阻塞用户进程,而(5)在第一阶段的准备阶段和第二阶段的数据拷贝过程都不会阻塞用户进程。
四、 各种模型的详细区别
(1) 阻塞IO模型
阻塞IO模型中,以数据接收为例,进程调用recvfrom系统调用向内核请求读取数据,其内部过程将如下图1所示:
图1阻塞IO模型
由图1可以看到用户进程从发起recvfrom系统调用开始后的两个阶段都被阻塞。
(2) 非阻塞IO模型
以读取数据为例,非阻塞模型中,进程需要先把socket设置为非阻塞模式,这样内核在准备数据时就不会阻塞该系统调用,而是直接返回EWOULDBLOCK,在读取操作时,用户进程一样是调用recvfrom向内核请求数据,其内部过程如下图2所示:
图2 非阻塞模式IO模型
由上图2可以看到非阻塞模式中,在数据准备阶段,系统调用不会被阻塞,但是在数据复制阶段也会被阻塞;因此这里所说的阻塞是指数据准备阶段是否被阻塞,而不是数据拷贝阶段。
(3) IO复用模型
IO复用模型中,进程可以让内核监控多个描述符的就绪状态,一般使用的接口函数为select或者poll,内核通过select查询到所监控的描述符中有就绪的,则把就绪的描述符返回给调用进程,进程再调用recvfrom实际读取数据,其内部过程大致如下图3所示:
图3 、IO复用模型
由上图可以看出,在IO复用模型中,用户进程先调用select或者poll查看是否有数据就绪,如果有数据就绪再调用recvfrom去读取数据,系统在阶段1和阶段2都将会被阻塞,但是阻塞于不同的系统调用(阶段1阻塞于select或者poll,阶段2阻塞于recvfrom)。
(4) 信号驱动IO模型
信号驱动IO模型是让内核在描述符就绪时发送SIGIO信号通知用户进程。在使用信号驱动模型进行IO操作时,首先要打开socket使用信号驱动IO的能力;然后调用sigaction系统调用设置信号SIGIO的处理函数,sigaction系统调用不会被阻塞。这样就可以在信号处理函数中读取数据了,其处理过程如下图4所示:
图4 信号驱动IO模型
由上图4可以看出,信号驱动模型只要设置好信号处理函数后就可以进行其他操作了,当有数据到来时内核将会通知进程进行处理;与非阻塞IO模型相比,信号驱动IO模型不需要在准备数据时轮询是否准备好。
(5) 异步IO模型
异步IO模型中,用户进程发起读取调用之后会立即返回,待内核接收到数据并将数据拷贝到用户进程空间之后,再通知用户进程,其过程如下图5所示:
图5异步IO模型
五、5中IO模型的比较
上述5中IO操作模型的区别可以参见图6所示:
图6、5中IO模型的比较
同步IO:导致请求进程阻塞,直到IO操作完成;
异步IO:不导致请求阻塞;
因此,前面介绍的5中模型中,前4中IO模型都属于同步IO,因为他们第二阶段的复制数据过程将阻塞用户进程,只有第5种才是真正的异步IO模型。
顶