前言
本文是UNIX领域5种常见的I/O模型的总结,这5种I/O模型是:
阻塞式I/O模型
⾮阻塞式I/O模型
I/O复⽤模型
信号驱动式I/O模型
异步I/O模型
函数recvfrom约定
为了方便描述, 把recvfrom函数抽象为某种执行I/O操作的系统调⽤api , 视为应用程序和内核的交互 (系统调用会从在应⽤进程空间中运⾏切换到在内核空间中运⾏,⼀段时间之 后再切换回来)
阻塞式I/O模型
进程调⽤recvfrom ,数据报到达且被复制到应⽤进程的缓冲区中或者发⽣错误时, 系统调⽤才返回。
非阻塞式I/O模型
进程会把⼀个套接字设置成⾮阻塞, 是在通知内核:当所请求的I/O操作没有完成时,不要把本进程投⼊睡眠,⽽是返回⼀个错误。
前三次recvfrom调⽤是应⽤进程轮询(polling)调用, 因为没有数据可返回,因此内核转⽽⽴即返回⼀个错误。第四次调⽤recvfrom时已有⼀个数据报准备好, 它被复制到应⽤进程缓冲区,于是recvfrom成功返回, 进程可以接着处理数据
I/O复⽤模型
进程阻塞于select调⽤,等待数据报套接字变为可读。 当select返回套接字可读这⼀条件时,进程调⽤recvfrom把所读数据报复制到应⽤进程缓冲区。
对比阻塞式I/O模型,I/O复用模型的劣势是使⽤select需要两个⽽不是单个系统调⽤,优势在于我们可以等待多个描述符就绪。
另外,类似于select的系统调用还有poll、epoll
信号驱动式I/O模型
使⽤信号让内核在描述符就绪时发送信号通知进程,这种模型被称为信号驱动式I/O模型
程序⾸先开启套接字的信号驱动式I/O功能,并通过系统调⽤安装⼀个信号处理函数,该系统调⽤将⽴即返回,进程无阻塞地继续⼯作。
内核会在数据报准备好被读取时,为进程产⽣⼀个SIGIO信号,进程随后可以进行处理数据报的读取操作。这个操作既可以在信号处理函数中调⽤recvfrom读取数据报,并通知主循环数据已准备好待处理, 也可以⽴即通知主循环,让它读取数据报。
这种模型的优势在于等待数据报到达期间进程不被阻塞。
异步I/O模型
异步IO函数的⼯作机制是:告知内核启动某个操作,并让内核在整个操作(包括将数据从内核复制到我们⾃⼰的缓冲区)完成后通知进程
和信号驱动模型的主要区别在于: 信号驱动式I/O是由内核通知我们何时可以启动⼀个I/O操作,⽽异步I/O模型是由内核通知我们 I/O操作何时完成。
5种I/O模型的⽐较
POSIX定义
POSIX把这两个术语定义如下:
同步I/O操作:导致请求进程阻塞,直到I/O 操作完成;
异步I/O操作:不导致请求进程阻塞。
那么, 根据POSIX的定义, 在阻塞式I/O模型、⾮阻塞式I/O模型、I/O复⽤模型和信号驱动式I/O模型中, 真正的 I/O操作( recvfrom)阻塞了进程, 所以这4种模型是同步I/O。 在异步I/O模型中, 真正的 I/O操作( recvfrom)没有阻塞进程, 所以是异步I/O.
同步和阻塞的区别
一般而言,可以把IO操作看作两步, 第一步是发起IO的请求, 第二步是实际的IO操作
阻塞IO和非阻塞IO的区别在于第一步操作是否阻塞 :
如果发起的IO请求阻塞到一直到请求完成, 是传统的阻塞IO。
如果发起的IO请求不阻塞,是非阻塞IO。
同步IO和异步IO的区别在于第二步操作是否阻塞:
如果实际的IO操作阻塞了进程,就是同步IO。阻塞式I/O模型、⾮阻塞式I/O模型、I/O复⽤模型和信号驱动式I/O模型 都是同步IO.
如果实际的IO操作不阻塞进程, 而是操作系统帮用户做完实际的IO操作后再将结果返回给用户, 那么就是异步IO。异步I/O模型属于异步I/O.