c++oding="utf-8" ?>
Linux下用dlopen/dlsym加载插件需编译为-fPIC -shared共享库,主程序调用dlopen打开.so,再用dlsym获取extern "C"导出的C风格函数地址,避免C++名字修饰导致查找失败。

如何用 dlopen/dlsym 在 Linux 上加载插件
Linux 下最轻量、最可控的动态插件方案就是直接用 dlfcn.h,不依赖任何框架。核心是:插件编译为共享库(.so),主程序在运行时调用 dlopen 打开它,再用 dlsym 获取导出函数地址。
关键点在于插件必须显式导出 C 风格符号——C++ 编译器会做名字修饰(name mangling),导致 dlsym 找不到函数。解决办法是在插件中用 extern "C" 包裹接口声明:
extern "C" {
PluginInterface* create_plugin();
void destroy_plugin(PluginInterface* p);
}
主程序里不能直接 static_cast 插件类型,而要定义一致的纯虚基类(如 PluginInterface),并确保 ABI 兼容(禁用 RTTI/异常、用 POD 类型传参)。
- 插件编译命令必须加
-fPIC -shared,例如:g++ -fPIC -shared -o myplugin.so myplugin.cpp -
dlopen失败时,用dlerror()查错误,常见原因是路径不对、缺少依赖或符号未导出 - 卸载前必须确保插件内部无活跃线程、无 pending 的异步回调,否则
dlclose行为未定义
Windows 上用 LoadLibrary 和 GetProcAddress 的注意事项
Windows 路径和符号导出规则更严格。插件必须是 .dll,且导出函数需用 __declspec(dllexport) 显式标记,或者通过 .def 文件导出。
主程序调用 LoadLibrary 时,路径必须是绝对路径或位于 DLL 搜索路径中(当前目录、PATH 环境变量等)。相对路径容易失败,尤其当工作目录被第三方库修改后。
- 推荐用
GetFullPathName将插件路径转为绝对路径再传给LoadLibrary -
GetProcAddress返回的是FARPROC,需强制转为具体函数指针类型,例如:auto create = (PluginInterface*(*)())GetProcAddress(h, "create_plugin"); - Windows 下
FreeLibrary不保证立即释放内存;若插件 DLL 依赖其他 DLL,卸载顺序错误会导致后续加载失败
插件生命周期管理:为什么不能靠 RAII 自动卸载
表面上看,把 dlopen / LoadLibrary 结果封装进 RAII 类很自然,但实际极易出错。问题不在构造,而在析构——插件可能正在执行回调、持有全局锁、或触发了静态对象析构期的跨模块调用。
更现实的做法是:插件加载后由主程序统一维护一个插件句柄池,提供显式的 unload() 接口,并要求插件实现 shutdown() 同步等待所有后台任务结束。
- 禁止在插件 DLL 的全局对象构造/析构函数中调用主程序函数或访问主程序全局变量
- 插件内避免使用
std::thread,改用主程序提供的任务调度器(如回调注册 + 主循环派发) - 如果插件用了
new分配内存,必须提供对应destroy函数,不能让主程序调用delete—— 两边可能链接不同版本的 libc++/MSVCRT
如何避免插件与主程序的 ABI 冲突
这是最容易被忽略却最致命的问题:只要主程序和插件链接了不同版本的 STL、不同编译器、或不同 C++ 标准(如主程序用 C++17,插件用 C++20),哪怕接口完全一致,也可能在传 std::string 或 std::vector 时崩溃。
根本解法是接口层彻底隔离 C++ ABI:所有跨边界数据都用 C 风格结构体 + 字符串指针 + 固定长度数组,或序列化为 const uint8_t* + 长度。插件只负责解析,不 new/delete 主程序传来的内存。
- 禁止跨模块传递 STL 容器、智能指针、异常、RTTI 类型信息
- 插件接口函数参数和返回值只允许:基本类型、
void*、C 结构体(不含虚函数、不含非 POD 成员)、const char* - 主程序可提供“内存分配器”回调(如
alloc_fn/free_fn),让插件按主程序规则申请/释放内存
ABI 不兼容不会立刻报错,往往在某个边缘场景(比如字符串长度刚好超过小字符串优化阈值)才崩溃,调试成本极高。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











