非阻塞IO与异步IO


本文是自己对最近学习到的IO相关知识的一点整理,之后会逐渐增加。

首先非阻塞IO(non-blocking IO)相信很多人都听说过,比如Nginx,Redis,NodeJS等等。得到的印象大多是非阻塞IO比传统IO(blocking IO)要好。这里多少有点误解。

非阻塞IO的目的是高并发,比如C10K这种目标。在连接数不高的时候性能并不会比传统IO好。为什么传统IO难以做到C10K,主要原因还是可以建立的进程/线程数量有限,以及高并发情况下IO等待时间太多,阻塞进程/线程运行等原因。

当然非阻塞IO并不是完全非阻塞的,IO通常分为数据等待和数据从内核空间拷贝到用户空间的两部分,传统IO(阻塞IO)在这两个步骤都会阻塞,但是非阻塞IO只在数据拷贝的时候阻塞,数据等待时系统通常会返回一个特定的异常码来提示数据未准备好。

非阻塞IO是一个行为特征,具体实现有select/poll,Linux 2.6之后的epoll等IO多路复用的系统调用。直观上来讲,非阻塞IO和IO多路复用没有关系,但是如果你一直在某个fd(文件描述符)上轮询的话,就会变得和传统IO没有区别,所以一般都是在多个fd上等待,当其中某个数据就绪了再取数据,这样就可以体现在非阻塞IO在数据等待这一步非阻塞的优势了。

从编程语言来说,非阻塞也可以选择用注册callback异步调用来实现。实际的IO类型中也有异步IO,但是大部分人都不怎么谈到异步IO,说非阻塞IO其实就是指epoll,其原因之一是Linux上AIO(异步IO)实现差强人意,Windows上IO多路复用只有select,剩下的就是异步IO,即IOCP,Windows貌似希望开发人员使用IOCP而不是开发类似epoll在Windows上的实现。服务器端开发,你懂的,Windows的用武之地很少。

IO多路复用为什么epoll脱颖而出,主要还是他的数据通知机制。select虽然大部分平台都支持,但是fd有数量限制,1024个。poll消除了这个限制,但是得到数据就绪的响应之后,你必须遍历庞大的fd列表来得到就绪的fd。epoll的优势在于返回的响应中包含数据就绪的fd,综合各方面来说是最优的。

当然epoll是Linux的系统调用,如果在Windows上你只能用select,在Solaris上你需要/dev/poll,OS X是kqueue,通常编程语言会帮你统一API,比如Java。但是关于非阻塞IO的基本知识建议还是要了解一下。

参考: