【问题标题】:VirtualAlloc failingVirtualAlloc 失败
【发布时间】:2015-07-04 14:44:54
【问题描述】:

我正在尝试使用 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);

第一次分配返回 0x00000000000d0000。对 VirtualQuery 的调用会在“信息”中产生以下数据:

    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

我解释这意味着有 0xff 可用页面从 0xd1000 开始处于 MEM_FREE 状态。那么为什么我在 0xd1000 提交页面的尝试失败了呢?

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

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

【问题讨论】:

    标签: c++ windows memory virtualalloc


    【解决方案1】:

    如果你想为分配指定连续的页面,你想将分配地址空间与分配内存分开来支持它。牢记这一点,我们可以实现如下代码:

    #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
    

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

    【讨论】:

      【解决方案2】:

      来自documentation for VirtualAlloc

      如果内存被保留,则指定地址向下舍入到分配粒度的最接近的倍数。

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

      【讨论】:

        猜你喜欢
        • 2020-08-19
        • 2010-11-06
        • 1970-01-01
        • 2017-11-13
        • 1970-01-01
        • 2018-11-07
        • 1970-01-01
        • 2020-12-23
        相关资源
        最近更新 更多