Как вы упомянули, представляется невозможным частично освободить диапазон зарезервированных страниц, поскольку документация VirtualFree()
гласит:
Если параметр dwFreeType имеет значение MEM_RELEASE, [ lpAddress ] должен быть базовым адресом, возвращаемым функцией VirtualAlloc , когда регион страниц [было] зарезервировано.
а также:
Если параметр dwFreeType имеет значение MEM_RELEASE, [ dwSize ] должно быть 0 (ноль).
VirtualFree()
сам по себе является тонкой оболочкой функции ядра NtFreeVirtualMemory()
. На странице документации (такой же, как для ZwFreeVirtualMemory()
) также есть эта формулировка.
Один из возможных обходных путей - разделить одно большое резервирование с несколькими меньшими. Например, предположим, что вы обычно резервируете 8 МБ виртуального адресного пространства одновременно. Вместо этого вы можете попытаться зарезервировать диапазон в тридцати двух смежных резервациях по 256 КиБ. Первое резервирование 256 КиБ будет содержать 32-разрядное поле без знака, где бит i th устанавливается, если i th Зарезервировано 256 КиБ:
#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;
}
Пример вывода:
pfirst_reservation = 0x009A0000
Obtained reservation #2
Obtained reservation #3
Obtained reservation #4
Obtained reservation #5
Obtained reservation #6
Obtained reservation #7
Obtained reservation #8
Obtained reservation #9
Obtained reservation #10
Obtained reservation #11
Obtained reservation #12
Obtained reservation #13
Obtained reservation #14
Obtained reservation #15
Obtained reservation #16
Obtained reservation #17
Obtained reservation #18
Obtained reservation #19
Obtained reservation #20
Obtained reservation #21
Obtained reservation #22
Obtained reservation #23
Obtained reservation #24
Obtained reservation #25
Obtained reservation #26
Obtained reservation #27
Obtained reservation #28
Obtained reservation #29
Obtained reservation #30
Obtained reservation #31
Obtained reservation #32
pfirst_reservation->rfield = 0xffffffff
РЕДАКТИРОВАТЬ: Я обнаружил, что гораздо лучше "предварительно зарезервировать" тридцать два 256-килобайтных диапазона одновременно, бесплатно, а затем попытаться зарезервировать как можно больше .
Я обновил код и пример вывода выше.
В многопоточной среде код может возвращаться к распределению «место где угодно» первого резервирования. Возможно, это хорошая идея - попытаться зарезервировать RESERVATION_SIZE
байт в зарезервированном-то-освобожденном диапазоне 32*RESERVATION_SIZE
байт пять или более раз, в конце концов вернувшись к распределению «где угодно».