dlopen动态装载共享库
dlopen动态装载共享库
网游排行前十名⼀、基本说明
显式运⾏时链接 (Explicit Run-time Linking),有时候也叫做运⾏时加载,程序⾃⼰在运⾏时控制加载指定的模块,即动态库,并且可以在不需要该模块时将其卸载。
和普通的动态链接相⽐,显式运⾏时链接,可以使程序在需要使⽤到某个插件或驱动时,才将相应的模块加载进来,⽽不需要在⼀开始就将所有插件和驱动的模块进⾏加载,从未减少了程序启动的时间和内存使⽤。并且程序可以在运⾏过程中动态的重新加载模块,从⽽实现程序在不重启的前提下,实现模块的增加、删除、更新等需求。
女神快乐经典短句普通的动态链接,是由动态链接器在程序启动之前完成模块的装载和链接的,这⼀过程⾃动完成(eg:gcc -o demo demo.c -ltest);但是动态库的显式运⾏链接则是通过⼀系列的动态链接器提供的API(dlopen,dlsym,dlerror,dlclose),⼿动⼲预完成。
⼆、接⼝介绍
运⾏时加载,⼀共由四个函数完成:打开动态库(dlopen)、查符号(dlsym)、错误处理(dlerror)、卸载动态库(dlclose)。这四个API实现在/lib/libdl.so.2中,声明和相关常量被定义在系统
标准头⽂件<dlfcn.h>。
1. dlopen:打开⼀个动态库,并将其加载到进程的地址空间,完成初始化过程
1. 函数原型:void * dlopen(const char *filename, int flag);
2. 参数说明:
3. const char *filename:被加载的动态库的路径
该路径可以是绝对路径,如果使⽤了绝对路径,那么 dlopen 函数会尝试直接打开对应路径的动态库;也可以使⽤相对路径,在该情况下,dlopen 则会尝试按⼀定顺序去寻该动态库,若到则打开,并返回对应句柄。
以下为使⽤相对路径情况下,dlopen 函数查的顺序:
(1)(仅限ELF)如果程序的可执⾏⽂件中包含 DT_RPATH 标签并且没有包含 DT_RUNPATH 标签,那么查 DT_RPATH 中指定的⽬录
(2)查环境变量 LD_LIBRARY_PATH 指定的⼀系列⽬录
(3)(仅限ELF)如果可执⾏⽂件中包含了 DT_RUNPATH 标签,那么查 DT_RUNPATH 中指定的⽬录
(4)查由 /etc/ld.so.cache ⾥⾯所指定的共享库路径
(5)/lib 、/usr/lib
(1和3,来⾃于Linux Programmer’s Manual,也有⼈说这⾥的描述是错误的,正确性尚未明确)
filename 还可以被设置为0,那么 dlopen 返回的将是全局符号表的句柄,也就是说,可以在运⾏时到全局符号表⾥的任何⼀个符号,并且可以执⾏他们。全局符号表包括了:程序可执⾏⽂件本⾝、被动态链接库加载到进程中的所有共享模块,以及在运⾏时通过 dlopen() 打开并且使⽤了 RTLD_GLOBAL ⽅式的模块中的所有符号。
4. int flag:函数符号的解析⽅式,有以下⼏种,部分⽅式在使⽤时可以通过“或”运算⼀起使⽤
1. RTLD_LAZY:使⽤延迟绑定。
当函数在第⼀次被调⽤的时候才执⾏绑定(符号查、重定位等),如果没有⽤到则不进⾏绑定。所以在 dlopen() 函数返
回的时候,模块间调⽤的函数调⽤都没有进⾏绑定,⽽是需要⽤到时才由动态链接器来负责绑定,这样能提⾼模块的加载
速度。
2. RTLD_NOW:
当模块被加载完时即完成所有函数的绑定⼯作,如果有任何未定义的符号引⽤的绑定⼯作没法完成,那么 dlopen() 就会返
回错误。
3. RTLD_GLOBAL:为什么桌面图标不见了
3. RTLD_GLOBAL:
被加载的模块的全局符号合并到进程的全局符号表中,使得以后加载的模块可以使⽤这些符号。
4. RTLD_LOCAL:
与 RTLD_GLOBAL 作⽤相反,该动态库内定义的符号不能被在其后加载的动态库重定位,即后续加
载的动态库不可使⽤
该库中的符号。
注:RTLD_LAZY 和 RTLD_NOW 必须使⽤⼀个;RTLD_GLOBAL 和 RTLD_LOCLA 可以与 RTLD_LAZY 和
RTLD_NOW 任意⼀个⼀起使⽤,通常操作为“或“,RTLD_LOCLA 和 RTLD_GLOBAL 在缺省的情况下,默认为
RTLD_LOCLA
5. 注意事项:
dlopen 的返回值是被加载模块的句柄,该句柄在后续使⽤ dlsym() 和 dlclose() 时需要⽤到。如果加载失败,则返回 NULL 。
如果模块已经被加载过了,那么将返回与上⼀次加载相同的句柄。
如果加载的模块之间存在依赖关系,那么该依赖需要⼿动⼲预。⽐如模块 A 依赖于模块 B,那么必须先加载 B,再进⾏加载 A 的操作。
2. dlsym:运⾏时装载的核⼼,通过该函数到所需符号
1. 函数原型:void * dlsym(void *handle, char *symbol);
2. 参数介绍:
1. void *handle:dlopen() 返回的动态库句柄;
2. char *symbol:需要查的符号名字,⼀个以 ‘\0’ 结尾的C字符串。
3. 使⽤说明:
如果 dlsym() 到对应的符号,则返回该符号的值;没有到,则返回 NULL。
dlsym() 返回的值对于不同类型的符号,意义不同。如果查的符号是函数,那么将返回该函数的地址;如果是变量,则返回该变量的地址;如果是常量,那么返回的是常量的值,如果常量的值恰好是 NULL 或 0,那么将通过 dlerror() 函数来进⾏判断,如果到符号,那么 dlerror() 将返回 NULL;如果没到,dlerror() 会返回对应的错误信息。
3. 注意事项:
内蒙古草原旅游
在查动态库符号时,存在符号优先级问题。常规的动态链接采⽤的优先级⽅式为装载序列,即先装⼊的符号优先。但是
dlsym() 对符号的查优先级存在两种情况:
(1)如果在全局符号表中进⾏符号查,即 dlopen() 的 filename 参数为 NULL,那么由于全局符号表使⽤的是装载序列,所以 dlsym() 使⽤的也是装载序列;
(2)如果是对某个通过 dlopen() 打开的共享对象进⾏符号查的话,那么采⽤的是⼀种叫做依赖序列的优先级。依赖序列,是以被 dlopen() 打开的共享对象为根节点,对它所有依赖的共享对象进⾏⼴度优先遍历,直到到符号为⽌。
3. dlerror
1. 函数原型:char * dlerror (void);
2. 使⽤说明:
每次调⽤ dlopen()、dlsym() 或 dlclose() 以后,都可以调⽤ dlerror() 函数来判断上⼀次调⽤是否成功。如果成功,则
dlerror() 返回 NULL;如果失败,则返回对应的错误信息。
4. dlclose
1. 函数原型:int dlclose (void * handle);
肇事逃逸致人死亡
2. 使⽤说明:
dlclose() 的作⽤与 dlopen() 相反,它的作⽤是将⼀个已经加载的模块卸载。系统会维持⼀个加载引⽤计数器,每次使⽤
dlopen() 加载某模块时,相应的计数器加1;每次使⽤ dlclose() 卸载某模块时,相应计数器减1。只有当计数器减⾄0时,模块才被真正地卸载掉。卸载过程与加载相反,先执⾏“.finit”段代码,然后将相应的符号从符号表中去除,取消进程空间与模块的映射关系,然后关闭模块⽂件。
附录
以下为dlopen等接⼝的简单使⽤demo:
//"a.h"
/
/libadd.so头⽂件
#ifndef __A_H__
#define __A_H__
#ifdef __cplusplus
extern "C"{
#endif
int add(int x, int y);
#ifdef __cplusplus
}
#endif
#endif // !__A_H__
/
/a.c
//libadd.so的实现,简单实现两个int类型整数的相加
#include"a.h"
int add(int x,int y)
{
return x+y;
}
//cmd: gcc -fpic -shared -o libadd.so -g a.c
//main.c
#include<stdio.h>
#include<dlfcn.h>
#include<stdlib.h>大学社团
typedef int(*func_add)(int,int);
//相对路径
const char* relativePath_a ="./libadd.so";
//绝对路径
const char* absPath_a ="/home/44124/test/libadd.so";
int main(int argc,char** argv)
{
void* handle =dlopen(absPath_a, RTLD_LAZY);
if(!handle){
fprintf(stderr,"[%s](%d) dlopen get error: %s\n",__FILE__,__LINE__,dlerror());
exit(EXIT_FAILURE);
}
do{
func_add add =(func_add)dlsym(handle,"add");
printf("1 add 2 is [%d]\n",add(1,2));
}while(0);
dlclose(handle);
return0;
}
//cmd: gcc -o demo -g main.c -ldl; ./demo
//output: 1 add 2 is [3]

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。