什么是插件
插件(Plug-In) 又称:加载项(Add-On), 外接程序(Add-In),扩展(Extension)等等, 是一种通过动态加载功能模块(即插件)来增强主程序功能的软件设计策略。通过制定标准化接口,确保插件与主程序之间的兼容性与独立性,并方便功能拓展。插件技术广泛应用于IDE开发、游戏引擎、多媒体处理等场景。
插件的基本架构
插件的原理
C++使用动态加载技术即运行时加载:在运行过程中通过系统调用加载,主动加载多个接口一致动态库。
- (动态链接)通常情况:一个程序启动加载dll是在链接器确定的,并且该符号不能重复
- 动态加载:程序在运行期主动加载或者卸载,可以有相同的符号
可以看出来,插件实现在于规范了接口,以保证各个插件的接口统一。而动态加载则保证了插件可以正常加载到程序中。
从依赖的建立来看,动态链接依赖在链接的时候建立的。一旦exe发布后,这个依赖就存在而不可以打破。动态加载则是在exe运行加载时建立的即exe系统调用加载动态库的时候
动态库的加载时机
- 动态链接:在exe启动的时候加载
- 延时加载:在程序运行过程中第一次用到依赖库导出的符号再加载
- MVSC:/DDLAYLOAD: demo.dll (符号重载决议)在调用函数和
- 动态加载: 程序员自动控制加载
- 系统API
实现一个简单的插件
- plugin
//定义插件的公共接口
class IPlugin {
public:
virtual void initialize() = 0;
virtual void execute() = 0;
virtual void cleanup() = 0;
};
- Helloplugin
#include <iostream>
class HelloPlugin : public IPlugin {
public:
void initialize() override {
std::cout << "HelloPlugin initialized." << std::endl;
}
void execute() override {
std::cout << "Hello from HelloPlugin!" << std::endl;
}
void cleanup() override {
std::cout << "HelloPlugin cleaned up." << std::endl;
}
};
extern "C" IPlugin* create() {
return new HelloPlugin();
}
extern "C" void destroy(IPlugin* plugin) {
delete plugin;
}
- Pluginmanager
#include "IPlugin.h"
#include <string>
#include <vector>
#include <dlfcn.h> // Linux下的动态链接库头文件
class PluginManager {
public:
void loadPlugin(const std::string& path) {
void* handle = dlopen(path.c_str(), RTLD_LAZY);
if (!handle) {
std::cerr << "Cannot load plugin: " << dlerror() << std::endl;
return;
}
// 获取创建插件的函数
IPlugin* (*create)();
create = (IPlugin* (*)())dlsym(handle, "create");
if (!create) {
std::cerr << "Cannot load create function: " << dlerror() << std::endl;
return;
}
// 创建插件实例
IPlugin* plugin = create();
plugin->initialize();
plugins.push_back(std::make_pair(plugin, handle));
}
void executePlugins() {
for (auto& p : plugins) {
p.first->execute();
}
}
void unloadPlugins() {
for (auto& p : plugins) {
p.first->cleanup();
dlclose(p.second);
delete p.first;
}
plugins.clear();
}
private:
std::vector<std::pair<IPlugin*, void*>> plugins;
};
- application
#include "PluginManager.h"
int main() {
PluginManager manager;
manager.loadPlugin("./HelloPlugin.so");
manager.executePlugins();
manager.unloadPlugins();
return 0;
}
从上面可以看出来插件的设计主要关注以下几点
- 动态库加载(跨平台)
- 内存管理
- 插件接口如何设计
- 插件生命周期管理
- 插件间通信
- 各个插件的依赖项版本不一致的管理
- 各个插件的公共库管理
插件系统的拓展
- 应用层
- 热插拔(替换新插件)
- 自动文件检测 (拖入新插件,插件本身的含义)
- 插件接口
- 函数注册版 + 配置文件
- rpc服务
- 函数注册版 + 配置文件