为了使程序方便扩展,具备通用性,可以采用插件形式。采用异步事件驱动模型,保证主程序逻辑不变,将各个业务已动态链接库的形式加载进来,这就是所谓的插件。linux提供了加载和处理动态链接库的系统调用,非常方便。
- 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++ 要实现同名函数的重载,需要对函数命进行修饰,会出现函数名符号找不到的情况。另外由于是通过函数名来查找的,建议库里面的函数不要使用同名函数重载。
- 导入单个类库的具体实例
假设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);
- 插件式类库管理的实例
假设triangle 、quadrangle、pentagon都是派生于polygon类,对用的类库文件分比为triangle.so、quadrangle.so、pentagon.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;
}