版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/vrg000/article/details/78139153
库的动态加载
一般用于实现插件功能,可通过调用函数来加载、卸载共享库。可以查找共享库中函数名、变量名所对应的指针。
# include <dlfcn.h>
void *dlopen(const char *libfilename, int flags); //打开共享库
int dlclose(void *handle); //关闭共享库
const char *dlerror(void); //返回错误字符串
void *dlsym(void *handle, char *symbol); //查找共享库中的符号
注意:使用共享库的动态加载,在gcc链接时,要加入 -ldl 选项。
dlopen() 打开共享库
void *dlopen(const char *libfilename, int flags);
- void *返回值:成功返回共享库句柄,失败返回NULL
- const char *libfilename:共享库名称
- 名字包含’/’:按照绝对路径或相对路径使用
- 名字不包含’/’:按照连接器默认方式搜索共享库
- int flags:选项
- RTLD_LAZY:不解析共享库中未定义函数符号的内存地址,使用时再解析
- RTLD_NOW:解析共享库库中未定义函数符号的内存地址,可用于调试
注:以上两个选项为必选选项,二选一 - RTLD_GLOBAL:这个共享库中解析的函数符号可以被后面打开的库使用
- RTLD_LOCAL:这个共享库中解析的函数符号不能被后面打开的库使用
注:以上两个选项为共享库的使用范围 - RTLD_NODELETE:不卸载共享库,即使dlclose()后引用计数为0。此时,dlopen()不会重新初始化库中静态变量(glibc2.2可用)
- RTLD_NOLOAD:不加载共享库,作用1:可用与测试共享库是否被加载,如果加载返回共享库句柄,不加载返回NULL。作用2:可再已打开的共享库上添加新的flags。(glibc2.2可用)
- RTLD_DEEPBIND:解析库中符号引用时,先搜索库中的定义,如果库中没有,再搜索全局定义。(这个很有用,可无脑加上,glibc2.3.4可用)
dlclose() 关闭共享库
int dlclose(void *handle);
- int返回值:成功返回0,失败返回非0
- void *handle:共享库句柄
dlerror() 错误原因
const char *dlerror(void);
- const char *返回值:如果dl系列函数出错,可调用dlerror()获取出错原因字符串
dlsym():获取符号的地址
void *dlsym(void *handle, char *symbol);
- void *返回值:成功返回符号的地址,失败返回NULL
- void *handle:共享库句柄
- char *symbol:符号名称(函数名、变量名)
例子
root@jing-VirtualBox:~# ls
a.out libt.so main.c t.c
root@jing-VirtualBox:~# cat t.c #有点懒,并没有写t.c的头文件。。。
# include <stdio.h>
void f()
{
printf("lib t\n");
}
root@jing-VirtualBox:~# cat main.c
# include <stdio.h>
# include <dlfcn.h>
int main(void)
{
void *hander = dlopen("./libt.so", RTLD_LAZY);
if (hander == NULL) {
printf("%s\n", dlerror());
}
void (*f)() = dlsym(hander, "f");
f(); //调用函数f
dlclose(hander);
return 0;
}
root@jing-VirtualBox:~# gcc main.c -ldl
root@jing-VirtualBox:~# ./a.out
lib t
共享库的设计
因库可以动态加载,所以优秀共享库的.h文件,只包含对外可见的接口&变量。这样可以减少开销,防止程序员使用共享库私有的接口导致共享库升级时版本不兼容。
使用static关键字修饰符号,表示符号私有于一个代码快,无法被其他.o文件使用。
链接器版本脚本
作用:确保连接器只导出指定符号。
gcc -shared a.o b.o c.o -o libabc.so -Wl,--version-script,scriptfile.map
-Wl,–version-script,scriptfile.map:指定一个脚本,规定可以导出的符号,脚本如下
VER_1 {
global: # 可以被加载的符号
f_a;
f_b;
f_c;
local:
*; #*表示除global之外的所有符号
};
共享库的初始化函数和终止函数
初始化函数会在共享库加载的时候自动执行,终止函数会在共享库卸载的时候自动执行,代码如下。
void __attribute__ ((constructor)) 初始化函数名(void)
{
/*初始化代码*/
}
void __attribute__ ((destructor)) 终止函数名(void)
{
/*终止代码*/
}
注:以上均出自 Linux/UNIX系统编程手册,只选了感觉有用的。