1. I/O 模型
Unix下可用的5种I/O模型:
- 阻塞式I/O;
- 非阻塞式I/O;
- I/O复用(select 和 poll);
- 信号驱动式I/O(SIGIO);
- 异步I/O(POSIX的aio_系列函数)。
一个输入操作通常包括2个不同的阶段:
- 等待数据准备好;
- 从内核向进程复制数据。
对于一个套接字上的输入操作,第一步通常涉及等待数据从网络中到达。当所等待分组到达时,它被复制到内核中的某个缓冲区。第二步就是把数据从内核缓冲区复制到应用进程缓冲区。
1.1 阻塞式I/O模型(blocking I/O)
最流行的I/O模型是阻塞式I/O模型。默认情况下,所有套接字都是阻塞的。
阻塞式I/O模型在这里,把recvfrom函数视为系统调用。进程调用recvfrom,其系统调用直到数据报到达且被复制到应用进程的缓冲区中或者发生错误才返回。进程在从调用recvfrom开始到它返回的整段时间内是被阻塞的。recvfrom成功返回后,应用进程开始处理数据报。
1.2 非阻塞(nonblocking I/O)
进程把一个套接字设置成非阻塞是在通知内核:当所请求的I/O操作非得把本进程投入睡眠才能完成时,不要把本进程投入睡眠,而是返回一个错误。
非阻塞式I/O模型前三次调用recvfrom时没有数据可返回,因此内核转而立即返回一个EWOULDBLOCK错误。
第四次调用recvfrom时已有一个数据报准备好,它被复制到应用进程缓冲区,于是recvfrom成功返回。我们接着处理数据。
当一个应用进程像这样对一个非阻塞描述符循环调用
recvfrom时,我们称之为轮询(polling)
。应用进程持续轮询内核,以查看某个操作是否就绪。这么做往往耗费大量CPU时间。
1.3 I/O 复用模型(I/O multiplexing)
有了I/O复用,我们就可以调用select或poll,阻塞在
这两个系统应用中的某一个上
,而不是阻塞在真正的I/O系统调用上。
我们阻塞于select调用,等待数据报套接字变为可读
。当select返回套接字可读这一条件时,我们调用recvfrom把所读数据报复制
到应用进程缓冲区。
比较图6-3和6-1,I/O复用并不显得有什么优势,事实上由于使用select需要两个而不是单个系统调用,I/O复用还稍有劣势。不过,使用select的优势在于
我们可以等待多个描述符就绪。
1.4 信号驱动式I/O模型(signal-driven I/O)
我们也可以使用信号,让内核在描述符就绪时发送SIGIO信号给我们。称这种模型为信号驱动式I/O。
信号驱动式I/O模型- 首先开启套接字的信号驱动式I/O功能,并通过 sigaction 系统调用安装一个信号处理函数。该系统调用将立即返回,我们的进程继续工作,也就是说它没有被阻塞。
- 当数据报准备好读取时,内核就为该进程产生一个SIGIO信号。
- 我们随后既可以在信号处理函数中调用recvfrom读取数据报,并通知主循环数据已准备好待处理,也可以立即通知主循环,让它读取数据报。
无论如何处理SIGIO信号,这种模型的优势在于等待数据报到达期间进程不被阻塞。主循环可以继续执行,只要等待来自信号处理函数的通知:既可以是数据已准备好被处理,也可以是数据报已准备好被读取。
1.5 异步I/O模型(asynchronous I/O,AIO)
由POSIX规范定义。
一般来说,这些函数的工作机制是:告知内核启动某个操作,并让内核在整个操作(包括将数据从内核复制到我们自己的缓冲区)完成后通知我们。
这种模型与信号驱动模型的主要区别在于:
- 信号驱动式I/O:内核通知我们
何时可以启动
一个I/O操作, - 异步I/O模型:内核通知我们I/O操作
何时完成
。
我们调用aio_read函数(POSIX异步I/O函数以aio_或lio_开头),给内核传递描述符
、缓冲区指针
、缓冲区大小
(与read相同的三个参数)和文件偏移(与lseek类似),并告诉内核当整个操作完成时如何通知我们。该系统调用立即返回
,而且在等待I/O完成期间,我们的进程不被阻塞
。
本例子中我们假设要求内核在操作完成时产生某个信号。该信号直到数据已复制到应用进程缓冲区才产生,这一点不同于信号驱动式I/O模型。
至此时,支持POSIX异步I/O模型的系统仍较罕见
。我们不能确定这样的系统是否支持套接字上的这种模型。这儿我们只是用它作为一个与信号驱动式I/O模型相比照的例子。
1.6 各种I/O模型的比较
图6-6 对比了上述5种不同的I/O模型。
可以看出,前4种模型
的主要区别在于第一阶段
,因为它们的第二阶段是一样的
:在数据从内核复制到调用者的缓冲区期间,进程阻塞于recvfrom调用。
相反,异步I/O模型在这两个阶段都要处理,从而不同于其他4种模型。
1.7 同步I/O和异步I/O对比
POSIX把这两个术语定义如下:
- 同步I/O操作(synchronous I/O opetation)导致请求进程阻塞,直到I/O操作完成;
- 异步I/O操作(asynchronous I/O opetation)不导致请求进程阻塞。
根据上述定义,我们的前4种模型——阻塞式I/O模型、非阻塞式I/O模型、I/O复用模型和信号驱动式I/O模型都是同步I/O模型
,因为其中真正的I/O操作(recvfrom)将阻塞进程
。
只有异步I/O模型与POSIX定义的异步I/O相匹配。
5种I/O模型的比较
网友评论