• 动态加载.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 
     ");
                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
    ");
        wk = new worker::worker();
        return wk;
    }
    
    extern "C" void destroy(worker_base* rpt)
    {
        printf("delete report
    ");
        //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
    ");
            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, '');
                for(size_t i=0;i<toks;i++){
         
                    if(!invoke_method(hooktype, split[i]))
                        return false;
                }
            }else{
                printf("open confile error: %s
    ", fname.c_str());
                return false;
            }
        }catch(...){
            printf("catch: DomParser init error
    ");
            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.
    ",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!!
    ");
            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

  • 相关阅读:
    010-你觉得单元测试可行吗
    跳台阶
    斐波那契数列
    旋转数组的最小数字
    用两个栈实现队列
    重建二叉树
    从尾到头打印链表
    替换空格
    二维数组中的查找
    Best Time to Buy and Sell Stock III
  • 原文地址:https://www.cnblogs.com/jiayy/p/3456398.html
Copyright © 2020-2023  润新知