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 因具有高并发(特別是静态资源)、 占用系统资…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...
使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...
篇章二 论坛系统——系统设计
目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...
AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)
Name:3ddown Serial:FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名:Axure 序列号:8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP...
