業務以動態庫插件加載的實現原理

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章