📌 什么是热加载(Hot Reload)
- 指主程序在不重启的前提下,通过动态方式加载(或重新加载)动态库(
.so
文件),使得逻辑可以在运行时替换或更新。 - 热加载通常需要重新编译
.so
,但不需要重新运行主程序。
🛠 基本流程
void* handle = dlopen("libplugin.so", RTLD_LAZY);
typedef int (*AddFunc)(int, int);
AddFunc add = (AddFunc)dlsym(handle, "add");
int result = add(1, 2);
dlclose(handle);
🔧 主要系统函数说明(<dlfcn.h>)
函数名 | 作用 |
---|
dlopen | 加载动态库,返回句柄 void* |
dlsym | 从句柄中查找符号(函数或变量)地址 |
dlclose | 卸载已加载的动态库 |
dlerror | 获取最近一次 dlopen / dlsym / dlclose 的错误信息 |
示例:
void* handle = dlopen("libplugin.so", RTLD_LAZY);
if (!handle) {fprintf(stderr, "dlopen error: %s\n", dlerror());
}
⚠️ extern “C” 的必要性
- C++ 编译器会对函数名做“名称修饰”(name mangling),导致
dlsym
无法识别函数名。 - 加上
extern "C"
让函数以 C 的方式导出,无名称修饰。
extern "C" int add(int a, int b);
📦 动态库函数的参数设计建议
参数类型 | 是否推荐 | 说明 |
---|
int , float | ✅ | 简单类型,兼容性好 |
const char* | ✅ | 字符串以 C 风格传递 |
void* | ✅ | 通用指针,可用于传结构体地址等 |
std::string | ❌ | 不推荐,C++ 类型,ABI 不稳定 |
std::vector | ❌ | 不推荐,使用裸数组 + size 替代 |
自定义 struct | ⚠️ | 结构体需对齐一致、layout 固定 |
🌀 动态更新库的流程(热加载)
- 主程序运行;
- 加载
libplugin.so
; - 程序运行中,卸载该库(
dlclose
); - 替换或重新编译
libplugin.so
; - 主程序再次加载,即可使用新逻辑。
🧪 编译示例
1. 动态库(plugin.cpp)
extern "C" int add(int a, int b) {return a + b;
}
g++ -fPIC -shared -o libplugin.so plugin.cpp
2. 主程序(main.cpp)
#include <dlfcn.h>
#include <iostream>typedef int (*AddFunc)(int, int);int main() {void* handle = dlopen("./libplugin.so", RTLD_LAZY);if (!handle) {std::cerr << "dlopen failed: " << dlerror() << std::endl;return 1;}AddFunc add = (AddFunc)dlsym(handle, "add");if (!add) {std::cerr << "dlsym failed: " << dlerror() << std::endl;dlclose(handle);return 1;}std::cout << "1 + 2 = " << add(1, 2) << std::endl;dlclose(handle);return 0;
}
g++ main.cpp -ldl -o main