支持动态链接的系统往往都支持一种更加灵活的模块加载方式,叫做显示运行时链接,有时候也叫做运行时加载。
这种运行时加载使得程序的模块组织变得很灵活,可以用来实现一些诸如插件,驱动等功能。当程序需要用到某个插件或者驱动的时候,才将相应的模块装载进来,而不是需要从一开始就将他们全部装载进来,从而少了程序启动时间和内存使用。并且程序在运行的时候重新加载某个模块,这样使得程序本身不必重新启动而实现模块的增加,删除,更新等。
在Linux中,而动态库的装载则是通过一系列的API提供:dlopen
, dlsym
,dlerror
,dclose
。
dlopen
dlopen函数负责打开一个动态库,并将其加载到进程进程的地址空间,完成初始化过程。
void* dlopen(const char* filename, int flag);
-
第一个参数
第一个参数是被加载动态库的路径,如果整个路径是绝对路径,则该函数将会尝试直接打开该动态库,如果是相对路径,那么将会按照下面的顺序查找:- 查找有环境变量
LD_LIBRARY_PATH
指定的一系列目录 - 查找由
/etc/ld.so.cache
里面所指定的共享库路径 -
/lib
,/usr/lib
- 查找有环境变量
-
第二个参数
表示函数符号的解析方式:- RTLD_LAZY
表示使用延迟绑定, - RTLD_NOW
- RTLD_LAZY
返回值
dlopen
的返回值是被加载模块的句柄,如果加载失败则返回NULL。
dlsym
void* dlsym(void* handle, char *symbol);
- 第一个参数
由dlopen
返回的动态库的句柄。 - 第二个参数
即要查找的符号的名字。 - 返回值
如果dlsym
找到了相应的符号,则返回该符号的值,如果查找的符号是一个函数,则返回函数的地址;
如果是一个变量,则返回变量的地址。
如果整个符号是一个常量,那么返回的是该常量的值。
dlerror
每次调用以后,都可以调用dlerror函数来判断上一次调用是否成功。
dlclose
dlclose
的作用和dlopen
相反,它的作用是将一个已经加载的模块卸载,系统会维持一个加载的引用计数器,每次使用dlopen
加载模块时,相应的计数器加1,每次使用dlclose
卸载模块时,相应计数器减1,只有当计数器值减少到0时,模块才真正被卸载掉。
例子
#include <stdio.h>
#include <dlfcn.h>
int main(int argc, char **argv)
{
void* handler;
double (*func)(double);
char* error;
handler = dlopen(argv[1], RTLD_NOW);
if (handler == NULL) {
printf("Open library %s error: %s\n %s\n", argv[1], dlerror());
return -1;
}
func = dlsym(handler, "sin");
if ( (error = dlerror()) != NULL ) {
printf("Symbol sin not found: %s\n", error);
goto exit_runso;
}
printf("%f\n", func(3.1415926 / 2));
exit_runso:
dlclose(handler);
}
# gcc RunSoSimple.c -o RunSoSimple -ldl # -ldl 表示使用DL(Dynamical Loading)库
# ./RunSoSimple /lib64/libm-2.17.so
1.000000