DLL内存分配与共享的问题,请熟悉操作系统和C/C++的高手指点
在网上找到一篇关于DLL的介绍,其中有些内容如下,其中有我关心的内容,但是有些不理解:
<quote>
一旦DLL的文件映像被映射到调用进程的地址空间中,DLL的函数就可以供进程中运行的所有线程使用。实际上,DLL几乎将失去它作为DLL的全部特征。对于进程中的线程来说,DLL的代码和数据看上去就像恰巧是在进程的地址空间中的额外代码和数据一样。当一个线程调用DLL函数时,该DLL函数要查看线程的堆栈,以便检索它传递的参数,并将线程的堆栈用于它需要的任何局部变量。此外, <b> DLL中函数的代码创建的任何对象均由调用线程所拥有,而DLL本身从来不拥有任何东西 </b> 。
如你所知,可执行文件的全局变量和静态变量不能被同一个可执行文件的多个运行实例共享。Windows98能够确保这一点,方法是在可执行文件被映射到进程的地址空间时为可执行文件的全局变量和静态变量分配相应的存储器。Windows2000确保这一点的方法是使用第13章介绍的写入时拷贝(copy-on-write)机制。DLL中的全局变量和静态变量的处理方法是完全相同的。当一个进程将DLL的映像文件映射到它的地址空间中去时,系统将同时创建全局数据变量和静态数据变量的实例。
注意 必须注意的是,单个地址空间是由一个可执行模块和若干个DLL模块组成的。这些模块中,有些可以链接到静态版本的C/C++运行期库,有些可以链接到一个DLL版本的C/C++运行期库,而有些模块(如果不是用C/C++编写
的话)则根本不需要C/C++运行期库。许多开发人员经常会犯一个常见的错误,因为他们忘记了若干个C/C++运行期库可以存在于单个地址空间中。请看下面的代码:
VOID EXEFunc(){ PVOID pv = DLLFunc(); free(pv);}
PVOID DLLFunc(){return(malloc(100)); }
那么你是怎么看待这个问题的呢?上面这个代码能够正确运行吗?DLL函数分配的内存块是由EXE的函数释放的吗?答案是可能的。上面显示的代码并没有为你提供足够的信息。如果EXE和DLL都链接到DLL的C/C++运行期库,那么上面的代码将能够很好地运行。 <b> 但是,如果两个模块中的一个或者两个都链接到静态C/C++运行期库,那么对free函数的调用就会失败。 </b> 我经常看到编程人员编写这样的代码,结果都失败了。
有一个很方便的方法可以解决这个问题。当一个模块提供一个用于分配内存块的函数时,该模块也必须提供释放内存的函数。让我们将上面的代码改写成下面的样子:
VOID EXEFunc(){ PVOID pv = DLLFunc(); DLLFreeFunc(pv);}
PVOID DLLFunc(){ PVOID pv = malloc(100); return(pv);}
BOOL DLLFreeFunc(PVOID pv){ return(free(pv));}
这个代码是正确的,它始终都能正确地运行。当你编写一个模块时,不要忘记其他模块中的函数也许没有使用C/C++来编写,因此可能无法使用malloc和free函数进行内存的分配。应该注意不要在代码中使用这些假设条件。
另外,在内部调用malloc和free函数时,这个原则对于C++的new和delete操作符也是适用的。
</quote>
我的问题:
1.“如果两个模块中的一个或者两个都链接到静态C/C++运行期库,那么对free函数的调用就会失败”,为什么? 静态CRT library和动态CRT library有什么区别?
2. “DLL中函数的代码创建的任何对象均由调用线程所拥有,而DLL本身从来不拥有任何东西”, DLL中的全局变量怎么解释?由多个应用程序使用该DLL时,DLL中的全局变量需要加上同步机制吗?
[解决办法]
1.因为 malloc/free, new/delete 都是调用 HeapAlloc/HeapFree 来实现来实现内存分配是释放的。查看 Windows 的 API 可以看到,这两个函数都需要一个 Heap 的 HANDLE 做为参数。CRT 库采用了全局变量来保存这个 HANDLE。如果是静态链接, CRT 库的代码会链接到各个 dll 中去,也包括这个全局变量。也就是说,每个静态链接的 dll 都有一个自己的全局堆句柄,他们自己都在这个句柄上使用内存。当一个 dll 释放另外一个 dll 分配的内存时由于使用的堆句柄不一致于是出错。当使用动态链接时,有于每个 dll 都是去调用 CRT 库的 dll 函数来分配和释放内存的,使用的是同一个句柄,所以就没有这个问题。
[解决办法]
1.按照您的讲解,如果一个应用程序中静态链接了多个CRT库的话,那么这一个应用程序就含有多个指向堆空间的不同句柄,也就是说,这个应用程序有多个堆空间,是这样吗?
2.再疑问一下,CRT库中的堆句柄由谁决定的?链接多个CRT库时,为什么会不同?
-------------------------
1.这个不光取决于应用程序的链接方式,应用程序的 EXE 只是一个模块,整个进程中加载的模块中有多少个静态链接了 CRT 就会创建多少个堆,如果其中某一个模块使用的动态链接 CRT 的话,就会加载它需要的那个 crt 库的 dll, 而这些 CRT 库的版本不一定是相同的。又或者某些 dll 不是由 C/C++ 创建的,他们有自己的内存分配方式,里面或许又创建了自己的堆了。所以一个程序有多少个堆真的很难说清楚。
2. CRT 库的堆句柄是在它初始化的时候用 HeapCreate API 创建的。多个 CRT 库时,它们各创建各的,自然就多个了。