VirtualQuery返回的自由区域上的VirtualAlloc无法生成

时间:2015-07-30 17:33:16

标签: c++ winapi memory-management virtualalloc virtualquery

我试图在一个在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失败时我必须循环几个步骤,直到它最终成功。

2 个答案:

答案 0 :(得分:3)

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

来自VirtualAlloc的MSDN文档:

  

lpAddress [in,optional]

     

要分配的区域的起始地址。如果记忆正在   保留,指定的地址向下舍入到最接近的倍数   分配粒度。 [...]确定页面的大小   和主机上的分配粒度,使用   GetSystemInfo功能。

答案 1 :(得分:-1)

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

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

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;
}