Unix下的5种I/O模型:
1. 阻塞式I/O
2. 非阻塞式I/O
3. I/O复用(select/poll/epoll)
4. 信号驱动式(SIGIO)
5. 异步I/O(POSIX的aio_系列函数)
一个输入操作通常包括两个不同的阶段:
(1)等待数据准备好
(2)从内核向进程复制数据
对于一个套接字上面的输入操作,第一步通常是等待数据从网络中到达,当等待的分组到达时,被复制进内核的某个缓冲区,第二步就是把数据从内核缓冲区复制到相应的应用进程缓冲区。
一.五种I/O模型
下面以数据报套接字(UDP)为例子,来分析这五种IO模型,之所以不使用TCP作为例子是因为UDP比较简单,TCP本身比较复杂一些:
1> 阻塞式I/O模型(blockingI/O)
阻塞式I/O是最流行的I/O模型。默认情况下所有的套接字都是阻塞的。
如图6-1,进程调用recvfrom,其系统调用直到数据报到达且被复制到相应的应用进程的缓冲区中或者发生错误才返回,最常见的错误是系统调用被信号中断。在图中,进程在从调用recvfrom开始到它返回的这段时间内是阻塞的。
2>非阻塞式I/O模型(nonblocking I/O)
进程把一个套接字设置成非阻塞式I/O是在通知内核:当请求的I/O操作非得把本进程投入睡眠时,不要把本进程投入睡眠而是返回一个错误。
在上图中,前三次recvfrom没有准备好数据,此时直接返回一个EWOULDBLOCK错误,第四次时已经有数据准备好,它被复制到相应进程缓冲区。
3> I/O复用模型
有了I/O复用,我们就可以调用select或者poll,阻塞在这两个系统调用的某一个之上,而不是阻塞在真正的I/O系统调用上。
在上图中我们阻塞在select调用上,等数据报套接字变为可读时,我们调用recvfrom把数据报复制到进程缓冲区。
4> 信号驱动式I/O模型(signal-driven I/O)
我们首先开启套接字的驱动式I/O功能,并通过sigaction系统调用安装一个信号处理函数。该系统调用立即返回,进程继续工作。当数据报数据准备好时,内核为该进程产生一个SIGIO信号。我们可以在信号处理函数中调用recefrom函数读取数据报。
5>异步I/O模型
异步I/O的工作机制是: 告知内核启动某个操作,并让内核在整个操作(包括将数据从内核复制到进程缓冲区)完成后通知我们。
这与信号驱动模型的主要区别在于:信号驱动式I/O是由内核通知我们何时可以启动一个I/O操作,而异步I/O模型是有内核通知我们I/O何时完成。
在上图中,我们调用aio_read函数,并告诉内核当整个操作完成之后通知我们。该系统调用立即返回,而且在等待I/O完成期间,进程并不阻塞。
二.各种I/O模型的比较
上图中我们可以看出:
前四种I/O模型的主要区别在第一阶段,第二阶段是一样的:在数据从内核复制到进程缓冲区期间,进程阻塞于recvfrom调用。
异步I/O模型在这两个阶段都要处理,不同于其他几个模型。
异步I/O操作:导致进程阻塞,直到I/O操作完成;
异步I/O操作:不导致进程阻塞;
阻塞式I/O,非阻塞式I/O,I/O复用模型,信号驱动式I/O模型都是同步I/O。异步I/O模型是异步I/O。