不能在 VirtualQuery 返回的空闲区域上使用 VirtualAlloc
Can't VirtualAlloc on free region returned by 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()
returns ERROR_INVALID_ADDRESS
(487).
我解决它的方法是,如果它足够大,使用页面大小步长扫描 mbi.RegionSize
以找到一个地址,让我可以分配我需要的内存。
为什么根据 VirtualQuery
整个区域应该是空闲的,我应该能够在我想要的任何地址内分配,但通常当第一个 VirtualAlloc
失败时我必须循环几个步骤,直到最后成功。
当您向 VirtualAlloc 提供地址并使用 MEM_RESERVE 标志时,地址会向下舍入到分配粒度 (64K) 的最接近倍数。您可能会发现空闲页面区域位于分配的 64K 块中,这些块并未完全保留。在释放已分配块中的所有页面之前,无法分配(或保留)这些块中的未保留页面。
来自 VirtualAlloc 的 MSDN 文档:
lpAddress [in, optional]
The starting address of the region to allocate. If the memory is being
reserved, the specified address is rounded down to the nearest multiple
of the allocation granularity. [...] To determine the size of a page
and the allocation granularity on the host computer, use the
GetSystemInfo function.
我找到了适合我的解决方案。在我之前的例子中,我试图同时分配和保留;并且我使用的地址与分配粒度不一致。所以我不得不四舍五入到适合该区域的最接近的分配粒度倍数。
类似这样的方法有效(注意,我还没有测试过这段代码)。
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;
}
我正在尝试在 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()
returns ERROR_INVALID_ADDRESS
(487).
我解决它的方法是,如果它足够大,使用页面大小步长扫描 mbi.RegionSize
以找到一个地址,让我可以分配我需要的内存。
为什么根据 VirtualQuery
整个区域应该是空闲的,我应该能够在我想要的任何地址内分配,但通常当第一个 VirtualAlloc
失败时我必须循环几个步骤,直到最后成功。
当您向 VirtualAlloc 提供地址并使用 MEM_RESERVE 标志时,地址会向下舍入到分配粒度 (64K) 的最接近倍数。您可能会发现空闲页面区域位于分配的 64K 块中,这些块并未完全保留。在释放已分配块中的所有页面之前,无法分配(或保留)这些块中的未保留页面。
来自 VirtualAlloc 的 MSDN 文档:
lpAddress [in, optional]
The starting address of the region to allocate. If the memory is being reserved, the specified address is rounded down to the nearest multiple of the allocation granularity. [...] To determine the size of a page and the allocation granularity on the host computer, use the GetSystemInfo function.
我找到了适合我的解决方案。在我之前的例子中,我试图同时分配和保留;并且我使用的地址与分配粒度不一致。所以我不得不四舍五入到适合该区域的最接近的分配粒度倍数。
类似这样的方法有效(注意,我还没有测试过这段代码)。
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;
}