通过动态链接库,程序可以在运行时加载所需的库,而不是在编译时静态地链接它们
这一特性使得在系统中添加或删除功能模块时无需重新编译整个程序,极大地提高了代码的复用性和模块化程度
而在Linux C中实现动态链接库加载的核心函数就是`dlopen`
dlopen函数简介 `dlopen`函数定义在`dlfcn.h`头文件中,并在`dl`库中实现
它的主要作用是打开一个动态链接库文件,并将其加载到内存中,同时返回一个句柄(handle),供后续的函数调用使用
这个句柄是后续`dlsym`、`dlclose`等函数操作动态链接库的重要参数
函数原型如下: void dlopen(const char pathname, int mode); - `pathname`:这是动态链接库文件的路径
可以是绝对路径,也可以是相对路径
如果传递`NULL`,则`dlopen`会尝试打开调用进程本身的文件
- `mode`:这是打开动态链接库的模式标志,决定了符号解析的时机和作用范围
常用的标志包括`RTLD_LAZY`和`RTLD_NOW`
打开模式详解 - `RTLD_LAZY`:在`dlopen`返回前,对于动态库中的未定义的符号不执行解析(只对函数引用有效,对于变量引用总是立即解析)
这意味着符号的解析会延迟到实际使用时才进行
- `RTLD_NOW`:在`dlopen`返回前,解析出所有未定义符号
如果解析不出来,`dlopen`会返回`NULL`,并设置相应的错误信息
除了上述两种模式,还有其他一些标志,可以与解析方式通过“|”组合使用: - `RTLD_GLOBAL`:动态库中定义的符号可被其后打开的其它库重定位
这允许后续加载的库能够访问当前库中定义的符号
- `RTLD_LOCAL`:与`RTLD_GLOBAL`作用相反,动态库中定义的符号不能被其后打开的其它库重定位
如果没有指明是`RTLD_GLOBAL`还是`RTLD_LOCAL`,则默认使用`RTLD_LOCAL`
- `RTLD_NODELETE`:在`dlclose()`期间不卸载库,并且在以后使用`dlopen()`重新加载库时不初始化库中的静态变量
这个标志不是POSIX-2001标准
- `RTLD_NOLOAD`:不加载库
可用于测试库是否已加载(`dlopen()`返回`NULL`说明未加载,否则说明已加载),也可用于改变已加载库的标志
- `RTLD_DEEPBIND`:在搜索全局符号前先搜索库内的符号,避免同名符号的冲突
这个标志也不是POSIX-2001标准
使用示例 下面是一个使用`dlopen`、`dlsym`和`dlclose`函数的简单示例
这个示例演示了如何加载一个动态链接库,获取其中定义的函数指针,并调用该函数
假设我们有一个动态链接库`test_module.so`,其中定义了一个简单的函数`int add(int a, int b)`,用于计算两个整数的和
首先,我们编写并编译这个动态链接库:
// test_module.c
include