0

我试图在 Windows 应用程序中加载的 DLL 中的某个内存范围内分配一定数量的内存。

我这样做的方式是使用VirtualQuery()搜索标记为空闲并且在我需要进行分配的边界内的内存区域。我所看到的是,即使该区域被标记为MEM_FREE VirtualAlloc()有时无法分配内存。

代码非常接近以下内容:

LPVOID address = NULL, mem = NULL;

for (address = LOWER_RANGE; address < UPPER_RANGE;) {
    MEMORY_BASIC_INFORMATION mbi = {0};

    if (VirtualQuery(address, &mbi, sizeof(mbi))) {
        if (mbi.State == MEM_FREE && mbi.RegionSize >= ALLOC_SIZE) {
            mem = VirtualAlloc(address, ALLOC_SIZE, 
                MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READ);
            if (mem) {
                break;
            }
        }
    }

    address = mbi.BaseAddress + mbi.RegionSize;
}

VirtualAlloc()失败时GetLastError()返回ERROR_INVALID_ADDRESS (487).

我解决它的方法是,如果它足够大,则mbi.RegionSize使用页面大小步骤扫描以找到一个地址,该地址将允许我分配我需要的内存。

为什么根据VirtualQuery整个区域应该是免费的,我应该能够在我想要的任何地址内分配,但通常当第一次VirtualAlloc失败时,我必须循环几个步骤,直到它最终成功。

4

2 回答 2

5

当您向 VirtualAlloc 提供地址并使用 MEM_RESERVE 标志时,地址会向下舍入到分配粒度 (64K) 的最接近的倍数。您可能会发现空闲页面区域位于未完全保留的已分配 64K 块中。在分配块中的所有页面都被释放之前,这些块中的未保留页面无法分配(或保留)。

来自VirtualAlloc的 MSDN 文档:

lpAddress [输入,可选]

要分配的区域的起始地址。如果内存被保留,则指定的地址向下舍入到分配粒度的最接近的倍数。[...] 要确定页面大小和主机上的分配粒度,请使用 GetSystemInfo函数。

于 2015-07-30T18:17:05.603 回答
0

我找到了一个适合我的解决方案。在我之前的示例中,我试图同时分配和保留;并且我使用的地址与分配粒度不一致。所以我不得不四舍五入到最接近区域内的分配粒度倍数。

像这样的东西有效(注意,我没有测试过这段代码)。

PVOID address = NULL, mem = NULL;

for (address = LOWER_RANGE; address < UPPER_RANGE;) {
    MEMORY_BASIC_INFORMATION mbi = {0};
    if (VirtualQuery(address, &mbi, sizeof(mbi))) {
        PVOID reserveAddr = mbi.BaseAddress + (mbi.BaseAddress % alloc_gran);
        PVOID end_addr = mbi.BaseAddress + mbi.RegionSize;

        if (mbi.State == MEM_FREE && 
                mbi.RegionSize >= ALLOC_SIZE &&
                reserveAddr + ALLOC_SIZE <= end_addr) {
            mem = VirtualAlloc(reserveAddr, ALLOC_SIZE, 
                MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READ);
            if (mem) {
                break;
            }
        }
    }

    address = mbi.BaseAddress + mbi.RegionSize;
}
于 2015-07-31T17:00:08.077 回答