首页 > 解决方案 > 合并 Win32 的虚拟分配占位符

问题描述

我试图围绕 Win32 的虚拟分配占位符概念以及如何合并它们。

不幸的是,我在网上找不到任何示例,尽管尝试了各种变体,但我无法让我的代码工作。

有人会这么好心指出我缺少什么吗?

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

// Helpers
// -------------------------------------------------------------------------

static void
get_page_size(size_t *page_size)
{
    SYSTEM_INFO system_info;

    GetSystemInfo(&system_info);
    *page_size = system_info.dwPageSize;
}

static void
touch_pages(void *buf, size_t size, size_t page_size)
{
    size_t i;

    for (i = 0; i < size; i += page_size) {
        void *ptr;

        ptr = (void *)((uintptr_t)buf + i);
        *((size_t *)ptr) = i;
    }
}

static void
check_pages(void *buf, size_t size, size_t page_size)
{
    size_t i;
    size_t good;

    good = 0;
    for (i = 0; i < size; i += page_size) {
        void *ptr;

        ptr = (void *)((uintptr_t)buf + i);
        if (*((size_t *)ptr) != i) {
            printf("invalid data!\n");
            exit(0);
        }

        ++good;
    }

    printf("all good!\n");
}

static void
print_last_error(void)
{
    char buf[256];

    FormatMessageA(
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        GetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
        buf,
        (sizeof(buf) / sizeof(char)),
        NULL);

    printf("%s\n", buf);
}

// -------------------------------------------------------------------------

int
main(void)
{
    size_t page_size;
    ULARGE_INTEGER alloc_size;

    get_page_size(&page_size);
    alloc_size.QuadPart = 0x10000000;

    // PART 1
    // -------------------------------------------------------------------------

    // Allocate 256 MB at a fixed address.
    // This is done by reserving a block of virtual addresses, creating
    // paging-backed sections, and mapping them onto the allocation block.

    void *addr_1 = (void *)0x10000000; // Aligned with allocation granularity.

    void *placeholder_1 = VirtualAlloc2(
        GetCurrentProcess(),
        addr_1,
        (size_t)alloc_size.QuadPart,
        MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
        PAGE_NOACCESS,
        NULL,
        0
    );

    if (placeholder_1 == NULL) {
        printf("placeholder_1: VirtualAlloc2 failed\n");
        print_last_error();
        exit(0);
    }

    HANDLE section_1 = CreateFileMappingA(
        INVALID_HANDLE_VALUE,
        NULL,
        PAGE_READWRITE,
        alloc_size.HighPart,
        alloc_size.LowPart,
        NULL
    );

    if (section_1 == NULL) {
        printf("section_1: CreateFileMappingA failed\n");
        print_last_error();
        exit(0);
    }

    void *view_1 = MapViewOfFile3(
        section_1,
        GetCurrentProcess(),
        addr_1,
        0,
        (size_t)alloc_size.QuadPart,
        MEM_REPLACE_PLACEHOLDER,
        PAGE_READWRITE,
        NULL,
        0
    );

    if (view_1 == NULL) {
        printf("view_1: MapViewOfFile3 failed\n");
        print_last_error();
        exit(0);
    }

    // Touch each page just to make sure that they are fully committed.
    touch_pages(view_1, (size_t)alloc_size.QuadPart, page_size);

    // PART 2
    // -------------------------------------------------------------------------

    // Grow the previous allocation by another 256 MB.
    // The exercise here is to try replicating Linux's `mremap()` by using
    // `MEM_COALESCE_PLACEHOLDERS`.
    // The idea is to reserve another block of virtual addresses just after
    // the first allocation block, merging both blocks into a single one,
    // creating another bunch of paging-backed sections, and mapping
    // all the pages onto the merged allocation block.

    void *addr_2 = (void *)0x20000000; // Aligned with allocation granularity.

    void *placeholder_2 = VirtualAlloc2(
        GetCurrentProcess(),
        addr_2,
        (size_t)alloc_size.QuadPart,
        MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
        PAGE_NOACCESS,
        NULL,
        0
    );

    if (placeholder_2 == NULL) {
        printf("placeholder_2: VirtualAlloc2 failed\n");
        print_last_error();
        exit(0);
    }

    // It seems that we first need to unmap or else the following call to
    // `VirtualFree()` complains about attempting to access an invalid address.
    if (!UnmapViewOfFile2(
            GetCurrentProcess(),
            view_1,
            MEM_PRESERVE_PLACEHOLDER
        ))
    {
        printf("view_1: UnmapViewOfFile2 failed\n");
        print_last_error();
        exit(0);
    }

    if (!VirtualFree(
            placeholder_1,
            (size_t)alloc_size.QuadPart * 2,
            MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS
        ))
    {
        printf("placeholder_1: VirtualFree failed\n");
        print_last_error();
        exit(0);
    }

    // Create a new allocation block that represents both previous blocks
    // merged into a single one.
    void *placeholder_3 = VirtualAlloc2(
        GetCurrentProcess(),
        addr_1,
        (size_t)alloc_size.QuadPart * 2,
        MEM_RESERVE | MEM_REPLACE_PLACEHOLDER,
        PAGE_NOACCESS,
        NULL,
        0
    );

    if (placeholder_3 == NULL) {
        printf("placeholder_3: VirtualAlloc2 failed\n");
        print_last_error();
        exit(0);
    }

    HANDLE section_2 = CreateFileMappingA(
        INVALID_HANDLE_VALUE,
        NULL,
        PAGE_READWRITE,
        alloc_size.HighPart,
        alloc_size.LowPart,
        NULL
    );

    if (section_2 == NULL) {
        printf("section_2: CreateFileMappingA failed\n");
        print_last_error();
        exit(0);
    }

    // This fails due to an attempt to access an invalid addres.
    void *view_1b = MapViewOfFile3(
        section_1,
        GetCurrentProcess(),
        addr_1,
        0,
        (size_t)alloc_size.QuadPart,
        MEM_REPLACE_PLACEHOLDER,
        PAGE_READWRITE,
        NULL,
        0
    );

    if (view_1b == NULL) {
        printf("view_1b: MapViewOfFile3 failed\n");
        print_last_error();
        exit(0);
    }

    // This fails for the same reason if the previous mapping is commented.
    void *view_2b = MapViewOfFile3(
        section_2,
        GetCurrentProcess(),
        addr_2,
        0,
        (size_t)alloc_size.QuadPart,
        MEM_REPLACE_PLACEHOLDER,
        PAGE_READWRITE,
        NULL,
        0
    );

    if (view_2b == NULL) {
        printf("view_2b: MapViewOfFile3 failed\n");
        print_last_error();
        exit(0);
    }

    // Check that we have the same data as the one we touched.
    check_pages(view_1, (size_t)alloc_size.QuadPart, page_size);

    return 0;
}

标签: cwindowswinapimemory-managementmemory-mapping

解决方案


根据VirtualAlloc2MEM_REPLACE_PLACEHOLDER AllocationType将占位符替换为正常的私有分配。所以只是评论placeholder_3行。你不能MapViewOfFile3在占位符被替换后。
以下代码适用于我。

int
main(void)
{
    size_t page_size;
    ULARGE_INTEGER alloc_size{};

    get_page_size(&page_size);
    //alloc_size.QuadPart = page_size;
    alloc_size.QuadPart = 0x10000000;

    // PART 1
    // -------------------------------------------------------------------------

    // Allocate 256 MB at a fixed address.
    // This is done by reserving a block of virtual addresses, creating
    // paging-backed sections, and mapping them onto the allocation block.

    void* addr_1 = (void*)0x10000000; // Aligned with allocation granularity.

    void* placeholder_1 = VirtualAlloc2(
        GetCurrentProcess(),
        addr_1,
        (size_t)alloc_size.QuadPart,
        MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
        PAGE_NOACCESS,
        NULL,
        0
    );

    if (placeholder_1 == NULL) {
        printf("placeholder_1: VirtualAlloc2 failed\n");
        print_last_error();
        exit(0);
    }

    HANDLE section_1 = CreateFileMappingA(
        INVALID_HANDLE_VALUE,
        NULL,
        PAGE_READWRITE,
        alloc_size.HighPart,
        alloc_size.LowPart,
        NULL
    );

    if (section_1 == NULL) {
        printf("section_1: CreateFileMappingA failed\n");
        print_last_error();
        exit(0);
    }

    void* view_1 = MapViewOfFile3(
        section_1,
        GetCurrentProcess(),
        addr_1,
        0,
        (size_t)alloc_size.QuadPart,
        MEM_REPLACE_PLACEHOLDER,
        PAGE_READWRITE,
        NULL,
        0
    );

    if (view_1 == NULL) {
        printf("view_1: MapViewOfFile3 failed\n");
        print_last_error();
        exit(0);
    }

    // Touch each page just to make sure that they are fully committed.
    //touch_pages(view_1, (size_t)alloc_size.QuadPart, page_size);

    // PART 2
    // -------------------------------------------------------------------------

    // Grow the previous allocation by another 256 MB.
    // The exercise here is to try replicating Linux's `mremap()` by using
    // `MEM_COALESCE_PLACEHOLDERS`.
    // The idea is to reserve another block of virtual addresses just after
    // the first allocation block, merging both blocks into a single one,
    // creating another bunch of paging-backed sections, and mapping
    // all the pages onto the merged allocation block.

    //void* addr_2 = (void*)((PCHAR)addr_1 + alloc_size.QuadPart); // Aligned with allocation granularity.
    void* addr_2 = (void*)0x20000000; // Aligned with allocation granularity.

    void* placeholder_2 = VirtualAlloc2(
        GetCurrentProcess(),
        addr_2,
        (size_t)alloc_size.QuadPart,
        MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
        PAGE_NOACCESS,
        NULL,
        0
    );

    if (placeholder_2 == NULL) {
        printf("placeholder_2: VirtualAlloc2 failed\n");
        print_last_error();
        exit(0);
    }

    // It seems that we first need to unmap or else the following call to
    // `VirtualFree()` complains about attempting to access an invalid address.
    if (!UnmapViewOfFile2(
        GetCurrentProcess(),
        view_1,
        MEM_PRESERVE_PLACEHOLDER
    ))
    {
        printf("view_1: UnmapViewOfFile2 failed\n");
        print_last_error();
        exit(0);
    }

    alloc_size.QuadPart *= 2;
    if (!VirtualFree(
        addr_1,
        (size_t)alloc_size.QuadPart,
        MEM_RELEASE |MEM_COALESCE_PLACEHOLDERS
    ))
    {
        printf("placeholder_1: VirtualFree failed\n");
        print_last_error();
        exit(0);
    }

    // Create a new allocation block that represents both previous blocks
    // merged into a single one.
    /*void* placeholder_3 = VirtualAlloc2(
        GetCurrentProcess(),
        addr_1,
        (size_t)alloc_size.QuadPart,
        MEM_RESERVE | MEM_REPLACE_PLACEHOLDER,
        PAGE_NOACCESS,
        NULL,
        0
    );

    if (placeholder_3 == NULL) {
        printf("placeholder_3: VirtualAlloc2 failed\n");
        print_last_error();
        exit(0);
    }*/

    HANDLE section_2 = CreateFileMappingA(
        INVALID_HANDLE_VALUE,
        NULL,
        PAGE_READWRITE,
        alloc_size.HighPart,
        alloc_size.LowPart,
        NULL
    );

    if (section_2 == NULL) {
        printf("section_2: CreateFileMappingA failed\n");
        print_last_error();
        exit(0);
    }

    // This fails due to an attempt to access an invalid addres.
    void* view_1b = MapViewOfFile3(
        section_2,
        GetCurrentProcess(),
        addr_1,
        0,
        (size_t)alloc_size.QuadPart,
        MEM_REPLACE_PLACEHOLDER,
        PAGE_READWRITE,
        NULL,
        0
    );

    if (view_1b == NULL) {
        printf("view_1b: MapViewOfFile3 failed\n");
        print_last_error();
        exit(0);
    }

    char* lpPtr = (char*)view_1b;
    lpPtr[0x20000000 - 1] = 'a';
    //// This fails for the same reason if the previous mapping is commented.
    //void* view_2b = MapViewOfFile3(
    //  section_2,
    //  GetCurrentProcess(),
    //  addr_2,
    //  0,
    //  (size_t)alloc_size.QuadPart,
    //  MEM_REPLACE_PLACEHOLDER,
    //  PAGE_READWRITE,
    //  NULL,
    //  0
    //);

    //if (view_2b == NULL) {
    //  printf("view_2b: MapViewOfFile3 failed\n");
    //  print_last_error();
    //  exit(0);
    //}

    return 0;
}

推荐阅读