C++动态加载 插件
动态加载(Dynamic Loading)是指在程序运行时,根据需要动态地加载和链接代码或资源。
动态加载的主要目的是实现程序的灵活性和可扩展性,以及减少内存消耗和启动时间。通过动态加载,程序可以根据运行时的需求加载特定的模块、库或插件,而不需要将所有代码和资源都打包在一起。这样可以实现模块化设计,减少程序的体积,以及在需要时进行动态升级和扩展。
在C++中,常见的动态加载方式是使用动态链接库(Dynamic Link Library,DLL)或共享对象(Shared Object,SO)文件。通过使用操作系统提供的动态链接库机制,程序可以在运行时加载、链接和调用动态链接库中的函数和数据。
动态加载通常包括以下步骤:
加载库:程序使用操作系统提供的函数加载指定的动态链接库或共享对象文件,并获得一个句柄或句柄指针。
解析符号:程序使用操作系统提供的函数根据函数名或符号名称从动态链接库中获取对应的函数指针或变量地址。
使用功能:程序通过获取到的函数指针或变量地址,可以调用动态链接库中的函数或访问其中的变量。
卸载库:当不再需要使用动态链接库时,程序使用操作系统提供的函数(如dlclose())关闭句柄,并释放相关资源。
动态加载可以在运行时根据需求选择加载特定的模块或插件,从而实现更灵活和可扩展的软件架构。它常用于插件系统、模块化设计、动态升级和动态扩展等场景。同时,需要注意合理管理和使用动态加载,在加载和卸载过程中保证正确性和安全性,避免内存泄漏或资源冲突等问题。
常用的动态加载库的方式
1. 使用dlopen()和dlsym()函数(UNIX-like系统):
UNIX-like系统是指类似于UNIX操作系统的系统家族
Linux:
Linux是一种基于UNIX设计原则的开源操作系统。它以自由、开放源代码和高度可定制性而闻名,并且广泛用于服务器、嵌入式系统和个人计算机等领域。
macOS:
macOS是苹果公司开发的基于UNIX的操作系统,主要用于苹果的Mac电脑和服务器设备。它具有直观的用户界面和强大的开发者工具,支持多种应用程序和开发环境。
FreeBSD:
FreeBSD是一个自由、开源的UNIX-like操作系统,它是从BSD项目分支出来的一个分支。FreeBSD具有高度可靠性、稳定性和安全性,广泛应用于服务器和网络设备。
Solaris:
Solaris是由甲骨文公司(Oracle)开发和支持的UNIX操作系统。它具有强大的网络功能、可扩展性和可靠性,广泛用于企业级服务器和高性能计算领域。
包括但不限于以上几种常见的操作系统。
使用dlopen()和dlsym()函数的常见限制:
- 平台限制:
dlopen()和dlsym()函数是针对UNIX-like系统的动态链接库机制,因此在不同平台上的可用性和行为可能会有所不同。比如,Windows系统使用的是LoadLibrary()和GetProcAddress()函数。 - 符号查找限制:
dlsym()函数是通过符号名称来查找并返回对应的函数指针或变量地址。因此,对于不可重定位的代码或使用了隐藏符号的库,dlsym()可能无法直接获取到这些符号。可以通过在编译时使用-fvisibility=hidden选项来隐藏符号,但需要提供额外的导出接口。 - 符号命名限制:
当使用dlsym()查找函数时,函数名需要与库中定义的符号名称完全匹配。这包括符号名称的大小写、命名空间和重载等因素。如果符号名称没有正确匹配,dlsym()将无法获取到函数指针。 - 依赖关系处理:
dlopen()和dlsym()函数不会自动处理库之间的依赖关系。如果一个库依赖于其他库,需要手动调用dlopen()加载所有依赖库,并确保它们按照正确的顺序加载和链接。 - 版本兼容性:
动态加载的库可能会在不同的版本之间发生变化,导致函数签名、结构体布局或符号名称的变化。在使用dlsym()获取函数指针时,需要确保库的版本兼容性,以免出现函数调用错误或内存访问错误。 - 安全性问题:
动态加载库的方式可能存在一些安全性问题,如潜在的代码注入、恶意库替换等。因此,在使用dlopen()和dlsym()时需要谨慎处理,确保加载和使用的库来自可信源,并进行适当的安全检查。
#include <dlfcn.h> // 动态链接库相关头文件
#include <iostream>
int main() {// 加载库void* libraryHandle = dlopen("./libexample.so", RTLD_LAZY);if (!libraryHandle) {std::cerr << "Failed to load library: " << dlerror() << std::endl;return 1;}// 获取库中的函数指针typedef void (*FunctionPtr)();FunctionPtr function = reinterpret_cast<FunctionPtr>(dlsym(libraryHandle, "exampleFunction"));if (!function) {std::cerr << "Failed to get function: " << dlerror() << std::endl;dlclose(libraryHandle);return 1;}// 使用函数function();// 关闭库dlclose(libraryHandle);return 0;
}
2. 使用LoadLibrary()和GetProcAddress()函数(Windows平台):
使用LoadLibrary()和GetProcAddress()函数的常见限制:
- 平台限制:
LoadLibrary()和GetProcAddress()函数是Windows操作系统特有的函数,因此只能在Windows平台上使用。在其他操作系统上,使用dlopen()和dlsym()函数进行动态加载库。 - 文件路径限制:
LoadLibrary()函数需要指定库文件的完整路径,或者是根据Windows系统的搜索规则进行查找。如果库文件不存在或无法找到,LoadLibrary()将失败。因此,需要确保提供正确的库文件路径。 - 符号查找限制:
GetProcAddress()函数是通过函数名来查找并返回对应的函数指针。与dlsym()类似,对于不可重定位的代码或使用了隐藏符号的库,GetProcAddress()可能无法直接获取到这些符号。可以通过在编译时使用__declspec(dllexport)来导出符号,但需要提供额外的导出接口。 - 符号命名限制:
当使用GetProcAddress()查找函数时,函数名需要与库中定义的符号名称完全匹配。这包括符号名称的大小写、命名空间和重载等因素。如果符号名称没有正确匹配,GetProcAddress()将无法获取到函数指针。 - 依赖关系处理:
LoadLibrary()和GetProcAddress()函数不会自动处理库之间的依赖关系。如果一个库依赖于其他库,需要手动调用LoadLibrary()加载所有依赖库,并确保它们按照正确的顺序加载和链接。 - 版本兼容性:
动态加载的库可能会在不同的版本之间发生变化,导致函数签名、结构体布局或符号名称的变化。在使用GetProcAddress()获取函数指针时,需要确保库的版本兼容性,以免出现函数调用错误或内存访问错误。 - 安全性问题:
动态加载库的方式可能存在一些安全性问题,如潜在的代码注入、恶意库替换等。因此,在使用LoadLibrary()和GetProcAddress()时需要谨慎处理,确保加载和使用的库来自可信源,并进行适当的安全检查。
#include <windows.h> // Windows平台下的头文件
#include <iostream>
int main() {// 加载库HINSTANCE libraryHandle = LoadLibrary(TEXT("example.dll"));if (!libraryHandle) {std::cerr << "Failed to load library: " << GetLastError() << std::endl;return 1;}// 获取库中的函数指针typedef void (*FunctionPtr)();FunctionPtr function = reinterpret_cast<FunctionPtr>(GetProcAddress(libraryHandle, "exampleFunction"));if (!function) {std::cerr << "Failed to get function: " << GetLastError() << std::endl;FreeLibrary(libraryHandle);return 1;}// 使用函数function();// 关闭库FreeLibrary(libraryHandle);return 0;
}
完整示例
创建插件接口:
// PluginInterface.h
class PluginInterface {
public:virtual void doSomething() = 0;
};
创建插件实现:
// PluginImplementation.cpp
#include "PluginInterface.h"
#include <iostream>
class PluginImplementation : public PluginInterface {
public:void doSomething() override {std::cout << "Plugin is doing something!" << std::endl;}
};
// 导出插件实现的函数
extern "C" PluginInterface* createPlugin() {return new PluginImplementation();
}extern "C" void destroyPlugin(PluginInterface* plugin) {delete plugin;
}
编译插件为动态链接库或共享对象文件:
$ g++ -shared -fPIC PluginImplementation.cpp -o plugin.so
在主程序中加载插件并使用功能:
// Main.cpp
#include "PluginInterface.h"
#include <dlfcn.h> // 动态链接库相关头文件
#include <iostream>
int main() {// 加载插件void* pluginHandle = dlopen("./plugin.so", RTLD_LAZY);if (!pluginHandle) {std::cerr << "Failed to load plugin: " << dlerror() << std::endl;return 1;}// 获取插件中的函数指针typedef PluginInterface* (*CreatePluginFunc)();CreatePluginFunc createPlugin = reinterpret_cast<CreatePluginFunc>(dlsym(pluginHandle, "createPlugin"));if (!createPlugin) {std::cerr << "Failed to get createPlugin function: " << dlerror() << std::endl;dlclose(pluginHandle);return 1;}// 创建插件实例PluginInterface* plugin = createPlugin();// 使用插件功能plugin->doSomething();// 销毁插件实例typedef void (*DestroyPluginFunc)(PluginInterface*);DestroyPluginFunc destroyPlugin = reinterpret_cast<DestroyPluginFunc>(dlsym(pluginHandle, "destroyPlugin"));if (!destroyPlugin) {std::cerr << "Failed to get destroyPlugin function: " << dlerror() << std::endl;dlclose(pluginHandle);return 1;}destroyPlugin(plugin);// 关闭插件dlclose(pluginHandle);return 0;
}
在上述示例中,使用了extern "C"来修饰createPlugin()函数。这是因为该函数是作为插件接口中的一个导出函数,它将被主程序动态加载并调用。通过使用extern "C"修饰,确保了函数名在生成的符号表中按照C语言的规则进行命名,以便主程序能够正确地找到并调用该函数。
需要注意的是,在使用extern "C"修饰函数时,需要确保该函数的声明和定义都位于extern "C"块内或者使用extern "C"修饰。这样才能保证函数名的一致性,避免因为名称修饰导致的链接错误。
相关文章:
C++动态加载 插件
动态加载(Dynamic Loading)是指在程序运行时,根据需要动态地加载和链接代码或资源。 动态加载的主要目的是实现程序的灵活性和可扩展性,以及减少内存消耗和启动时间。通过动态加载,程序可以根据运行时的需求加载特定的…...

redis的缓存更新策略
目录 三种缓存更新策略 业务场景: 主动更新的三种实现 操作缓存和数据库时有三个问题 1.删除缓存还是更新缓存? 2.如何保证缓存与数据库的操作的同时成功或失败? 3.先操作缓存还是先操作数据库? 缓存更新策略的最佳实践方案&am…...

Android应用开发(6)TextView进阶用法
Android应用开发学习笔记——目录索引 本章介绍文本视图(TextView)的显示,包括:设置文本内容、设置文本大小、设置文本显示颜色。 一、设置TextView显示内容 Layout XML文件中设置 如:res/layout/activity_main.xm…...

Matlab滤波、频谱分析
Matlab滤波、频谱分析 滤波: 某目标信号是由5、15、30Hz正弦波混合而成的混合信号,现需要设计一个滤波器滤掉5、30Hz两种频率。 分析:显然我们应该设计一个带通滤波器,通带频率落在15Hz附近。 % 滤波 % 某目标信号是由5、15、3…...

车载软件架构 —— 车载软件入侵检测系统
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 没有人关注你。也无需有人关注你。你必须承认自己的价值,你不能站在他人的角度来反对自己。人…...
“深入解析JVM内部机制:理解Java虚拟机的工作原理“
标题:深入解析JVM内部机制:理解Java虚拟机的工作原理 摘要:本文将深入探讨Java虚拟机(JVM)的内部机制,解释其工作原理。我们将讨论JVM的组成部分、类加载过程、运行时数据区域以及垃圾回收机制。此外&…...

FPGA初步学习之串口发送模块【单字节和字符串的发送】
串口相关简介 UART 在发送或接收过程中的一帧数据由4部分组成,起始位、数据位、奇偶校验位和停止位,如图所示。其中,起始位标志着一帧数据的开始,停止位标志着一帧数据的结束,数据位是一帧数据中的有效数据。 通常用…...
Kotlin重点理解安全性
目录 一 Kotlin安全性1.1 可空类型1.2 安全调用运算符1.3 Elvis 运算符1.4 非空断言运算符1.5 安全类型转换1.6 延迟初始化 一 Kotlin安全性 Kotlin 在设计时采用了一系列策略,旨在尽可能地减少空指针异常(NullPointerException)的出现。空指…...

基于Java+SpringBoot+SpringCloud+Vue的智慧养老平台设计与实现(源码+LW+部署文档等)
博主介绍: 大家好,我是一名在Java圈混迹十余年的程序员,精通Java编程语言,同时也熟练掌握微信小程序、Python和Android等技术,能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…...
Spring中的全局异常处理
在项目中通常会定义各种异常,比如断言异常、业务异常,甚至调用外部接口报的异常需要转成自己系统中的异常类型,这时就需要将各种异常类型进行统一的处理,返回统一的数据结构。在spring中提供了这样全局统一处理异常的注解Controll…...

【安全测试】Web应用安全之XSS跨站脚本攻击漏洞
目录 前言 XSS概念及分类 反射型XSS(非持久性XSS) 存储型XSS(持久型XSS) 如何测试XSS漏洞 方法一: 方法二: XSS漏洞修复 原则:不相信客户输入的数据 处理建议 资料获取方法 前言 以前都只是在各类文档中见到过XSS,也进…...

LeNet卷积神经网络-笔记
LeNet卷积神经网络-笔记 手写分析LeNet网三卷积运算和两池化加两全连接层计算分析 修正上图中H,W的计算公式为下面格式 基于paddle飞桨框架构建测试代码 #输出结果为: #[validation] accuracy/loss: 0.9530/0.1516 #这里准确率为95.3% #通过运行结果可以看出&am…...

使用XMLHttpRequest实现文件异步下载
1、问题描述 我想通过异步的方式实现下载文化,请求为post请求。一开始我打算用ajax。 $.ajax({type:post,contentType:application/json,url:http://xxx/downloadExcel,data:{data:JSON.stringify(<%oJsonResponse.JSONoutput()%>)},}).success(function(dat…...

Lombok 的安装与使用
文章目录 一、什么是 Lombok1.1 Lombok 的概念1.2 为什么使用 Lombok1.3 Lombok 的相关注解 二、Lombok 的安装2.1 引入依赖2.2 安装插件 三、Lombok 的使用案例四、Lombok 的原理 一、什么是 Lombok 1.1 Lombok 的概念 Lombok(“Project Lombok”)是一…...

springBean生命周期解析
本文基于Spring5.3.7 参考: kykangyuky Spring中bean的生命周期 阿斌Java之路 SpringBean的生命周期, 杨开振 JavaEE互联网轻量级框架整合开发 黑马程序员 JavaEE企业级应用开发教程 马士兵 Spring源码讲解 一. SpringBean生命周期流程图 二. 示例代码 …...

人工智能轨道交通行业周刊-第54期(2023.7.31-8.6)
本期关键词:BIM智能运维、铁水联运、编组站美容、鸿蒙4.0、LK-99完全悬浮 1 整理涉及公众号名单 1.1 行业类 RT轨道交通人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通RailMetro轨道世界…...

Docker Compose 使用方法
目录 前言 安装 Docker Compose Ubuntu 安装与更新 Red Hat 安装与更新 验证是否安装 Docker Compose 创建 docker-compose.yml 文件 创建一个MySQL 与 tomcat 示例 使用Docker Compose启动服务 前言 Docker Compose 是一个工具,旨在帮助定义和 共享多容器…...

HTML 初
前言 HTML的基本骨架 HTML基本骨架是构建网页的最基本的结果。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">…...

IPv6地址分类,EUI-64转换规则
1、可聚合的单全球单播地址Global Unique Address: Aggregate global unicast address,前3位是001,即2000::/3,目前IANA已经将一部分可聚合全球单播进行了专门使用,如:2001::/16用于IPV6互联网,…...

Nginx安装部署
什么是Nginx? Nginx(发音同engine x)是一款由俄罗斯程序员Igor Sysoev所开发轻量级的网页服务器、反向代 理服务器以及电子邮件(IMAP/POP3)代理服务器。 Nginx 因具有高并发(特別是静态资源)、 占用系统资…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...

现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...