使用 `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 提供了极大的灵活性,但也需要开发者自己管理更多的细节,适合需要动态加载功能的场景。
相关推荐
wellc4 小时前
java进阶知识点
java·开发语言
听风吹等浪起4 小时前
用Python和Pygame从零实现坦克大战
开发语言·python·pygame
灰色小旋风4 小时前
力扣合并K个升序链表C++
java·开发语言
_MyFavorite_4 小时前
JAVA重点基础、进阶知识及易错点总结(28)接口默认方法与静态方法
java·开发语言·windows
Yzzz-F4 小时前
Problem - 2146D1 - Codeforces &&Problem - D2 - Codeforces
算法
取码网4 小时前
最新在线留言板系统PHP源码
开发语言·php
Kk.08024 小时前
力扣 LCR 084.全排列||
算法·leetcode·职场和发展
环黄金线HHJX.4 小时前
龙虾钳足启发的AI集群语言交互新范式
开发语言·人工智能·算法·编辑器·交互
Omics Pro4 小时前
虚拟细胞:开启HIV/AIDS治疗新纪元的关键?
大数据·数据库·人工智能·深度学习·算法·机器学习·计算机视觉