Linux下5种IO模型的小结
概述
接触网络编程,我们时常会与各种与IO相关的概念打交道: 同步(Synchronous)、异步(ASynchronous)、阻塞( blocking )和非阻塞(non-blocking) 。关于概念的区别在知乎上看到一位朋友( 链接 )打了一个比较形象的比喻:
你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下",然后开始查啊查,等查好了(可能 是5秒,也可能是一天)告诉你结果(返回结果)。而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然 后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。
你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果。如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。
在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。
同步与异步的主要区别就在于: 会不会导致 请求进程(或线程) 阻塞 。同步会使请求进程(或线程)阻塞而异步不会。
linux下有五种常见的IO模型,其中只有一种异步模型,其余皆为同步模型。如图:
阻塞IO模型
阻塞IO模型是最常见的IO模型了,对于所有的“慢速设备”(socket、pipe、fifo、terminal)的IO默认的方式都是阻塞的 方式。阻塞就是进程放弃cpu,让给其他进程使用cpu。进程阻塞最显著的表现就是“进程睡眠了”。阻塞的时间通常取决于“数据”是否到来。
非阻塞IO模型
非阻塞IO就是设置IO相关的系统调用为non-blocaking,随后进行的IO操作无论有没有可用数据都会立即返回,并设置errno为 EWOULDBLOCK或者EAGAIN。我们可以通过主动check的方式(polling,轮询)确保IO有效时,随之进行相关的IO操作。当然这种 方式看起来就似乎不太靠谱,浪费了太多的CPU时间,用宝贵的CPU时间做轮询太不靠谱儿了。图示:
多路复用IO模型
多路复用是让 阻塞 发生在我们的多路复用IO操作的系统调用上面,而不是我们真正去执行IO的系统调用。使用这个方式的好处就是可以同时监控多个用于IO的文件描述符。详细的使用方式之前写了一篇博客有提到: http://www.cnblogs.com/ittinybird/p/4574397.html
信号驱动IO模型
所谓信号驱动,就是利用信号机制,安装信号SIGIO的处理函数(进行IO相关操作),通过监控文件描述符,当其就绪时,通知目标进程进行IO操作(signal handler)。具体使用方法之前博客也有说明: http://www.cnblogs.com/ittinybird/p/4574397.html
异步IO模型
Linux上异步IO有一组POSIX规定的接口,已aio开头的几个SYSCALL。如下:
int aio_read(struct aiocb *aiocbp); int aio_write(struct aiocb *aiocbp); ssize_t aio_return(struct aiocb *aiocbp); 使用时记得 Link with -lrt.
参数看起来给人一种很简洁的假象。其实相较于其他模型的参数一个也没有少,只是放到了结构体里边了。先看一下struct aiocb这个结构的原型吧,头文件是”aio.h“。
struct aiocb { int aio_fildes; /* File desriptor. */ int aio_lio_opcode; /* Operation to be performed. */ int aio_reqprio; /* Request priority offset. */ volatile void *aio_buf; /* Location of buffer. */ size_t aio_nbytes; /* Length of transfer. */ struct sigevent aio_sigevent; /* Signal number and value. */ /* Internal members. */ struct aiocb *__next_prio; int __abs_prio; int __policy; int __error_code; __ssize_t __return_value; #ifndef __USE_FILE_OFFSET64 __off_t aio_offset; /* File offset. */ char __pad[sizeof (__off64_t) - sizeof (__off_t)]; #else __off64_t aio_offset; /* File offset. */ #endif char __unused[32]; };
其实虽然结构体足够长,其实真正用到的也就前面那几个参数,也没那么复杂。具体用法不再赘述。具体可以参考以为网友的博文: http://blog.csdn.net/tq02h2a/article/details/3825114 。
下图是关于异步IO模型的图示:
参考
aio(7) - Linux manual page http://man7.org/linux/man-pages/man7/aio.7.html
sigevent(7) - Linux manual page http://man7.org/linux/man-pages/man7/sigevent.7.html
5张模型图片出处 《UNIX网络编程卷1》 史蒂文斯