TProactor源码浅析一
开篇
分析TProactor并不是一时心血来潮,而是为了研究它把Reactor分装为Proactor的实现逻辑,在对异步支持不好的平台上实现Proactor封装,便利代码的平台迁移,还是有相当的实用性和意义的。
TProactor是一个开源项目,意在为各种平台Linux/Unix,Windows等提供一个具有统一接口的Proactor模型,而不论该平台是否支持异步IO;支持度广泛,包括IOCP,epoll,select,dev/poll,poll等各种同步I/O模型;POSIX标准定义的异步I/O接口,Linux,Sun等平台实现的异步I/O接口。
这样基于TProactor,就可以在Win32和各种POSIX平台上使用统一的可靠而高性能的Proactor模型来开发网络程序。
TProactor的源代码读起来还算行,但是各类之间的关系也挺复杂的,再加上中断机制,各种配置类型和Leader/Follow线程模型,还是要费不少功夫。
研究TProactor主要是为了学习它是如何使用select等I/O机制模拟实现Proactor模型的,这就是在它支持的所有AIO_Provider中类型为SELECT_Provider的那部分,其它的能忽略就忽略,先抓重点要紧。
TProactor在Win32平台上直接基于ACE实现Proactor模型,在POSIX平台还以自己的方式模拟实现了Proactor模型,对于系统原生支持的异步IO的封装,在各种平台上处理逻辑基本相同,封装也有很多相似之处。
Proactor相关类层次
无论如何,还是要先来看看TProactor的关键组件,它们是(结合下面的类图会更容易理解):
AIO_Dispatcher
– 负责选择合适的AIO_Processor,并且分发完成结果;
AIO_Processor
– 负责活动操作的管理,异步激活,发起、取消操作,等待异步操作完成,并且将完成结果传递给AIO_Dispatcher。
AIO_Processor在运行时创建AIO_Provider的具体实现,来执行低层次的原始接口:start_aio和cancel_aio; AIO_Processor依赖AIO_Provider的 AIO_Wait_Strategy接口来完成“等待操作完成”功能;
AIO_Provider – 它是代表“具体AIO机制”的抽象接口,它定义了低层次的原始接口:start_aio和cancel_aio。并且,它提供了访问AIO_Wait_Strategy接口的方法。
目前TProactor支持的“具体的AIO机制”(AIO_Provider-s)包括:
POSIX_STD_Provider – 基于标准POSIX AIO API.
SUN_Provider – 基于SUN API
SELECT_Provider – 基于select()等多路复用机制模拟POSIX AIO
LINUX_Provider – 基于 Linux内核2.6 API,在2.6内核下,Event_Poll_Strategy(epoll)是AIO_Wait_Strategy的最佳候选;
AIO_Wait_Strategy
– 等待异步完成策略,一个抽象接口,被AIO_Processor用来实现“等待操作完成”功能。 具体实现应该由AIO_Provider 创建或实现,通常AIO_Provider都有自己私有的AIO_Wait_Strategy,但是有些Provider可以使用不止一个AIO_Wait_Strategy. 比如,基于POSIX我们可以使用:
POSIX_STD_Strategy – 基于aio_suspend ()
POSIX_SIG_Strategy – 等待RT signals
POSIX_SGI_Strategy – 基于aio callback函数
AIO_Interrupt_Strategy
– 抽象接口,被AIO_Processor和AIO_Dispatcher在有请求需要中断“等待操作完成”行为的时候使用。具体实现应该由AIO_Wait_Strategy提供或创建。有两个AIO_Interrupt_Strategy可以被任何AIO_Provider使用,并且独立于AIO_Wait_Strategy,他们就是AIO_Interrupt_Pipe_Strategy和AIO_Interrupt_Signal_Strategy。然而, POSIX_SGI_Strategy还能使用自己私有的中断策略。
AIO_Config
– 简单类,允许你在运行时配置你的Proactor。
SELECT_Provider
支持所有的异步操作:
Read_Stream Write_Stream
Read_File Write_File
Read_Dgram Write_Dgram
Accept Connect
所有平台可用,不论是否支持POSIX AIO
可以替换POSIX或SUN provider
可以和POSIX或SUN provider组合使用
通过了不亚于Thread Pool Reactor (TP_Reactor)的性能;
支持一系列具体AIO_Wait_Strategy(select、poll和epoll等等),支持运行时根据平台选择最佳的多路复用机制 (见后面的SELECT_Provider章节)
一个Proactor同时支持两个{AIO_Processor-AIO provider}对(就是代码中的两个对象prime_processor_和second_processor_,类图中也有表示),这样做有两个优点:
1 每个provider支持合适的操作集。比如,一个类型是POSIX_STD_Provider 来完成在流和文件上的读写操作另外一个类型是SELECT_Provider来处理accept, connect数据报操作;
2 如果系统提供的POSIX AIO是不完整的,我们就可以把流操作 (socket/pipe)分配给SELECT_Provider,只是用POSIX_STD_Provider来处理文件的读写(磁盘文件);
类层次图
个人感觉TProactor的封装层次相当复杂,先大概画出主要类的组合继承关系,把基本架构搭起来,便于后续的分析,下面的类图是对Proactor类的封装层次。
类层次简介
再来简单说一下各类是干什么用的,有什么功能,扮演什么角色,鉴于复杂度的考虑,具体接口的功能分析也暂行略过,必要的代码分析将结合在后面的流程分析中。
TRB_Proactor类
首先TProactor导出的Proactor类是TRB_Proactor;
1 使用TRB_Proactor_Impl来进行具体操作,包括I/O时间注册,分发等;
2 使用TIMER_QUEUE来管理定时事件;
其它变量忽略;
TRB_Proactor_Impl类
TRB_Proactor_Impl是一个虚基类,只导出必要的接口;用来Adapt不同平台的I/O机制,POSIX系统的TRB_POSIX_Proactor类和WIN32的TRB_WIN32_Proactor类都派生于它。
TRB_POSIX_Proactor类
TRB_POSIX_Proactor类是所有POSIX系统的Proactor模型的基类,又是一个封装类,这个封装是对不同POSIX平台的不同I/O机制的封装;比如Linux的epoll,poll和NAIO,SUN的I/O异步机制,等等。
它的主要成员变量是:
TRB_POSIX_AIO_Processor类,对象prime_processor_和second_processor_,以及对应的config对象;
TRB_POSIX_AIO_Dispatcher类,对象dispatcher_,每个POSIX_Proactor有一个Dispatcher对象;
Allocator对象,这个是为了优化系统性能,本质就是一个free list,避免多次调用接口分配释和放内存,简单。
TRB_POSIX_Proactor类只做一些公共操作,比如创建异步操作结果对象等,具体的处理都直接丢给了TRB_POSIX_AIO_Dispatcher和TRB_POSIX_AIO_Processor,基本上都是对相应函数的直接调用。比如unregister handle操作:
TRB_POSIX_AIO_ Processor类
这个类更是简单,它又是一个封装类,下面就是真正的I/O机制实现了;主要成员变量是
TRB_POSIX_AIO_Dispatcher &completion_dispatcher_;
TRB_POSIX_AIO_Provider *provider_;
导出的接口也很简单,主要是注册、注销异步事件,发起、取消异步事件请求,如下:
int register_handle (ACE_HANDLE handle, const void *completion_key, int operations);
int unregister_handle (ACE_HANDLE handle);
int start_aio (TRB_POSIX_Asynch_Result * result);
int cancel_aio (ACE_HANDLE h);
它的接口基本都是直接调用provider的相关接口来完成,比如:
TRB_POSIX_AIO_Provider类
说曹操曹操到,TRB_POSIX_AIO_Provider类出场了,这是一个虚基类,接口都是纯虚的,它的作用就是规定,作为一个异步事件反应器你必须要提供什么接口(房地产开发?没钱的不要~~)。
TRB_POSIX_AIO_Emulation_Provider类
别以为这个就是具体epoll,select这些的实现了,还有一层呢,那就是TRB_POSIX_AIO_Emulation_Provider类。要它干什么呢?使用select和epoll这些反应堆来伪装异步,总有些共性嘛,Emulation Provider类就把这些共性提取和定义出来。
直接封装平台的异步IO接口实现的provider有TRB_POSIX_AIO_STD_Provider,TRB_POSIX_AIO_LINUX_NAIO_Provider和TRB_POSIX_AIO_SUN_Provider,它们可能没有子类,直接封装系统的异步I/O实现,但也都是和TRB_POSIX_AIO_Emulation_Provider平级的科级干部,先来一张关系图如下:
TRB_POSIX_AIO_Emulation_Provider类代表了(SELECT_Provider类型)使用select和epoll等反应堆模拟的异步provider,不需要系统原生异步I/O的支持;
TRB_POSIX_AIO_STD_Provider类代表了使用使用POSIX定义的标准异步I/O接口实现的异步provider,基于POSIX标准函数aio_read(),aio_write()和aio_cancel()等;
TRB_POSIX_AIO_SUN_Provider类代表了使用使用SUN平台定义的异步I/O接口实现的异步provider,基于SUN平台标准函数aioread(),aiowrite(),aiocancel();
TRB_POSIX_AIO_LINUX_NAIO_Provider类代表了使用使用Linux平台定义的异步I/O接口实现的异步provider,基于Linux平台标准函数io_submit (),io_cancel ();
结束
消化消化,暂时到这里吧,先把为实现Proactor而封装的类之间的层次关系搞清楚,提取出重点,免得盲目撒网。