有关DLL的几个问题

如何编写线程安全的DLL 在我的工作中经常会编写DLL,这些DLL有个共同的特点就是都有一个初始化函数,一个资源释放函数,其他几个函数都是核心功能函数。而且这些DLL有时会被多个进程同时调用,这就牵扯到多进程的多线程调用DLL的问题。有点绕口,以下我根据我实践中遇到的问题,分四种情况分享一下我解决此类问题的经验:1、动态库只有一个导出函数。这种情况非常少,也是最容易处理的情况。这种情况下编写函数时,只需要考虑不要有 阅读详情

一、DLL被多个进程调用问题

Win32系统会确保内存中只有一个该DLL的拷贝,这是通过内存映射文件来实现的。不同的进程分别将这份DLL的代码段地址映射到自己的进程空间中,同时不同的进程在自己的进程空间分别有各自的一份该DLL的数据段拷贝。

这是因为,在Win32环境中,每个进程都有了它自己的地址空间,DLL函数中的代码所创建的任何对象(包括变量)都归调用它的进程所有。当进程在载入DLL时,操作系统自动把DLL内存地址映射到该进程的私有空间,也就是进程的虚拟地址空间,而且也复制该DLL的全局数据的一份拷贝到该进程空间。(在物理内存中,多进程载入DLL时,DLL的代码段实际上是只加载了一次,只是将物理地址映射到了各个调用它的进程的虚拟地址空间中,而全局数据会在每个进程都分别加载)。也就是说每个进程所拥有的相同的DLL的全局数据,它们的名称相同,但其值却并不一定是相同的,而且是互不干涉的。因此,如果不采取特别的措施,DLL中的数据段不能跨进程共享。如果将将数据段的属性修改为共享的,这样该数据将被映像到所有的使用到该DLL的进程,即多个进程将共享该数据段

二、既然代码段共享,如果进程A中修改DLL的代码内容,使用同一DLL的其它进程B是否受影响?

不会!

这就涉及到一个内存的COPY ON WRITE 机制问题。

一个dll,被很多进程调用,为什么代码段共享,数据段不共享?就是因为 进程A调用这个dll,运行过程中,dll的数据段肯定会改变,那数据段就会被COPY一份,原来的依然存在不受影响。

当进程B调用这个dll,仍然用的是最开始的数据段,它往里写时再COPY一份到自己的进程空间,这就保证了两个进程A和B互不影响。

而由于代码段一般是不会被更改的,所以才提到代码段共享这个概念。但如果用hook等技术,把代码改了,代码同样会被COPY一份之后再改,所以不会影响其它的进程,只会影响你改的进程。于是可以解释下面某位同学所做的实验了:

 

在这里我做了一个试验,编写一个DLL和一个加载DLL并调用DLL中一个函数的程序,分别用二个OD加载这二个可执行程序调用DLL中的函数,为了说明加载的这个DLL分别位于不同的进程,没有任何的共享关系,我把二个进程同时一步一步调试,都执行到加载完DLL后,DLL的加载地址是一样的都0X10000000,接着我在第一个进程中转到DLL加载的地方,把MZ头用00填充,再一下F9,结果程序显示找不到调用的函数,接着再转到第二个进程,也转到0X10000000去看一下,这里没有被改动的迹象。F9一下,可以执行成功。说明加载的DLL虽然都在0X10000000,但是它们位于不同的进程,是不会相互干扰的


概括来说,Copy-On-Write机制,无论是代码段还是数据区,在一般情况下,如果没有被修改,都会被映射到相同的内存上,因此当加载同一个dll的时候,内存中只有一份dll。如果有内容被修改后,会构造副本,重新映射

 

三、DLL中在堆上分配的空间,能否在主程序中直接释放?

通常不行,谁负责分配,谁就必须负责释放
 原因之一就是因为DLL的调用是有被计数的,为了防止还有调用程序正在引用DLL,你不应该在程序中释放内存. 比如有一部分共享内存的数据,在进程A中释放了,进程B任然在使用,肯定会出问题。所以让DLL自己来释放,自己来管理。

原因之二就是因为   malloc/free,   new/delete   都是调用   HeapAlloc/HeapFree   来实现来实现内存分配是释放的。查看Windows的 API可以看到,这两个函数都需要一个Heap的HANDLE做为参数。CRT库采用了全局变量来保存这个HANDLE。如果是静态链接,CRT库的代码会链接到各个dll中去,也包括这个全局变量。也就是说,每个静态链接的   dll或EXE   都有一个自己的全局堆句柄,他们都在自己的这个句柄上使用内存。当在一个 dll或EXE中释放另外一个dll分配的内存时,由于使用的堆句柄不一致于是出错。当使用动态链接时,由于每个dll都是去调用CRT库的dll函数来分配和释放内存的,使用的是同一个句柄,所以就没有这个问题。

四、DLL加载的顺序

一个进程开始运行时,先会检查自己的导入表,看一下依赖的DLL有哪些,发现有依赖的DLL,那么就检查内存中是否有这个DLL了。如果没有,DLL首先被调入Win32系统的全局堆栈,然后映射到调用这个DLL的进程的地址空间。如果存在了,直接映射该DLL的代码段地址。因此不管该进程有多少一个实例,这个过程都是要做的。然后涉及到DLL的初始化问题,参考http://www.cppblog.com/AutomateProgram/archive/2010/03/15/109718.html,写的很好。

NoUnityCN:绕过地域限制,从Unity官方CDN直链下载全版本编辑器 在软件开发和游戏制作领域,内容分发网络(CDN)是保障全球用户高速、稳定获取大型文件的核心基础设施。其原理是通过将资源缓存至遍布各地的边缘节点,使用户能从地理距离最近的服务器获取数据,从而显著提升下载速度并降低源站压力。这一技术对于需要频繁下载大型开发工具(如游戏引擎)的团队而言,价值在于能确保开发环境部署的一致性与效率,是构建自动化CI/CD流水线和团队协作的基础。然而,当开发者因网络策略或地域限制无法直接访问官方CDN时,获取安装文件便成为阻碍工作流的痛点。NoUnityCN项目正是针对此场景的工程实践 阅读详情

相关推荐

HDMI设计6--Video PHY Controller IP

Reference:https://china.xilinx.com/content/dam/xilinx/support/documentation/ip_documentation/vid_phy_controller/v2_2/pg230-vid-phy-controller.pdf 1. 序 Video PHY Controller IP是为实现与视频(DisplayPort和HDMI™技术)MAC传输或接收子系统的即插即用连接而设计的。视频MAC层和PHY层的接口是标准化的,能...

Archar_Saber的博客 8338

同一个dll被多个进程使用的数据共享(Data_seg)

同一个dll被多个进程使用的时候,在大多数情况下,各个进程使用dll和独自使用dll效果是相同的。 因为对于dll中的局部变量是写入到进程的堆栈中的,全局变量也被操作系统为每个线程copy一份各自使用。 you can use either of the following approaches: · Create named data sections using the data_seg pragma. · Use memory mapped files. See t

春夜喜雨的专栏 6150

西门子PLC实战:基于1200/1500的工业物联网MQTT通讯全解析

本文详细解析了如何在西门子S7-1200/1500系列PLC上实现工业物联网MQTT通讯。通过从硬件准备、网络配置到TIA Portal中MQTT_Client功能块的实战编程,手把手指导工程师打通车间数据桥梁,解决信息孤岛问题,实现PLC数据高效上云或设备间互联。

whisky的博客 1089

使用DLL在多个进程间共享全局变量

     默认情况下,同一个程序启动多个进程,它们各自的变量值是不会相互影响的。第二个实例启动后,在修改全局变量的时候,系统会运用内存管理系统copy- on-write的特性来防止修改了第一个实例的数据,即系统会再分配一些内存,并将全局变量复制到这块内存中,每个实例使用自己的内存空间上的数据而互不影响。有时候我们想在多个进程间共享数据,windows提供了这种方法,就是创建自己的共享数据节,

呆若木鸡的专栏 6719

易智联LM401模块学习(四)——双串口收发

串口的发送和接受有三种方式,分别是阻塞发送和接收(轮询)、中断方式发送和接收、DMA方式发送和接收。这里我使用阻塞+中断的方式实现双串口通信。简单的串口收发。

weixin_46046597的博客 990

1688店铺所有商品API接口(整店商品查询API接口)

1688店铺所有商品API接口、1688店铺列表API接口,1688店铺API接口,1688店铺查询商品API接口,1688店铺商品API接口,1688店铺详情接口,1688商品API接口

tbprice的博客 1568

使用DLL调用防止程序多次运行的实例程序

一、命名空间namespace   命名空间是一个域,这在个域中所有的类型名字必须是唯一的,不同的类型分组归入到层次化的命名空间,命名空间的好处是:1、避免名字冲突,2、便于查找类型名字。 1、定义命名空间   命名空间的定义是以关键字 namespace 开始,后跟命名空间的名称 2、使用命名空间   using 关键字表明程序使用的是给定命名空间中的名称。 3、嵌套命名空间   命名空间可以被嵌套,即您可以在一个命名空间内定义另一个命名空间。您可以使用点(.)运算符访问嵌套的命名空间的成员。 二、C#如

dalong10的专栏 568

【仿真】Carla Python API实战:从零搭建自动驾驶测试环境

本文详细介绍了如何使用Carla Python API从零搭建自动驾驶测试环境。通过Carla仿真平台,开发者可以构建高度逼真的城市环境和车辆物理模型,利用Python API实现传感器配置、数据采集、场景构建及自动驾驶算法集成。文章还分享了实际项目中的调试技巧和性能优化经验,帮助读者快速掌握自动驾驶仿真测试的核心技术。

weixin_29050513的博客 212

JNA调用DLL函数遇到的几个问题

最近一个JSP项目需要用到分词模块,而分词模块实用C++写成的DLL库。于是上网搜各种方法,最后选择了JNA作为JSP调用DLL的工具。 JNA(Java Native Access )提供一组Java工具类用于在运行期动态访问系统本地库(native library:如Window的dll)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native li

雷霄骅(leixiaohua1020)的专栏 3878

如何用Python打造PubMed API客户端:科研文献检索自动化实践

PubMed提供了官方的Entrez E-utilities API接口,支持程序化检索文献,但直接调用API需处理请求重试、参数封装、数据解析等问题。批量获取文献ID及结构化详情(标题、作者、DOI等)内置重试机制,应对网络波动或API限流统一数据输出格式,便于后续分析(如文献计量、AI文本处理)灵活适配不同检索需求(如指定数据库、结果数量)本文实现的PubMed API客户端,通过封装API请求、重试机制、数据解析,让科研文献检索从“手动点击”变为“代码自动化”。

ASKCOS博客 357

YOLO26改进策略【小目标改进】| 添加专用于小目标的检测层 附YOLOv1~YOLOv11的检测头变化详解

在目标检测领域,小目标检测一直是一个具有挑战性的问题。算法以其高效快速的特点受到广泛关注,然而在面对小目标时,仍存在一些局限性。本文将介绍如何在中添加小目标检测层,以提高对小目标的检测能力。原始模型结构如下: 二、有效特征层对应的检测头类别 2.1 P3/8 - small检测头 原始模型中的对应的检测头主要用于检测相对较小的目标。其特征图大小相对较大,空间分辨率较高。 适合检测尺寸大概在到像素左右的目标。 2.2 P4/16 - medium检测头 这个检测头对应的经过了更多的下采样操作,相比P3/

Limiiiing的博客 348

关于以dll调用dll的几个小问题

在做“以一个dll调用另一个dll”时想到的几个问题和答案。

FinixLei的专栏 (https://github.com/FinixLei) 1175

视频流推理的工程架构:YOLO模型如何做到单卡处理16路1080P实时流?

<think>我们只需要根据内容生成摘要,不超过150字。内容是关于YOLO模型单卡处理16路1080P实时流的技术分析,包括方案对比、架构设计等。摘要要简洁,突出关键点。</think>本文深度解析了YOLO模型单卡处理16路1080P实时流的技术方案,对比EagleEye、TensorRT+DeepStream、华为CANN及RK3588四条路线,重点介绍流水线架构、并发模型及显存优化,指出消费级显卡(如RTX 4090)即可实现高并发智能视觉分析。

2601_95807009的博客 161

关于DLL的几个问题的解决方案

1. error C2664: “LoadLibraryW”: 不能将参数 1 从“const char *”转换为“LPCWSTR”。 解决方案:动态调用DLL,通过LoadLibrary、GetProcAddress、FreeLibrary实现。原因 :工程只支持UNICODE字符。 工程属性->配置属性-->常规--->字符集---->使用多字节符字符集 也就是宽字符,所以下面

GeekWill的博客 2400

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

在Delphi的DLL中使用FastReport要处理的几个问题

在dll中使用FastReport会遇到以下内个问题:1.调用时变量出错,最普通的一个变量注入也是一样2.出现Cannot assign a TFieldList to a TStringList的错误3.Invalid pointer operation的错误,在函数中返回WideString引起的以下是解决办法:1.不要在DLL中直接引用FastReport的单元文件,最好用一个接口类来实现

Rolkey 小家 3266

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

JSP使用JNA调用DLL函数遇到的几个问题

最近一个JSP项目需要用到分词模块,而分词模块实用C++写成的DLL库。于是上网搜各种方法,最后选择了JNA作为JSP调用DLL的工具。 JNA(Java Native Access )提供一组Java工具类用于在运行期动态访问系统本地库(native library:如Window的dll)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native lib...

蚂蚁学Python 344

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

PB PSLIB21.DLL 搭建SOCKET CLIENT的几个问题

PB使用PSLIB21.DLL 可以搭建SOCKET应用。下面对建SOCKET CLIENT的要注意的几个问题 一般都是在窗体或用户对象上动态打开uo_socket_client。如果业务逻辑可以放在窗体或用户对象上时,当打开uo_socket_client后,要窗体或用户对象传到uo_socket_client 在做CONNECT时,有时返回值是-1但GETLASTERRROR后的值是0,实际上CONNECT已经成功,问题原因不详。 如果检测到服务端停止服务或断开连接,最好有相应的处理,即在窗体或用户对象

notbig的博客 1312

C#调用Delphi DLL时,要注意的几个问题。

c# 调用Delphi的dll时, 1.如果dll中的函数参数含有var,则c#中要加上ref(引用);否则,会有提示错误:“尝试读取或写入受保护的内容。这通常指示其他内存已损坏”。 2.如果dll中的参数是THandle类型,在c#中用IntPtr代替。 3.如果dll中的参数是PChar类型,在c#中应该用byte[]代替。 使用网上说的用ref string或StringBuilde...

weixin_30267697的博客 228

快速解决《黑神话:悟空》中AkMatrixReverb.dll丢失问题的几个高效方法

【代码】快速解决《黑神话:悟空》中AkMatrixReverb.dll丢失问题的几个高效方法。

2401_87099071的博客 559
上一篇: MSCOMCTL.OCX 中的LISTVIEW控件各类异常终极解决方案
下一篇: 如何扑捉到WM_NCLBUTTONUP(OnNcLButtonUp)消息
SONG_CA
博客等级 码龄24年 74粉丝 20原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值