使用 `dlopen` 动态加载 `.so` 文件

使用 dlopen 动态加载 .so 文件

dlopen 是 Linux/Unix 系统提供的动态加载共享库的 API,它允许程序在运行时(而非编译时)加载和使用共享库中的函数。这种方式比 CMake 直接链接更灵活,适用于插件系统或需要动态加载功能的场景。

基本使用方法

1. 包含必要头文件

cpp#include <dlfcn.h> // dlopen 相关函数#include <iostream>

2. 加载共享库

cpp 复制代码
void* handle = dlopen("/path/to/library.so", RTLD_LAZY);if (!handle) {    std::cerr << "Cannot open library: " << dlerror() << std::endl;    return 1;}```
### 3. 获取函数指针
```cpp
// 定义函数指针类型typedef void (*my_func_t)(int);
// 获取函数my_func_t my_func = (my_func_t)dlsym(handle, "function_name");
if (!my_func) {    
	std::cerr << "Cannot load symbol: " << dlerror() << std::endl; 
   dlclose(handle);    
   return 1;
}

4. 使用函数

cppmy_func(42); // 调用动态加载的函数

5. 关闭库

cpp 复制代码
dlclose(handle);

完整示例

假设有一个共享库 libmath.so,其中包含 addsubtract 函数:

共享库代码 (math.cpp)

cpp 复制代码
extern "C" {    int add(int a, int b) { return a + b; }    int subtract(int a, int b) { return a - b; }}

编译为共享库:

bash 复制代码
g++ -shared -fPIC -o libmath.so math.cpp

主程序代码

cpp 复制代码
#include <dlfcn.h>
#include <iostream>
#include <string>
int main() {    
// 1. 打开共享库    
void* handle = dlopen("./libmath.so", RTLD_LAZY);   
 if (!handle) {        
 std::cerr << "Cannot open library: " << dlerror() << std::endl;        
 return 1;    
 }

// 2. 加载函数    
typedef int (*math_func_t)(int, int);        
math_func_t add = (math_func_t)dlsym(handle, "add");    
if (!add) {        std::cerr << "Cannot load symbol 'add': " << dlerror() << std::endl;        dlclose(handle);        return 1;    }    math_func_t subtract = (math_func_t)dlsym(handle, "subtract");    if (!subtract) {        std::cerr << "Cannot load symbol 'subtract': " << dlerror() << std::endl;        dlclose(handle);        return 1;    }    // 3. 使用函数    std::cout << "5 + 3 = " << add(5, 3) << std::endl;    std::cout << "5 - 3 = " << subtract(5, 3) << std::endl;    // 4. 关闭库    dlclose(handle);    return 0;}

编译主程序

bash 复制代码
g++ main.cpp -o main -ldl

重要注意事项

  1. 符号可见性 : - 共享库中的函数必须导出才能被 dlsym 找到 - 使用 extern "C" 避免 C++ 的名称修饰(name mangling)
    1. 错误处理 : - 每次 dlopendlsym 后都应检查错误 - dlerror() 返回最近一次错误的字符串描述
    1. 标志参数 : - RTLD_LAZY:延迟绑定,只在需要时解析符号(性能更好) - RTLD_NOW:立即解析所有符号(启动时检查所有符号是否存在) - RTLD_GLOBAL:使库的符号可用于后续加载的库 - RTLD_LOCAL:符号仅对本库可见(默认)
    1. 库搜索路径 : - 可以使用绝对路径或相对路径 - 也可以只指定库名(如 libmath.so),系统会在 LD_LIBRARY_PATH 中查找5. 内存管理 : - 确保在不再需要库时调用 dlclose - 多次 dlopen 同一个库会增加引用计数

在 CMake 项目中集成

cmake 复制代码
cmake_minimum_required(VERSION 3.10)
project(DynamicLoadExample)
add_executable(main main.cpp)
# 链接 dl 库target_link_libraries(main dl)
# 确保程序能找到共享库
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH "$ORIGIN")

高级用法

  1. 加载类: - 可以使用工厂函数创建类实例 - 在共享库中定义创建和销毁对象的函数
    1. 版本控制: - 可以在库中实现版本检查函数
    1. 插件系统 : - 扫描目录加载多个插件 - 定义标准接口供插件实现dlopen 提供了极大的灵活性,但也需要开发者自己管理更多的细节,适合需要动态加载功能的场景。
相关推荐
lqqjuly2 分钟前
前沿算法深度解析(二)
人工智能·算法·机器学习
Yolo_TvT1 小时前
C++:析构函数
c++
徐小夕1 小时前
万字长文!千万级文档 RAG 知识库系统落地实践
前端·算法·github
threelab2 小时前
Three.js 物理模拟着色器 | 三维可视化 / AI 提示词
开发语言·前端·javascript·人工智能·3d·着色器
武器大师722 小时前
lv_binding_js 代码解读
开发语言·javascript·ecmascript
不知名的老吴2 小时前
线程的生命周期之线程“插队“
java·开发语言·python
akunkuntaimei2 小时前
2026年高考数学各省真题及答案(完整版)
算法·高考
Hello:CodeWorld2 小时前
C 风格变参 vs C++ 变参模板:核心区别与选型指南
c语言·c++·算法
kaikaile19952 小时前
数字全息图处理系统(C# 实现)
开发语言·c#
8Qi83 小时前
LeetCode 516:最长回文子序列
算法·leetcode·职场和发展·动态规划