爲了使程序方便擴展,具備通用性,可以採用插件形式。採用異步事件驅動模型,保證主程序邏輯不變,將各個業務已動態鏈接庫的形式加載進來,這就是所謂的插件。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;
}