动态加载库
linux系统下有两种类型的库:1.静态库(.a);2.动态库(.so)。
静态库会直接链接到可执行文件中,所以可执行文件运行时,不再需要静态库。
动态库的使用分为两种:a.动态链接:在编译时声明动态库的存在,在运行期间链接动态库,意味着动态库在编译时对编译器可见。b.动态加载:在运行期间动态加载库,是由用户自己链接而不是由链接器,在编译期间可以不需要知道动态库的存在。
工程项目中,如果需要设计成为一个插件化的架构,就需要使用动态加载库的机制。这需要使用到dlopen、dlsym、dlclose三个函数实现,但在编译时,需要使用-rdynamic选项。假设现在我们的工程想要将几个功能实现成插件,这些插件由配置文件控制需要加载的模块,在工程需要添加或者删除其中的某些模块时,都不要重新进行编译。在程序初始化时,就需要将这些模块信息加载进来,其dso.h头文件实现如下:
#include <vector> using namespace std; #define MODULE_OK 0 #define MODULE_ERR 1 #define MAGIC_MAJOR_NUMBER 20170515 #define MAGIC_MINOR_NUMBER 0 #define STANDEAD_MODULE_STUFF MAGIC_MAJOR_NUMBER, \ MAGIC_MINOR_NUMBER, \ __FILE__ typedef struct Module{ int version; //主版本号 int minor_version; //子版本号 const char *name; //模块名 void (*init)(Module *); //初始化函数 int (*handle)(void *); //处理函数 }Module;
Module* dso_load(const char *path, const char *name);
dso.cpp:
Module* dos_load(const char *path, const char *name) { char *npath = strcat2(3, path, name, ".so"); char *handle = NULL; if((handle = (dlopen(npath, RTLD_GLOBAL|RTLD_NOW)) == NULL) { LOG(LOG_LEVEL_ERROR, "load module fail(dlopen): %s",dlerror()); } char *rv = NULL; if((rv = dlsym(handle, name)) == NULL) { LOG(LOG_LEVEL_ERROR, "load module fail(dlsym): %s", dlerror()); } module = (Module *)rv; module->init(module); }
其中,dlopen的函数原型是:
void *dlopen(const char *path, int mode);
dlopen以指定的mode模式打开path指定的动态链接库,并返回这个动态链接库的句炳。该函数主要用来加载动态库中的符号,这些符号是在程序编译时不知道的。mode指定了解析的方式:RTLD_LAZY、RTLD_NOW和作用范围:RTLD_GLOBAL、RTLD_LOCAL。RTLD_LAZY是对库中符号延迟解析,等由需要的时候再进行解析。RTLD_NOW是立即将所有符号导出。RTLD_GLOBAL是允许到处符号,使库中解析的变量在随后的其它动态链接库中可以使用。
dlsym的函数原型是:
void* dlsym(void* handle, const char *symbol);
dlsym的作用是获取动态库中的函数地址和变量地址。