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