通俗易懂说:dlopen、dlsym、dlclose动态链接库的加载
1. 什么是插件?
插件定义:
采用异步事件驱动模型,保证主程序逻辑不变,将各个业务已动态链接库的形式加载进来,这就是所谓的插件。
插件应用:
为了使程序方便扩展,具备通用性,可以采用插件形式。
2. 动态链接库的加载:dlopen、dlsym、dlclose
linux提供了加载和处理动态链接库的系统调用,dlopen、dlsym、dlclose。
接口定义:
#include <dlfcn.h>
void *dlopen(const char *filename, int flag);
char *dlerror(void);
void *dlsym(void *handle, const char *symbol);
int dlclose(void *handle);
2.1 dlopen
2.1.1 功能:
打开一个动态库,将其加载到进程地址空间中
2.1.2 原型
void* dlopen(const char* filename, int flag)
2.1.3 参数
第一个参数是:被夹在动态库的路径,可以绝对路径或者相对路径
第二个参数是:符号函数被解析的方式
-
RTLD_LAZY 表示:使用延迟绑定,当函数第一次被用到时,才进行绑定,即 PLT 机制。
优点:加载速度快
缺点:如果后面有符号绑定出错将难以发现 -
RTLD_NOW 表示:当模块加载时,即完成所有的函数绑定工作。
优点:慢
缺点:有错误及时发现
2.1.4 返回值:
被加载模块的句柄
2.2 dlsym:查找符号
void* dlsym(void* handle, char* symbol)
第一个参数是 dlopen 返回的句柄
第二个参数是 要查找的符号名,以\0结尾的字符串
dlsym如果找到符号就返回符号的地址或值,如果没有就返回NULL。
2.3 dlclose
dlclose来卸载打开的库
将一个已加载的模块卸载掉。
2.4 dlerror 错误处理
判断上一次执行是否正确,正确返回NULL,错误返回相应的错误值。
3. 代码实现计算sin(3.14/2)
dyTest.c
#include<stdio.h>
#include<dlfcn.h>
int main()
{
void* handle;
double (*func)(double);
char* error;
/* handle = dlopen("/lib/libm-2.6.1.so", RTLD_NOW); linux */
handle = dlopen("/lib/x86_64-linux-gnu/libm-2.23.so", RTLD_NOW);
if(handle == NULL){
printf("open lib error: %s\n", dlerror());
return -1;
}
func = dlsym(handle, "sin");
if(NULL != (error = dlerror())){
printf("symbol sin not found , error: %s\n",error);
return -1;
}
printf("%f\n",func(3.1415926 / 2));
dlclose(handle);
return 0;
}
root@ubuntu-admin-a1:/home/6Chapter# gcc -o dyTest dyTest.c -ldl
root@ubuntu-admin-a1:/home/6Chapter#
root@ubuntu-admin-a1:/home/6Chapter# ./dyTest
1.000000
root@ubuntu-admin-a1:/home/6Chapter#
-ldl 说明:
程序中使用dlopen、dlsym、dlclose、dlerror 显示加载动态库,需要设置链接选项 -ldl
-dl ,是显式加载动态库的动态函数库