Linux动态库热加载驱动插件机制-示例
🎯 目标
构建一个插件式驱动系统,使主程序能够动态加载外部 .so
驱动,并通过接口调用相应功能。驱动类的注册通过工厂实现,支持 C++ 标准类型和继承体系。
📦 插件机制原理
- 使用
dlopen()
加载.so
插件 - 插件通过静态对象构造自动注册驱动到单例工厂
- 工厂类存储驱动构造函数(Lambda)
- 主程序根据名字获取驱动指针并调用接口
🧩 插件结构设计(以相机为例)
1. 驱动接口基类(CameraDriver.h)
#pragma once
#include <vector>
#include <cstdint>class CameraDriver {
public:virtual ~CameraDriver() = default;virtual void start() = 0;virtual std::vector<uint8_t> capture_image() = 0;
};
2. 工厂注册类模板(CameraRegistry.h)
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>template <typename T>
class CameraRegistry {
public:static CameraRegistry& instance() {static CameraRegistry inst;return inst;}void register_driver(const std::string& name, std::function<std::unique_ptr<T>()> creator) {creators_[name] = std::move(creator);}T* get(const std::string& name) {if (creators_.count(name)) {instances_[name] = creators_[name]();return instances_[name].get();}return nullptr;}private:std::unordered_map<std::string, std::function<std::unique_ptr<T>()>> creators_;std::unordered_map<std::string, std::unique_ptr<T>> instances_;
};
3. 插件注册辅助模板(RegisterMacro.h)
#pragma once
#include "CameraRegistry.h"
#include <string>template <typename T>
struct CameraRegister {CameraRegister(const std::string& name) {CameraRegistry<CameraDriver>::instance().register_driver(name, []() {return std::make_unique<T>();});}
};// 使用宏简化写法
#define REGISTER_CAMERA_DRIVER(CLASS) \\static CameraRegister<CLASS> reg_##CLASS(#CLASS);
4. 插件实现(如 libusb_camera.so)
#include "CameraDriver.h"
#include "RegisterMacro.h"
#include <iostream>class UsbCamera : public CameraDriver {
public:void start() override {std::cout << "UsbCamera started\\n";}std::vector<uint8_t> capture_image() override {return {42, 43, 44};}
};REGISTER_CAMERA_DRIVER(UsbCamera)
-
这里的
REGISTER_CAMERA_DRIVER(UsbCamera)
定义了一个全局静态对象reg_UsbCamera
。 -
动态库被
dlopen
加载时,该静态对象的构造函数会自动执行,完成注册。 -
主程序无需调用注册函数。
🚀 主程序加载插件并调用
#include "CameraDriver.h"
#include "CameraRegistry.h"
#include <dlfcn.h>
#include <iostream>int main() {void* handle = dlopen("libusb_camera.so", RTLD_LAZY);if (!handle) {std::cerr << "Failed to load plugin: " << dlerror() << "\\n";return 1;}// 不需要 dlsym 调用注册函数,加载动态库时自动注册// 使用注册的驱动CameraDriver* driver = CameraRegistry<CameraDriver>::instance().get("UsbCamera");if (driver) {driver->start();auto img = driver->capture_image();std::cout << "Image size: " << img.size() << "\\n";} else {std::cerr << "Driver not found\\n";}dlclose(handle);return 0;
}
🔧 常用系统函数说明
函数名 | 说明 |
---|---|
dlopen | 加载 .so 文件,返回句柄 |
dlsym | 获取动态库中的函数地址 |
dlclose | 卸载动态库 |
dlerror | 获取最近一次 dl 操作的错误信息 |
dlmopen | 支持加载多个命名空间的变种 dlopen |
✅ 总结
- 动态库加载时,全局静态对象构造自动注册驱动。
- 主程序直接通过工厂单例获取驱动实例。
- 避免手动调用注册函数和暴露 C 符号。
- 代码更简洁,符合现代 C++ 插件设计理念。