码迷,mamicode.com
首页 > 其他好文 > 详细

[转载]DLL和exe里的malloc和free不能混用的问题

时间:2015-02-02 19:26:04      阅读:122      评论:0      收藏:0      [点我收藏+]

标签:

源自:

http://bjwf.cndev.org/2004/06/03/559/

http://bbs.csdn.net/topics/40214261

======================================

今天老玉米提了一个问题问为什么dll里malloc的内存如果在exe里free的话会出错,我分析了一下C库的原代码,得出结论如下:

 

刚看了一下malloc和free 的源代码,在这两个函数中都有对全局变量的引用,而malloc和free是C库函数,分别被静态链接到exe和dll里,这样他们引用的全局变量也会有两份各是各的,自然不能混用。

GlobalAlloc之类的windows API函数应该行。



茶壶贴了一贴,说dll因为有自已的堆栈所以不能混用,我表示怀疑,继续看源代码,找到支持上面结论的真正原因:

 

DLL是否有自已的堆栈我不知道, 但dll和exe里的malloc和free不能混用的真正原因就是我上面说的,有源代码为证,

malloc 里真正分配的是这一句,见malloc.c里
    return HeapAlloc( _crtheap, 0, size );

_crtheap是个全局变量,在_heap_init这个函数里初始化,见heapinit.c:
    //  Initialize the “big-block” heap first.
  if ( (_crtheap = HeapCreate( mtflag ? 0 : HEAP_NO_SERIALIZE, BYTES_PER_PAGE, 0 )) == NULL )
            return 0;

而_heap_init 在wWinMainCRTStartup 这个函数里调用(见crt0.c),同时也会在_DllMainCRTStartup里调用(见dllcrt0.c )。而winmain和dllmain 分别在wWinMainCRTStartup  和DllMainCRTStartup里调用,但都在调用了_heap_init之后(事实上是DllMainCRTStartup调用了_CRT_INIT之后才会调用用户的dllmain,而_CRT_INIT里调用了_heap_init这也就是说至少_crtheap这个变量在dll和exe里会各有一份,这才是dll和exe里不能混用malloc和free的真正原因。

 

如果使用LoadLibraryEx装入dll,并指定DONT_RESOLVE_DLL_REFERENCES参数来让系统不调用用户的dllmain,dll和exe的malloc和free仍然不能混用,因为即使指定了DONT_RESOLVE_DLL_REFERENCES参数DllMainCRTStartup里仍会调用_CRT_INIT来初始化自已的_crtheap。

 

当然如果dll和exe的编写语言不同的话,更不能混用了,各种语言都会封装winAPI来实现自已的内存管理库函数。



DLL中的内存分两类,一类是共享内存,一类是进程私有内存

例如,进程P1和P2同时引用同一个DLL,一般情况下,DLL中声明的变量会在P1和P2的进程空间内产生各自的副本

也就是说,DLL中的变量,对于各个进程是独立的

某些情况下,有必要在DLL中产生一块共享内存,多个进程都访问到同一块数据,比如,某些设备驱动程序,需要内部保留一个设备忙/空闲之类的状态标志,这个标志就必须作为共享内存存在,保证只有一份

对于DLL中分配的内存,我觉得本身没有什么神秘之处,Windows程序之下的内存分配,最终都需要通过OS来完成,老玉米的问题,很可能是象北京色狼说的那样,不是内存本身的问题,是内存管理程序的故障

因为各种语言的RTL库中的内存管理程序,它本身也需要通过自己的内部数据来管理内存分配的状态

在DLL中调用的内存管理程序和在EXE中调用的内存管理程序不是同一块,而且它们内部维护内存分配信息的数据也不是同一块,所以造成老玉米的问题

如果上面的说法成立的话,那么,不要用C的RTL来申请和释放内存,直接通过Windows API来申请和释放内存,比如GlobalAlloc/GlobalFree/LocalAlloc/LocalFree/HeapAlloc/HeapFree等等,应该就不会有问题了

====================================================

一个模块一个堆,一个线程一个栈。

问题的一切都要归咎于C运行时期库是静态连接的。
我回去看了一下C运行时期库的源码,发现一个很搞笑的事实:CRT(C运行时期库)不是使用进程缺省的堆来实现malloc(new中调用malloc)的,而是使用一个全局句柄HANDLE __crtheap(好象是_crtheap,记不清了)来分配内存的。这个_crtheap是在XXXCRTStartUp(CRT提供的进口点函数)中创建的。
由于CRT静态连接,则楼主的DLL里有也有一个CRT,因此也有一个_crtheap。而在dll中的new使用dll中的_crtheap句柄分配堆,在exe中的delete使用exe中的_crtheap释放堆,当然失败!不信的话,楼主大可在你的exe和dll中各下一个断点,在变量查看中输入_crtheap来查看其值,一般会差个0x10000000-0x04000000,因为缺省dll的基址是0x10000000,而exe的是0x04000000。

因此你的模块也有一个与之相关联的堆了,因为使用C/C++语言编写,而且CRT静态连接

[转载]DLL和exe里的malloc和free不能混用的问题

标签:

原文地址:http://www.cnblogs.com/Acg-Check/p/4268257.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!