动态加载.so文件并执行类函数
背景:不同产品组将其功能编译为.so,这些.so 可以加载到统一的基础平台上运行,如果产品组代码有改动,只需要更新对应的.so
问题:如何动态加载.so文件,并使用里边的函数/类 ?
解决方法1: 使用类的多态特性,将各种产品功能抽象为“工作类”,这些“工作类”都继承一个“动态加载基类”,然后定义纯C的类创建和销毁函数,产品功能.so加载进来后,基础平台寻找创建和销毁函数,就可以创建一个“工作类”实例,并通过基类指针使用。下面是示例代码
class worker_base { protected: int wtype; public: worker_base():wtype(0){} virtual ~worker_base(){} void settype(int type) { wtype = type; } virtual int doJob() = 0; }; typedef worker_base * create_t(); typedef void destroy_t(worker_base *);
上图是worker_base.h, 定义了worker_base类,这个类是一个基类,工作函数 doJob 定义为纯虚函数,同时,定义基类的创建和销毁函数指针 create_t 和 destroy_t
#include <string> #include "worker_base.h" namespace worker{ class worker : public worker_base{ public: worker(){} ~worker(){} public : virtual int doJob() { printf("dojob in module X \n "); return 0; } }__rte_cache_aligned; }
上图是worker.h , 定义class worker 为具体的产品工作类,继承 work_base, 并实现自己的 doJob
#include <stdio.h> #include "worker.h" worker::worker * wk = NULL; extern "C" worker_base* create(){ printf("create report\n"); wk = new worker::worker(); return wk; } extern "C" void destroy(worker_base* rpt) { printf("delete report\n"); //delete rpt; }
上图为worker.cpp , 实现了worker 类实例的创建和销毁函数
#include <string> #include <list> #include <dlfcn.h> #include "worker_base.h" #include "DomParser.h" /// worker list typedef std::list<worker_base*>::iterator worker_iter; enum hooktype { WK }; class Register { public: Register(){}; ~Register(){}; public: std::list<worker_base*> worker_list; private: bool parse(); bool hook_register(hooktype ht, void* hk);///<Register one hook function into the list bool invoke_method(const std::string& hooktype, const std::string& libname); bool init(); bool run(); public: inline static Register& instance() { extern Register __g_reg; return __g_reg; } }; static inline Register& GetHook() { return Register::instance(); }
上图是register.h , 定义了一个注册类,该类通过一个链表维护所以工作类实例,parse 函数解析XML文件,将产品.so加载进来,dlsym 寻找create_t和delete_t符号,创建实例然后插入链表。主流程通过调用链表里的实例的run方法完成对.so类的调用,下面是加载一个.so的方法
#include <strsplit.h> #include <iostream> #include "register.h" Register __g_reg; /*将一个worker实例(基类指针)注册到链表*/ bool Register::hook_register(hooktype ht, void* hk) { if(ht == WK){ worker_list.push_back((worker_base*)hk); }else{ printf("hook typedef error\n"); return false; } return true; } bool Register::parse() { const std::string& fname("./model.xml"); nsfocus::DomParser parser; try{ if(parser.open(fname)){ xercesc::DOMNode* node; xercesc::DOMNode* root = parser.getRootNode(); node = nsfocus::DomUtil::getChildNode(root, "modules/netio", "/"); std::string hooktype; std::string val; nsfocus::DomUtil::getAttribNodeValue<std::string>(node, "hooktype", hooktype); nsfocus::DomUtil::getAttribNodeValue<std::string>(node, "files_list", val); size_t toks = 0; nsfocus::strsplit split(val, " ", true, 3000, toks, '\0'); for(size_t i=0;i<toks;i++){ if(!invoke_method(hooktype, split[i])) return false; } }else{ printf("open confile error: %s\n", fname.c_str()); return false; } }catch(...){ printf("catch: DomParser init error\n"); return false; } return true; }
/*使用dlopen接口打开一个.so文件,通过dlsym接口寻找create和destroy的符号表
对应的地址,这两个地址就是.so工作类实例创建和销毁的函数*/
bool Register::invoke_method(const std::string& hooktype, const std::string& libname) { std::string lib = "./" + libname; void *handler = dlopen(lib.c_str(), RTLD_NOW); if(handler == NULL){ printf("dlopen err : %s.\n",dlerror()); return false; } if(hooktype == "worker"){ create_t * create_worker = (create_t*) dlsym(handler,"create"); const char * dlsym_error = dlerror(); if(dlsym_error) { std::cerr << "load worker create error:" << dlsym_error << std::endl; return false; } destroy_t * destroy_worker = (destroy_t*) dlsym(handler,"destroy"); dlsym_error = dlerror(); if(dlsym_error) { std::cerr << "load worker destroy error:" << dlsym_error << std::endl; return false; } worker_base * w = create_worker(); return hook_register(WK, w); }else{ printf("hooktype compare failed!!\n"); return false; } return true; } /*遍历整个worker链表,调用其doJob方法*/ bool Register::run() { worker_iter it = worker_list.begin(); worker_iter it_end = worker_list.end(); for (; it != it_end; ++it){ (*it)->doJob(); } return true; } bool Register::init() { return parse(); }
下面是main函数
#include <stdlib.h> #include <stdio.h> #include "register.h" int main(){ Register & reg = GetHook(); reg.init(); reg.run(); return 0; }
下面是model.xml示例
<root> <modules> <netio hooktype="worker" files_list="libworker.so"/> </modules> </root>
参考 http://stackoverflow.com/questions/496664/c-dynamic-shared-library-on-linux