如何调整VirtualAlloc分配的区域的大小?

cib*_*cib 7 c winapi

我想调整由MS窗口的VirtualAlloc分配的内存区域.查看VirtualFree文档,可以仅部分地解除区域,但不可能部分释放它.也就是说,可以释放部分物理内存,但不能释放虚拟内存的一部分.

我知道在这种情况下可能有必要重新分配该区域.但是,复制整个区域的效率会相当低.有没有办法让windows分配一个不同大小的新区域,指向同一个内存?

Dan*_*ien 5

正如您所提到的,似乎不可能部分释放一系列保留页面,因为VirtualFree()文档指出:

如果dwFreeType参数是 MEM_RELEASE,则 [ lpAddress ] 必须是当页面区域被保留时VirtualAlloc函数返回的基地址。

也:

如果dwFreeType参数是 MEM_RELEASE,则 [ dwSize ] 必须为 0(零)。

VirtualFree()本身就是内核函数的一个瘦包装器NtFreeVirtualMemory()它的文档页面(与 for 相同ZwFreeVirtualMemory())也有这样的措辞。

一种可能的解决方法是将单个大型预留与多个较小预留分开。例如,假设您通常一次保留 8 MiB 的虚拟地址空间。您可以改为尝试在 32 个连续的 256 KiB 预留中预留该范围。第一个 256 KiB 预留将包含一个 32 位无符号位字段,如果获得第i256 KiB 预留,则设置i位:

#define NOMINMAX
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#define RESERVATION_SIZE (256*1024)

typedef struct st_first_reservation {
    size_t reservation_size;
    uint32_t rfield;
    char premaining[0];
} st_first_reservation;

int main()
{
    SYSTEM_INFO sys_info = { 0 };
    GetSystemInfo(&sys_info);

    assert((RESERVATION_SIZE % sys_info.dwPageSize) == 0);

    void *vp = VirtualAlloc(NULL, 32*RESERVATION_SIZE, MEM_RESERVE, PAGE_NOACCESS);
    if (VirtualFree(vp, 0, MEM_RELEASE) == 0) {
        fprintf(stderr, "Error: VirtualFree() failed.\n");
        return EXIT_FAILURE;
    }

    st_first_reservation *pfirst_reservation = (st_first_reservation *) VirtualAlloc(vp, RESERVATION_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    if (pfirst_reservation == NULL) {
        pfirst_reservation = (st_first_reservation *) VirtualAlloc(NULL, RESERVATION_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
        if (pfirst_reservation == NULL) {
            fprintf(stderr, "Error: VirtualAlloc() failed.\n");
            return EXIT_FAILURE;
        }
    }

    fprintf(stderr, "pfirst_reservation = 0x%p\n", (void *) pfirst_reservation);

    pfirst_reservation->reservation_size = RESERVATION_SIZE;
    pfirst_reservation->rfield = 1LU;

    char *p = (char *) pfirst_reservation;
    unsigned i = 1;
    for (; i < 32; ++i) {
        vp = VirtualAlloc(p += RESERVATION_SIZE, RESERVATION_SIZE, MEM_RESERVE, PAGE_NOACCESS);
        if (vp != NULL) {
            assert(((void *) vp) == p);
            pfirst_reservation->rfield |= 1LU << i;
            fprintf(stderr, "Obtained reservation #%u\n", i + 1);
        } else {
            fprintf(stderr, "Failed to obtain reservation #%u\n", i + 1);
        }
    }

    fprintf(stderr, "pfirst_reservation->rfield = 0x%08x\n", pfirst_reservation->rfield);

    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

示例输出:

pfirst_reservation = 0x009A0000
获得预订#2
获得预订#3
获得预订#4
获得预订#5
获得预订#6
获得预订#7
获得预订#8
获得预订#9
获得预订#10
获得预订#11
获得预订#12
获得预订#13
获得预订#14
获得预订#15
获得预订#16
获得预订#17
获得预订#18
获得预订#19
获得预订#20
获得预订#21
获得预订#22
获得预订#23
获得预订#24
获得预订#25
获得预订#26
获得预订#27
获得预订#28
获得预订#29
获得预订#30
获得预订#31
获得预订#32
pfirst_reservation->rfield = 0xffffffff

编辑:我发现最好一次“预先保留”32 个 256 KiB 范围,免费,然后尝试重新保留尽可能多的范围。

我更新了上面的代码和示例输出。

在多线程环境中,代码可能会回退到第一个预留的“任意位置”分配。也许尝试RESERVATION_SIZE在保留然后释放的32*RESERVATION_SIZE字节范围内保留字节五次左右是一个好主意,最终回退到“任何地方”分配。