业务以动态库插件加载的实现原理

为了使程序方便扩展,具备通用性,可以采用插件形式。采用异步事件驱动模型,保证主程序逻辑不变,将各个业务已动态链接库的形式加载进来,这就是所谓的插件。linux提供了加载和处理动态链接库的系统调用,非常方便。

  1. dlopen、dlsym函数介绍
    在linux上man dlopen可以看到使用说明,函数声明如下:
#include <dlfcn.h>

void *dlopen(const char *filename, int flag);

char *dlerror(void);

void *dlsym(void *handle, const char *symbol);

int dlclose(void *handle);

dlopen以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程,dlerror返回出现的错误,dlsym通过句柄和连接符名称获取函数名或者变量名,dlclose来卸载打开的库。 dlopen打开模式如下:
RTLD_LAZY 暂缓决定,等有需要时再解出符号
RTLD_NOW 立即决定,返回前解除所有未决定的符号
2. 编译动态库时需要注意的事项
gcc/g++參数 -rdynamic 用来通知链接器将全部符号加入到动态符号表中(目的是可以通过使用 dlopen 来实现向后跟踪)
gcc/g++參数 -fPIC 作用: 当使用.so等类的库时,当遇到多个可运行文件共用这一个库时, 在内存中,这个库就不会被复制多份,让每一个可运行文件一对一的使用,而是让多个可运行文件指向一个库文件,达到共用. 宗旨:节省了内存空间,提高了空间利用率.
注意:在使用 g++ 编译时库函数在库中的定义要用extern“c”来申明,因为 c++ 要实现同名函数的重载,需要对函数命进行修饰,会出现函数名符号找不到的情况。另外由于是通过函数名来查找的,建议库里面的函数不要使用同名函数重载。

  1. 导入单个类库的具体实例
    假设triangle派生于polygon类 ,通过dlopen的方式导入triangle.so,如下:
#include "polygon.h" //类定义处

#include <dlfcn.h>

void* triangle = dlopen("./triangle.so", RTLD_LAZY);
create_t* create_triangle = (create_t*) dlsym(triangle, "create");

destroy_t* destroy_triangle = (destroy_t*) dlsym(triangle, "destroy");

polygon* poly = create_triangle();
// use the class
poly->set_side_length(7);
 cout << "The area is: " << poly->area() << '\n';
 
// destroy the class
  destroy_triangle(poly);
  
// unload the triangle library
 dlclose(triangle);

  1. 插件式类库管理的实例
    假设triangle 、quadrangle、pentagon都是派生于polygon类,对用的类库文件分比为triangle.soquadrangle.sopentagon.so。为了进行插件式管理,我们定义一个polygonManager的类来实现动态加载和管理。
    polygonManager.h头文件定义如下:
/*****************************************************/
/* polygonManager.h
 *
 * Copyright (C) YYYY.MM.DD by XXX
 * 
 * 多边形管理类
*/
/*****************************************************/


#ifndef __BASIC_POLYGON_MANAGE_H__
#define __BASIC_POLYGON_MANAGE_H__

#include "polygon.h"


#define SUPPORT_MAX_PLUGIN_NUM	(32)                                      //支持的最大插件(类库)的数目
#define POLYGONMODULE_PLUGIN_DIR "../lib/"                                //多边形插件库的库路径
#define POLYGONMODULE_PLUGIN_CFG 	"../cfg/BusinessModulePlugInCfg"      //所需要加载的插件配置文件路径和文件名
#define POLYGONMODULE_PLUGIN_FLAG	"FLAG:TM_BUSINESS_PLUGIN"             //配置文件头标志



class polygonManager
{
public:
	polygonManager();
	~polygonManager();
	
	void pM_Initialize(void);

private:

	int  pM_ParsePlugInCFG(const char *filename);
	int  pM_LoadPlugin(void);
	
private:
	polygon *m_pPolygonBase[SUPPORT_MAX_PLUGIN_NUM];//存储

	int  m_LoadPlugin;			//库文件加载标志   0:未加载   1:已加载
	
	int  m_PluginCount;
	char m_PluginName[SUPPORT_MAX_PLUGIN_NUM][32];
};
#endif

polygonManager.cpp定义如下:

/*****************************************************/
/* polygonManager.cpp
 *
 * Copyright (C) YYYY.MM.DD by XXX
 * 
 * 多边形管理类
*/
/*****************************************************/


#include <dirent.h>
#include <dlfcn.h>
#include <stdio.h>
#include <unistd.h>
#include "BusinessManager.h"

polygonManager::polygonManager()
{   
	m_PluginCount = 0;	
	for(int i = 0; i < SUPPORT_MAX_PLUGIN_NUM; i++)
	{
		memset(m_PluginName[i], 0, SUPPORT_MAX_PLUGIN_NUM);
	}
}

polygonManager::~polygonManager()
{
	
}

void polygonManager::pM_Initialize(void)
{
	m_LoadPlugin = 0;	
	pM_ParsePlugInCFG(POLYGONMODULE_PLUGIN_CFG);//解析插件配置文件	
	pM_LoadPlugin();//加载插件库
}



int polygonManager::pM_ParsePlugInCFG(const char *filename)
{
   //解析配置文件的具体实现
   .....................
   .....................
   return 0;
}

//加载插件的实现
int polygonManager::pM_LoadPlugin(void)
{
	void *dl = NULL;
	int index = 0;
	int i = 0;
	char path[1024]  = {0};
	
	for( i = 0; i < m_PluginCount; i++ )
	{
		memset(path, 0, sizeof(path));
		sprintf(path, "%s/%s", POLYGONMODULE_PLUGIN_DIR, m_PluginName[i]);
		if (0 == access(path, F_OK))
		{
			dl = dlopen(path, RTLD_LAZY);//以暂缓决定的方式打开动态库并获取句柄
			if(dl == NULL)
			{
				printf("load plugin failed%s, err : %s\n", path, dlerror());
				exit(1);
				continue;
			}
		
			Create_SubModule Create_func = (Create_SubModule)dlsym(dl, "OpenModule");//dlsym函数通过句柄dl和连接符名"OpenModule"获取加载的库的函数名
			if( (Create_func != NULL) && (index < SUPPORT_MAX_PLUGIN_NUM) )
			{
				m_pPolygonBase[index++] = Create_func();//将函数名存入数组指针
			}
			usleep(100*1000);
		}
	}
	return 0;
	
}



int main()
{	
	polygonManager *pPolygonManager = NULL;
	
	pPolygonManager = new polygonManager();
	assert( pPolygonManager != NULL );
	
	pPolygonManager->pM_Initialize();

	while(1)
	{
		//处理一些轮询消息事件
		.....................
		.....................
		usleep(500000);
	}

	return 0;
}

猜你喜欢

转载自blog.csdn.net/dawudayudaxue/article/details/89000624