与静态c运行时链接时内存分配/释放时的问题

时间:2012-08-31 12:46:41

标签: c++ c visual-c++

我在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 ++ 运行时库,对免费的调用失败。

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

有人可以解释免费失败的原因吗? 在这里找到类似的问题 Memory Allocation in Static vs Dynamic Linking of C Runtime

但我对MrPhilTx有同样的怀疑: 不是所有的堆都在同一个地址空间吗?

谢谢!

4 个答案:

答案 0 :(得分:4)

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

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

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

答案 1 :(得分:2)

您无法从一个分配器分配内存并将其与另一个分配器释放。不同的分配器使用不同的内部实现,并且向没有分配它的分配器提供内存块的结果是不可预测的。

因此,除非您知道两段代码使用相同的分配器这一事实,否则您无法在一段代码中分配内存并在另一段代码中释放它。通常的解决方案是确保同一单元分配和释放内存。在您的示例中,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);
}

答案 2 :(得分:1)

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

malloc并通过将其转换为堆来自由使用brk和sbrk返回的数据段。堆是当前porcess空间中的一大块内存,可以根据需要请求和返回小块内存。值得注意的是,许多对malloc和free的调用都不会进行系统调用。

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

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

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

无论如何,dll提供自己的免费功能是一种好习惯。如果没有其他文件需要释放DLLFunc返回的指针。

我想在Windows上也是如此。

答案 3 :(得分:0)

代码严格依赖于mallocfree的实施。一个好的实现没有问题,一个糟糕的实际上将失败。创建mallocfree的工作DLL实现肯定更容易,但在静态库中实现这一点绝非易事。

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