IO多路复用前世今生

之前总结了一篇文章《单服务器高性能》,主要整理了两方面的知识:

一是socket以及IO常识

二是单机高性能模式

你会发现IO知识一般不会单独出现,常会与socket,linux底层相关知识结合出现,所以在学习IO时,总会有很多的背景知识,不然会很吃力。或者不明就理。

这是为什么呢?

与socket关联,应用角度看,是因为发生IO时,数据来源与目的地除了磁盘就是网络了,而网络必谈socket;其次从OS角度看,linux思想就是一切皆是文件,socket也是文件的一种。

与linux关联,上面写了linux一切皆是文件,IO操作对象都是文件;其次IO操作得涉及内核,IO的一切优化都需要得到OS的支持。

所以你会发现,谈到I/O multiplexing、mmap、Zero-copy这些提升IO性能的技术时,都需要OS层面出手,否则很难有大的提升。

其中I/O multiplexing与Reactor经常被搞混,甚至被认为是同一种东西。

我再尝试总结一下I/O Multiplexing的前世今生以及与reactor的关联:

I/O multiplexing

multiplexing概念多用于通信领域,详细可看Multiplexing – Definition – Types of Multiplexing: FDM, WDM, TDM,截取两张图片表示multiplexing技术前后的对比:

在没有multiplexing时,每次通信,一个通道上只能传输一个信号,浪费了带宽

当有了multiplexing技术后,通过设备多路复用器multiplexer(MUX),一个通道可以传输多个信号,带宽最大化利用。在接受端,通过信号分离器demultiplexer(DEMUX),再把多个信号分离开。

通过上面的两张图可以看出,multiplexing技术里面有两个设备比较关键,一是MUX负责多路复用,另一个是DEMUX负责多路分发。

理解了multiplexing,再看看I/O

简要回顾一下:

从最原始的BIO,PPC模式进化到TPC,通过线程池有效控制线程扩张,但线程上下文切换也带来了性能损耗依然不能小觑。关键是IO处理没有丝毫改进。

从图中可以看出,之前block的依然block,之前system call的依然发生,对于成千上万的连接高并发,怎么达到高性能?

怎么办呢?

这一次还是kernal体现了大爱无私的胸怀,在提供了blocking read方法与non-blocking read方法后,又体贴地想出了两种方法

1、不要每次都来问了,一次性打包问吧。把所有的system call整合在一起,一次性打包给system kernal,结合上面的multiplexing技术,I/O multiplexing由此而来。

2、不要总跑来问我了,有情况我通知你吧。对,这就是著名的Hollywood Principle

根据这两个办法,打造出了最终的完美体epoll。当然在这个完美体前还有select、poll两个被丢弃的废品。至于这三者区别,可详看《单服务器高性能》


Reactor pattern

The reactor design pattern is an event handling pattern for handling service requests delivered concurrently by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to associated request handlers.

更详细的介绍可看Reactor An Object Behavioral Pattern for Demultiplexing and Dispatching Handles for Synchronous Events

看一下reactor pattern里面包含的部件:

1.event:
IO event like write, read, timeout and signal.

2.Event Source (ES):
file descriptor (fd) in linux and Socket or Handle in Windows. Program adds the event that it cares on the given event.

3.event_demultiplexer (ED):
select() and epoll() provided by the OS. The program firstly add the event source (fd) and the related events to the ED. When event comes, ED eill notify that the events on one or more EVs are ready where program can process the events in nonblock way. The same ED way like select(), poll() and epoll() are used in Libevent where they are encapsulated by eventop struct.

4.event_handler_interface and event_handler_imp (EH):
EH has a serial of interfaces and each interface refers to a different event.Reactor will invoke certain interface when some certain event fets ready. EH usually binds a valid ES.
In libevent, it is event struct.

5.reactor:
It is the event management. It uses ED internally to add and remove event. When event is ready, invoke the callback function on the event. It is actually event_base struct in libevent.

可以看到在reactor pattern中,有个reactor部件。而还有个部件event_demultiplexer是由多路复用技术支持。

所以在谈到reactor时,是reactor模式,还是reactor部件?也没必要咬文嚼字了,反正他们是一家。

不仅如此,还有一股编程思潮:响应式宣言里面也有reactor

头晕,反正他们都是一家族的。

总结

一堆名词堆一起,看是又不是,是不是剪不断,理还乱。

简单讲:

结合I/O与multiplexing,由此得来I/O multiplexing。

Reactor模式中包含了reactor部件及依赖了多路复用技术。而多路复用技术的底层epoll又吸取了reactor思想。

道生一,一生二。阴阳之道,存乎万物之间。

公众号:码农戏码
欢迎关注微信公众号『码农戏码』