【问题标题】:Memory Allocation in Static vs Dynamic Linking of C RuntimeC运行时静态与动态链接中的内存分配
【发布时间】:2011-12-30 18:27:32
【问题描述】:

我的程序架构涉及插件(dll)和exe(显然)。我们是唯一的插件提供商。有人告诉我,如果我们静态链接 c 运行时,在插件中分配内存然后在 exe 代码中释放它是一个潜在问题。但是,如果我们动态链接它,那么只有一个堆,而 c 运行时可以访问所有堆。我们根据这个建议切换到动态链接,但是我们从切换中看到的只是与新运行时的分发和安装相关的头痛和麻烦。 (不知道我们在内存分配问题方面避免了什么。)

内存分配的含义是什么?假设一个插件分配内存,而 exe 释放它。静态与动态链接的 c 运行时之间是否存在不同的行为?如果我们使用插件,我们在静态链接 c 运行时会遇到麻烦吗?如果我们切换回静态链接,会不会弄乱内存泄漏检测和故障转储报告?

符合 cmets 在Which VC++ runtime version do I choose - static or dynamic? 特别是Which VC++ runtime version do I choose - static or dynamic? 上提出的一些问题

http://msdn.microsoft.com/en-us/library/abx4dbyh(v=VS.100).aspx 也有一些讨论。

【问题讨论】:

    标签: c visual-c++ dynamic-linking static-linking msvcrt


    【解决方案1】:

    分配/释放内存的函数是成对的;您不能调用 my_malloc() 然后使用 free(),或者调用 C++ 的“new”运算符,然后从某个随机库中调用 FreeMem() :)

    您的插件需要一种众所周知的分配和释放内存的方法。如果它执行 malloc(),那么您的主程序可能只执行 free()。但如果它做了一些更奇特的事情(Windows 有大量可用的内存分配器),你的插件 API 需要提供一种方法让主 exe 调用插件以释放其数据。

    所以如果你的主程序调用

    foo = plugin->allocate_something()
    

    那么你的插件 API 最好有相应的

    plugin->free_something (foo)
    

    主程序可以明确使用。

    【讨论】:

      【解决方案2】:

      如果您想在一个模块中从堆中分配,而在另一个模块中释放,那么您只需动态链接运行时。更重要的是,各方必须动态链接相同运行时。一旦你这样做了,那么就不会有问题了。

      现在,这对你的插件来说是一个非常严格的限制,你应该在做之前三思而后行。强迫所有插件作者与你同步升级是一个沉重的负担。我建议您考虑重构您的接口,以便分配始终与单个模块中的解除分配配对。这解除了我上面描述的限制,让您的插件作者的生活更轻松。

      至于为什么您仍然会遇到运行时错误,这很难说。我的猜测是,并非所有模块都链接到相同版本的运行时。


      在 cmets 中,您声明您控制所有插件。这意味着我上面讨论的约束不是强加的,因为您很容易为所有模块使用相同的编译器版本。但是,跨模块堆访问的动态链接规则仍然存在。您必须针对同一版本的运行时使用动态链接。

      【讨论】:

      • 我应该澄清的细节:我没有遇到运行时错误。我们是所有插件的作者。发布要求是同时重新编译所有插件。
      • 这让您的生活更轻松。但是,您仍然需要所有各方使用相同的堆。这意味着针对单个运行时版本进行动态链接。
      • 您还可以为插件提供“分配”和“释放”调用(由可执行文件提供),它们可用于访问可执行文件的分配器。我倾向于在模块中使用“ModuleInit”函数,为它提供一个指向回调函数的指针表。
      • @david 是的,这是一个选择。我个人更喜欢非常基础,并强制所有分配与同一模块中的免费配对。我还将动态链接到运行时以保持较小的图像大小。
      • 我本来希望msdn.microsoft.com/en-us/library/abx4dbyh(v=VS.100).aspx 提到内存分配问题 - 因为这似乎是跨 dll/exe 边界最常见的访问,而不是我会从一个开始 strtok 并在另一个结束.我还没有找到任何关于这个限制的“官方”文档,这让我感到惊讶。不是所有的堆都在同一个地址空间中吗?
      【解决方案3】:

      显然,问题在于插件和主应用程序使用不同且不兼容的堆管理器,这会破坏彼此的数据结构。理论上,使用动态 CRT 会强制两者使用相同的 CRT,因此两者会自动兼容。

      在一般情况下,最好让插件负责分配和删除它使用的所有内存。这意味着可能会向插件添加 API 以删除它先前创建并传递回主应用程序的对象。这也提供了插件和应用程序之间的良好隔离,允许插件出于性能或其他原因使用专门的分配器。例如,这就是 COM 所做的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-07-23
        • 2013-08-31
        • 2016-02-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-28
        • 2020-12-05
        • 2021-04-01
        相关资源
        最近更新 更多