构建自己的C/C++插件开发框架

原文转自https://blog.csdn.net/chgaowei


构建自己的C/C++插件开发框架(一)——初步设想

最近一直在学习OSGI方面的知识。买了一本《OSGI原理和最佳实践》,可是还没有到。遗憾的是,OSGI目前的几个开源框架只支持Java,对C和C++都不支持的。可惜我们公司目前主要的开发语言还是c和c++,即便是引进OSGI,所得的好处范围有限。而我对松散耦合的模块化开发向往已久。查了一下OSGI对C++支持的好像是有一个开源项目,不过好像应用范围很小。而SCA标准中是有对C++实现模型的支持的,但是几个开源的框架目前还只支持JAVA。

  昨天看了丁亮的转载的一篇博客《C/C++:构建你自己的插件框架》,原文的链接:http://blog.chinaunix.net/u/12783/showart_662937.html。看了一下里面讲的方法,自己倒是可以实现。所以有了构建自己的c/c++插件开发框架的想法。今天先写一下初步的设想。

 

  C/C++插件开发框架的要素

  BlueDavy有一篇介绍服务框架要素的文章(链接:http://www.blogjava.net/BlueDavy/archive/2009/08/28/172259.html)。我的插件框架也要考虑、解决以下的几个问题:

  1、如何注册插件;

  2、如何调用插件;

  3、如何测试插件;

  4、插件的生命周期管理;

  5、插件的管理和维护;

  6、插件的组装;

  7、插件的出错处理;

  8、服务事件的广播和订阅(这个目前还没有考虑要支持);

  其中有几个点很重要:1)插件框架要能够使模块松散耦合,做到真正的面向接口编程;2)框架要支持自动化测试:包括单元测试,集成测试;3)简化部署;4)支持分布式,模块可以调用框架外的插件。

 

  采用的技术
  插件框架要解决的一个问题就是插件的动态加载能力。这里可以使用共享库的动态加载技术。当然,为了简单,第一步只考虑做一个linux下的插件框架。

 

  总体结构

  框架的总体结构上,参考OSGI的“微内核+系统插件+应用插件”结构。这里要好好考虑一下把什么做在内核中。关于微内核结构,以前我做个一个微内核流程引擎,会在后面有时间和大家分享。

  框架中模块间的数据传送,有两种解决方法:一是普元采用的XML数据总线的做法。优点是扩展性好,可读性好。但是速度有些慢。二是采用我熟悉的信元流。优点的效率高,访问方便,但是可读性差一点,另外跨框架的数据传送,需要考虑网络字节序的问题。

  对于框架间的通信,通过系统插件封装,对应用插件隐藏通信细节。

 

      部署

      努力做到一键式部署。


构建自己的C/C++插件开发框架(二)——总体功能


在这一系列的上一个文章中,介绍了构建C/C++插件开发框架的初步设想,下面我会一步步的向下展开,来实现我的这个设想。

今天主要谈一下我对这个框架的功能认识,或是期望。昨天看了一篇关于持续集成能力成熟度模型的一篇文章,受此启发,我对此框架的认识渐渐清晰。

这个框架可以当做我们公司底层产品(交换机,资源服务器等)的基础设施。上层基于java开发的产品可以直接在OSGI上开发。

 

核心功能:

1、最重要的一个功能是,提供一个模块化的编程模型,促进模块化软件开发,真正的实现针对接口编程。

2、提供一个有助于提高模块可重用性的基础设施。

3、提供一个C/C++插件的运行环境。

4、提供一个动态插件框架,插件可以动态更改,而无需重启系统。这个功能虽然不难实现,但是用处好像不是很大。

 


扩展部分功能:

 

1、支持分布式系统结构,多个运行框架组合起来形成一个系统,对模块内部隐藏远程通讯细节。

2、支持系统的分层架构。

3、能够和其他的开发框架进行集成,比如OSGI,SCA等。

4、多个运行框架中,能够实现对运行框架的有效管理。

5、概念上要实现类似于SCA中component(构件),composite(组合构件),Domain(域)的概念。

 


开发部分功能:

 

1、为了简化开发,开发一个Eclipse插件,用于开发框架中的C/C++插件。能够根据插件开发向导,最终生成符合插件规范的公共代码,配置文件,Makefile文件等。

 


调试部分功能:

 

1、提供一个统一的日志处理函数,可以集成Log4cpp。

2、提供模块间的消息日志,以及框架对外的接口日志。

3、提供消息和日志的追踪功能,能将和某事件相关的消息和日志单独提取出来。

4、提供资源监测功能,监测对资源(内存,套接字,文件句柄等)的使用情况。

 


测试部分功能:

 

1、集成一些单元测试框架,比如unitcpp,达到自动化单元测试的目标。

2、自己实现自动化集成测试框架,并且开发相应的Eclipse插件,简化集成测试(利用脚本和信元流)。

3、集成原有的自动化功能测试框架flowtest,并且开发相应的Eclipse插件,简化功能测试。

4、实现性能测试,监测框架。

 


部署部分功能:

 

1、实现自动化部署。特别是在分布式应用的情况下。

2、提供一个命令行程序,通过命令更改系统配置,管理插件。





构建自己的C/C++插件开发框架(三)——总体结构

这几天为了设计插件开发框架,尝试用了一下发散思维来思考问题。中间看过依赖注入,AOP(面向方面编程),以及契约式设计等。虽然有些工具无法直接使用,但是这些思想还是可以借鉴的,比如依赖注入,契约式设计。至于AOP,和工具相关性较大,虽然思想不错,但是无法直接在C++中使用。

我设计的插件间的依赖不是通过接口实现的,而是通过插件间的数据(信元流)。而信元流的检测可以使用契约来检查。

插件开发框架的总体结构

结构图

微内核

1、 负责插件的加载,检测,初始化。

2、 负责服务的注册。

3、 负责服务的调用。

4、 服务的管理。

扩展层:

1、 日志的打印。

2、 消息(信元流)的解释,将二进制格式解释为文本。便于定位。

3、 消息和日志的追踪。

分布式处理层:

1、 用于和其他的框架通信。

2、 和其他的框架搭配,形成一个分布式的系统。

自动化测试框架层:

1、 集成 cppunit 。

2、 自动化集成测试框架。

3、 自动化功能测试框架。

和第三方框架集成层:

1 、和 第三方框架 集成层。


构建自己的C/C++插件开发框架(四)——核心层设计和实现

上面一篇文章大致描述了一下插件开发框架整体结构。这篇描述一下核心层的设计和实现。

至于核心层的设计,我想借鉴 一下微内核的思想。核心层只负责实现下面几个功能:

1、  插件的加载,检测,初始化。

2、  服务的注册。

3、  服务的调用。

4、  服务的管理。


插件的加载,检测,初始化

插件的加载利用linux共享库的动态加载技术。具体的方法可以看一下IBM网站的一篇资料《Linux 动态库剖析》。

 

服务的注册

服务的注册与调用采用表驱动的方法。核心层中维护一个服务注册表。

//插件间交互消息类型
typedef enum __Service_Type
{
    Service_Max,

}Service_Type;


//插件用于和其他插件通信接口函数,由插件提供。
typedef PRsp_Ele_Stream (*PF_Invoke_Service_Func)(PReq_Ele_Stream pele_str);


//驱动表
typedef PF_Invoke_Service_Func Service_Drive_Table[Service_Max];

驱动表是一个数组,下标为插件间交互消息类型,成员为插件提供的接收的消息处理函数,由插件初始化的时候,调用插件框架的的注册函数注册到驱动表。

插件的初始化实现为:
//插件用于注册处理的消息类型的函数,由插件框架提供。
typedef RET_RESULT (*PF_Service_Register_Func)(Service_Type service_type);

//插件用于和其他插件通信接口函数,由插件框架提供。
typedef PRsp_Ele_Stream (*PF_Invoke_Service_Func)(PReq_Ele_Stream pele_str);

//插件回复响应函数。插件收到异步请求后,处理完成后,发送响应消息给请求的插件。由插件框架提供
typedef void (*PF_Send_Response_Func)(PRsp_Ele_Stream pele_str);

//初始化插件信息
typedef struct Plugin_Init_St
{
    PF_Service_Register_Func register_func;//服务注册函数,要注册一系列的枚举值。插件可以处理的服务枚举值
    PF_Invoke_Service_Func invoke_serv_func;//和其他组件交互时,调用的用于和其他组件交互的函数。发送请求消息。
    PF_Send_Response_Func send_rsp_func;//再设计一个回复响应消息的接口。收到异步请求后,处理完毕后通知请求模块处理结果。
} Plugin_Init_St, *PPlugin_Init_St;

//初始化插件函数,类似于构造函数。由插件提供,供插件框架加载插件时初始化插件使用。
void PF_Init_Plugin(PPlugin_Init_St pinit_info);

      插件在函数PF_Init_Plugin中调用函数register_func来注册插件要处理的消息类型。

 

服务的调用
//信元结构体
typedef struct Ele_St
{
    Ele_Tag tag;
    Ele_Length len;
    Ele_Value  value;
    PEle_St next;
}Ele_St, *PEle_St;

//请求消息,信元流格式。
typedef struct Req_Ele_Stream
{
    Plugin_ID src_id;//源插件id
    Service_Type req_type;//请求类型
    PEle_St ele;
} Req_Ele_Stream, *PReq_Ele_Stream;

//响应消息,信元流格式。
typedef struct Rsp_Ele_Stream
{
    Plugin_ID dest_id;//目的插件id
    Service_Type req_type;//响应对应的请求的类型。
    Execute_Result result;//记录执行结果
    Execute_Reason reason;//记录执行结果的原因
    PEle_St ele;
} Rsp_Ele_Stream, *PRsp_Ele_Stream;

//接收插件调用服务请求函数,由插件提供,入参为请求信元流。返回值为响应信元流,用于同步请求处理。
PRsp_Ele_Stream PF_Receive_Invoke_Proc(PReq_Ele_Stream pele_str);

//插件收到响应消息的处理入口函数,由插件提供。如此为响应信元流。
void PF_Receive_Rsponse_Porc(PRsp_Ele_Stream pele_str);

插件间的依赖关系是通过信元流来实现的。至于信元流的使用在我的另一篇博客《使用信元流(TLVStream)规范、简化模块(C/C++)间交互 》中有描述。插件对外的接口都是统一的。

如果插件要和其他的插件通信,则调用PF_Init_Plugin函数的传递的服务调用接口: invoke_serv_func。插件框架根据信元流的类型,查找驱动表,找到对应的服务接收函数。插件用函数PF_Receive_Invoke_Proc接受其他插件的请求,此函数是插件想插件框架主动注册到驱动表的。

如果服务时同步的,这直接通过此函数返回,返回的信息在响应信元流中。如果是异步的请求,这插件在处理完成后,通过 send_rsp_func函数来发送响应。

 

插件的卸载
//卸载插件时调用的函数,类似于析构函数。由插件提供,供插件框架卸载插件时调用。
void PF_Destroy_Func();

你可能感兴趣的:(C/C++)