VirtualAlloc 失败

VirtualAlloc failing

我正在尝试使用 VirtualAlloc 来保留和提交一块内存,然后再次扩展该块。不幸的是,尽管 VirtualQuery 说请求的地址范围是免费的,但它返回 NULL 并出现错误 ERROR_INVALID_ADDRESS。这是我的代码:

void* allocation = VirtualAlloc(NULL, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
void* desiredNextAllocation = (char*)allocation + 4096;
MEMORY_BASIC_INFORMATION info;
size_t memory_info = VirtualQuery(desiredNextAllocation, &info, sizeof(info));
void* extended = VirtualAlloc(desiredNextAllocation, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

第一次分配returns0x00000000000d0000。对 VirtualQuery 的调用导致 'info' 中的以下数据:

    BaseAddress 0x00000000000d1000  void *
    AllocationBase  0x0000000000000000  void *
    AllocationProtect   0x00000000  unsigned long
    RegionSize  0x00000000000ff000  unsigned __int64
    State   0x00010000  unsigned long
    Protect 0x00000001  unsigned long
    Type    0x00000000  unsigned long

我将其解释为从 0xd1000 开始有 0xff 个可用页面处于 MEM_FREE 状态。那么,为什么我尝试提交 0xd1000 页面会失败?

我是 运行 Windows 7,这是一个 64 位版本。

我已经阅读了几篇关于 VirtualAlloc 的 Whosebug 帖子,但它们似乎都暗示这段代码应该像我对文档的理解一样工作。

如果要为分配指定连续的页面,则需要将分配地址 space 与分配内存分开以支持它。牢记这一点,我们可以实现这样的代码:

#include <windows.h>
#include <iostream>
#include <iomanip>

std::ostream &operator<<(std::ostream &os, MEMORY_BASIC_INFORMATION const &mi) {
    return os   << std::setw(20) << "Allocation Base: " << mi.AllocationBase << "\n"
                << std::setw(20) << "BaseAddress: " << mi.BaseAddress << "\n"
                << std::setw(20) << "Protection: " << mi.Protect << "\n"
                << std::setw(20) << "Region size: " << mi.RegionSize;
}

void show_page(void *page) {
    MEMORY_BASIC_INFORMATION info;

    VirtualQuery(page, &info, sizeof(info));
    std::cout << info << "\n\n";
}

static const int page_size = 4096;

void *alloc_page(char *address) {

    void *ret = VirtualAlloc(address, page_size, MEM_COMMIT, PAGE_READWRITE);
    show_page(ret);
    return ret;
}

int main() {
    static const int region_size = 65536;

    char * alloc = static_cast<char *>(VirtualAlloc(NULL, region_size, MEM_RESERVE, PAGE_READWRITE));

    for (int i = 0; i < 4; i++)
        alloc_page(alloc + page_size * i);
}

示例结果:

Allocation Base: 00000000000C0000
    BaseAddress: 00000000000C0000
     Protection: 4
    Region size: 4096

Allocation Base: 00000000000C0000
    BaseAddress: 00000000000C1000
     Protection: 4
    Region size: 4096

Allocation Base: 00000000000C0000
    BaseAddress: 00000000000C2000
     Protection: 4
    Region size: 4096

Allocation Base: 00000000000C0000
    BaseAddress: 00000000000C3000
     Protection: 4
    Region size: 4096

如您所见,现在所有分配都成功了。顺便说一句:当您保留地址 space 时,您可以分配的最小大小是 64K(如上所示)。您实际上应该通过调用 GetSystemInfo 并在它给您的 SYSTEM_INFO 结构中使用 dwPageSizedwAllocationGranularity 来获得页面大小和最小区域大小。

来自documentation for VirtualAlloc

If the memory is being reserved, the specified address is rounded down to the nearest multiple of the allocation granularity.

在这种情况下,地址 0xd1000 向下舍入为地址 0xd0000,该地址已被保留,因此无效。