2

我在 Jeffrey Richter 和 Christophe Nasarre 所著的 Windows via C-C++ 一书中看到了下面的注释。

检查以下代码:V

OID EXEFunc() {
PVOID pv = DLLFunc();
// Access the storage pointed to by pv...
// Assumes that pv is in EXE's C/C++ run-time heap
free(pv);
}
PVOID DLLFunc() {
// Allocate block from DLL's C/C++ run-time heap
return(malloc(100));
}

所以你怎么看?前面的代码能正常工作吗?DLL 函数分配的块是否被 EXE 函数释放?答案是:也许。显示的代码没有为您提供足够的信息。如果 EXE 和 DLL 都链接到 DLL C/C++ 运行时库,则代码可以正常工作。但是,如果其中一个或两个模块链接到静态 C/C++ 运行时库,则对 free 的调用将失败。

我无法理解为什么在将模块与静态 C 运行时链接时调用 free 会失败。

有人可以解释为什么免费失败吗?在这里找到了类似的问题: C运行时的静态与动态链接中的内存分配

但是我在这里和 MrPhilTx 有同样的疑问:不是所有的堆都在同一个地址空间中吗?

谢谢!

4

4 回答 4

6

当您的 DLL 和 EXE 都静态链接到 C 运行时,这两个运行时根本不知道彼此。因此,EXE 和 DLL 都有自己的运行时副本、自己的堆和堆元数据。双方都不知道其他元数据,并且在释放内存时没有安全的方法来更新数据。你最终会得到不稳定的元数据,事情最终会失败(如果你很幸运,它会立即失败)。

这意味着您的进程中至少有两个堆,每个堆都有自己的规则和元数据。EXE 无法知道 DLL 分配内存的确切方式,因此无法释放它。

至于为什么当一切都是动态链接时你可以共享一个堆,这很容易,进程中只有一个 C 运行时 DLL 的副本,所以如果每个 DLL 链接到它,它们都将调用相同的代码相同的元数据。

于 2012-08-31T16:55:57.370 回答
3

您不能从一个分配器分配内存并用另一个分配器释放它。不同的分配器使用不同的内部实现,并且将内存块分配给未分配它的分配器的结果是不可预测的。

因此,除非您知道两段代码使用相同的分配器这一事实,否则您不能在一段代码中分配内存并在另一段中释放它。通常的解决方案是确保同一个单元分配和释放内存。在您的示例中,DLL 可以提供一个“免费”函数,主代码可以调用该函数,而不是调用它自己的free函数来释放它自己的分配器。

所以改为这样做:

OID EXEFunc() {
    PVOID pv = DLLFunc();
    // Access the storage pointed to by pv...
    // Assumes that pv is in EXE's C/C++ run-time heap
    DLLFreeFunc(pv);
}

...

PVOID DLLFunc() {
    // Allocate block from DLL's C/C++ run-time heap
    return(malloc(100));
}

DLLFreeFunc(PVOID x) {
    free(x);
}
于 2012-08-31T12:51:18.773 回答
1

在 Linux 上,程序使用 brk 和 sbrk 系统调用从内核请求额外的数据页。sbrk 返回指向程序可以使用的数据段的地址。

malloc 和 free 通过将 brk 和 sbrk 返回的数据段变成堆来使用它。堆是当前进程空间中的一大块内存,可以根据需要请求和返回小块内存。重要的是要注意许多对 malloc 和 free 的调用不会进行系统调用。

现在,当 malloc 和 free 想要使用堆时,他们需要获取指向堆的指针。此指针存储在称为静态数据的单独数据段中,并在应用程序加载时分配。为了确保不同的 DLL(或 linux 上的共享库)不会相互冲突,每个 DLL 都有自己的静态数据部分。

现在让我们假设 dll 和可执行文件都静态链接到它们自己的库。在这种情况下,dll 和可执行文件将具有指向不同堆的指针,并且在这种情况下,dll 和可执行文件都必须释放自己的内存。

然而,在 linux 上,dll 和可执行文件都将通过一个通用 DLL(linux 上的 libc.so)访问 malloc 和 free。在这种情况下,由于 dll 和可执行文件都有效地访问了 libc 的堆,因此可执行文件可以安全地释放 dll 分配的内存。

无论如何,最好让 dll 提供自己的免费功能。如果没有其他文件表明需要释放 DLLFunc 返回的指针,则此为。

我想这在 Windows 上也是如此。

于 2012-08-31T17:23:28.723 回答
0

代码严重依赖于 和 的malloc实现free。一个好的实施没有问题,一个糟糕的实施确实会失败。malloc创建and的工作 DLL 实现肯定更容易free,但在静态库中这样做远非不可能。

一个简单的例子是一个静态库,它将调用直接转发到GlobalAllocand GlobalFree

于 2012-08-31T12:52:02.443 回答