(静态链接)DLL使用与主程序不同的堆吗?

Emi*_*ano 21 linux windows heap dll heap-corruption

我是Windows编程的新手,我只是"迷失"两个小时寻找一个每个人都知道的错误:你不能在DLL中创建一个对象并在另一个DLL(或主程序)中销毁它.

我几乎可以肯定,在Linux/Unix上,情况并非如此(如果是的话,请说出来,但我很确定我做了数千次而没有问题......).

在这一点上,我有几个问题:

1)静态链接的DLL使用与主程序不同的堆吗?

2)静态链接的DLL是否映射在主程序的同一进程空间中?(我很确定这里的答案是一个很大的问题,否则将主程序中的函数指针传递给DLL中的函数是没有意义的.)

我说的是普通/常规DLL,而不是COM/ATL服务

编辑:通过"静态链接"我的意思是我不使用LoadLibrary加载DLL但我链接到存根库

Jay*_*Jay 20

DLL/exes需要链接到C运行时库的实现.

对于C Windows运行时库,如果要链接到以下内容,则可以选择指定:

  1. 单线程C运行时库(现在已停止支持单线程库)
  2. 多线程DLL /多线程调试DLL
  3. 静态运行时库.
  4. 少数(你可以查看链接)

它们中的每一个都将引用不同的堆,因此不允许将从一个运行时库的堆中获取的传递地址传递给其他堆.

现在,它取决于您正在谈论的DLL链接到哪个C运行时库.假设让你说,你正在使用的DLL已链接到静态C运行时库,你的应用程序代码(包含主函数)已经链接到多线程C运行时DLL,然后如果你将指针传递给分配给它的内存DLL到您的主程序并尝试在那里释放它,反之亦然,它可能导致未定义的行为.因此,基本的根本原因是C运行时库.请仔细选择.

请在此处此处找到有关C运行时库的更多信息

来自MSDN的引用:

注意 不要混合运行时库的静态和动态版本.在进程中拥有多个运行时库副本可能会导致问题,因为一个副本中的静态数据不会与另一个副本共享.链接器阻止您在一个.exe文件中链接静态和动态版本,但您仍然可以使用两个(或更多)运行时库副本.例如,与运行时库的静态(非DLL)版本链接的动态链接库与与运行时库的动态(DLL)版本链接的.exe文件一起使用时可能会导致问题.(您还应该避免在一个进程中混合库的调试版和非调试版.)

  • @Jay - 您可以传递指针,只是不要在一个模块中分配并在另一个模块中释放.你可以随时解决这个问题,例如COM(通过工厂完成分配,通过对"Release"的虚拟调用完成释放)或引入特定于库的分配函数(例如`libpng`中的`png_free`). (4认同)

mor*_*lli 6

如果我有一个编译为 .exe 的应用程序并且想要使用一个库,我可以从 .lib 文件静态链接该库,也可以从 .dll 文件动态链接该库。

每个链接的模块(即每个 .exe 或 .dll)都将链接到 C 或 C++ 运行时的实现。运行时本身是一个库,可以静态或动态链接到不同的线程配置。

通过说静态链接 dll,您是否描述了应用程序 .exe 动态链接到库 .dll 并且该库内部静态链接到运行时的设置?我假设这就是你的意思。

另外值得注意的是,每个模块(.exe 或 .dll)都有自己的静态范围,即 .exe 中的全局静态与 .dll 中同名的全局静态不是同一实例。

因此,在一般情况下,不能假设在不同模块内运行的代码行使用相同的运行时实现,而且它们不会使用任何静态的相同实例。

因此,在处理跨模块边界的对象或指针时需要遵守某些规则。对于任何给定地址,分配和释放必须发生在同一模块中。否则堆将不匹配并且行为将无法定义。

COM 使用引用计数解决了这个问题,当引用计数达到零时,对象将自行删除。这是用于解决匹配位置问题的常见模式。

可能存在其他问题,例如Windows定义了某些操作,例如如何在每个线程的基础上而不是在每个模块的基础上处理分配失败。这意味着在模块 B 设置的线程上在模块 A 中运行的代码也可能会遇到意外行为。


Abh*_*ain 5

首先让我们了解应用程序/ DLL在Windows操作系统上的堆分配和堆栈。传统上,操作系统和运行时库随堆的实现一起提供。

  1. 在进程开始时,操作系统会创建一个默认堆,称为“进程堆”。如果不使用其他堆,则使用Process堆分配块。
  2. 语言运行时间还可以在一个进程中创建单独的堆。(例如,C运行时会创建自己的堆。)
  3. 除了这些专用堆之外,应用程序或许多已加载的动态链接库(DLL)之一可能会创建和使用单独的堆,称为专用堆
  4. 这些堆位于所有虚拟内存系统中操作系统的虚拟内存管理器的顶部。
  5. 让我们讨论更多有关CRT和关联堆的信息:
    1. C / C ++运行时(CRT)分配器:提供malloc()和free()以及new和delete运算符。
    2. 作为其初始化的一部分,CRT为其所有分配创建这样的额外堆(此CRT堆的句柄在内部称为“ _crtheap”的全局变量中存储在CRT库的内部)。
    3. CRT创建自己的私有堆,该堆位于Windows堆的顶部。
    4. Windows堆是围绕Windows运行时分配器(NTDLL)的薄层。
    5. Windows运行时分配器与虚拟内存分配器进行交互,后者分配并提交操作系统使用的页面。

您的DLL和exe链接到多线程静态CRT库。您创建的每个DLL和exe都有自己的堆,即_crtheap。分配和取消分配必须从各自的堆中进行。从DLL动态分配的,不能从可执行文件中取消分配,反之亦然。

你可以做什么?使用/ MD或/ MDd在DLL和exe中编译我们的代码,以使用运行库的特定于多线程和特定于DLL的版本。因此,DLL和exe都链接到相同的C运行时库,因此链接到一个_crtheap。分配始终与单个模块中的取消分配配对。