动态加载.so文件并执行类函数

时间:2023-01-08 15:51:57

背景:不同产品组将其功能编译为.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://*.com/questions/496664/c-dynamic-shared-library-on-linux