@
aiqier 我想有必要先梳理下几个概念,不然是无法讨论的。如果一问什么是IO复用,回答就是select/poll,那就没法谈了,因为作为poll增强版的epoll也是IO复用模型,只不过edge-trigger为异步提供了可能。
1、什么是IO复用:一次查询可以获取多个文件描述符,是IO复用的典型特征。符合这个描述的都可称为IO复用模型。相对的是accept,一次查询最多得一个结果(你可以称其为IO单用,但实际上没有这样一个名词)。值得注意的是,查询无论阻塞或者非阻塞都可以,看你怎么玩。
在这个前提下,楼主说的阻塞IO+线程,跟IO复用的方式并不是互斥的。并且在使用IO复用模型的时候,“数据好了再通知操作”是很容易实现并且也是目前已经流行起来的用法,否则epoll就没啥意义了——只不过每N个连接(N由backlog决定)需要一次查询。表面上看是在轮询,其实在大量连接请求的情况下已经不再是以往那种受人诟病的轮询等待的方式了。所以这种用法仍然是高效并且容易实现的,相对于复杂的消息模型构建要单纯得多。
2、什么是异步IO:就是非阻塞IO[2],这是同一个概念的两种表达。程序在IO操作未完成的情况下也能继续往下运行而非等待,是其特征,符合这个描述都属于异步IO。
但是我们也看到这样的一个描述非常宽泛,所以可以有许多具体的实现方式,非阻塞式IO复用就是其中一种方式。
还有一种很容易让人混淆的就是“托管异步IO”,一般就简称异步IO,但其实只是异步IO的实现形式之一,比如win下的IOCP,不需要使用者处理传输中断,发出IO请求后只需要等待完成的通知即可。这种方式并不比异步的IO复用高级多少,只不过是隐藏了底层机制,让使用者更方便而已,但对于有把控能力的编程者来说灵活性是大大降低了。
信号驱动(SIGIO)同样是其具体实现的一种。
3、信号驱动的问题:SIGIO实质上是中断驱动,只不过注册了的特定IO中断一定会产生SIGIO信号,所以说信号驱动也没问题。信号的问题在于可能会丢失,请求数密集了你就只能加队列先把事件记录下来然后逐个处理了,本质上跟轮询也没啥区别。即便如此,SIGIO对于TCP而言也就是理论上咱可以玩玩,其实没人真的敢拿来用,UDP还凑合[3]。
轮询也没什么不好的,在Linux里处理高速网络传输使用的是用软中断以类似轮询的方式处理的[4](跟前面描述的队列处理SIGIO是相似的原理),这是个很有趣的事情,事务少的时候使用轮询,当事务变多了人们开始喜欢中断,而事务足够多的时候人们却返回去使用轮询了。曾经有段时间许多人对于green thread也难以理解,好不容易掌握了多线程怎么这个世界开始玩儿单线程了。所以不要对一些东西单纯地贴标签,量变引起质变的时候你会觉得世界变化太快的。
4、没有什么是完美的,这几种模型都是shit,仍然不能解放苦逼的码奴,该你遭的时候你就知道了,人类还需要继续努力...
楼主觉得4和5更好,其实理论到了工程领域就必须要考虑复杂度和稳定性,复杂度高了稳定性就下来了,维护成本也是个问题。为什么这几年流行FP?因为不用费力担心state的变化,谁也不愿意费心费力最后搞出个维护不起的东西。相对来说性能如果可以靠堆硬件和改进算法解决是最好的。恩扯远了...
[1]
https://en.wikipedia.org/wiki/Asynchronous_I/O[2]
http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch25lev1sec2.html[3] 《I’ll Do It Later: Softirqs, Tasklets, Bottom Halves, Task Queues, Work Queues and Timers》. Matthew Wilcox