这里有必要先解释一下阻塞、非阻塞,同步、异步、I/O的概念。
(1).阻塞和非阻塞:
阻塞和非阻塞指的是执行一个操作是等操作结束再返回,还是马上返回。比如你去车站接朋友,这是一个操作。可以有两种执行方式。第一种,你这人特实诚,老早就到了车站一直等到车来了接到朋友为止。第二种,你到了车站,问值班的那趟车来了没有,“还没有”,你出去逛一圈,可能过会回来再问。第一种就是阻塞方式,第二种则是非阻塞的。我认为阻塞和非阻塞讲得是做事方法,是针对做事的人而言的。
(2).同步和异步:
同步和异步又是另外一个概念,它是事件本身的一个属性。比如老板让你去搬一堆石头,而且只让你一个人干,你只好自己上阵,最后的结果是搬完了,还是你砸到脚了,只有搬完了你才知道。这就是同步的事件。如果老板还给你个小弟,你就可以让小弟去搬,搬完了告你一声。这就变成异步的了。其实异步还可以分为两种:带通知的和不带通知的。前面说的那种属于带通知的。有些小弟干活可能主动性不是很够,不会主动通知你,你就需要时不时的去关注一下状态。这种就是不带通知的异步。
对于同步的事件,你只能以阻塞的方式去做。而对于异步的事件,阻塞和非阻塞都是可以的。非阻塞又有两种方式:主动查询和被动接收消息。被动不意味着一定不好,在这里它恰恰是效率更高的,因为在主动查询里绝大部分的查询是在做无用功。对于带通知的异步事件,两者皆可。而对于不带通知的,则只能用主动查询。
(3).I/O
回到I/O,不管是I还是O,对外设(磁盘)的访问都可以分成请求和执行两个阶段。请求就是看外设的状态信息(比如是否准备好了),执行才是真正的I/O操作。在Linux 2.6之前,只有“请求”是异步事件,2.6之后才引入AIO把“执行”异步化。别看Linux/Unix是用来做服务器的,这点上比Windows落后了好多,IOCP(Windows上的AIO)在Win2000上就有了,呵呵。
(4).总结
Linux上的前四种I/O模型的“执行”阶段都是同步的,只有最后一种才做到了真正的全异步。第一种阻塞式是最原始的方法,也是最累的办法。当然累与不累要看针对谁。应用程序是和内核打交道的。对应用程序来说,这种方式是最累的,但对内核来说这种方式恰恰是最省事的。还拿接人这事为例,你就是应用程序,值班员就是内核,如果你去了一直等着,值班员就省事了。当然现在计算机的设计,包括操作系统,越来越为终端用户考虑了,为了让用户满意,内核慢慢的承担起越来越多的工作,IO模型的演化也是如此。
非阻塞I/O ,I/O复用,信号驱动式I/O其实都是非阻塞的,当然是针对“请求”这个阶段。非阻塞式是主动查询外设状态。I/O复用里的select,poll也是主动查询,不同的是select和poll可以同时查询多个fd(文件句柄)的状态,另外select有fd个数的限制。epoll是基于回调函数的。信号驱动式I/O则是基于信号消息的。这两个应该可以归到“被动接收消息”那一类中。最后就是伟大的AIO的出现,内核把什么事都干了,对上层应用实现了全异步,性能最好,当然复杂度也最高。好了,下面我们就来详细说一说,这几种模式。 |
|