#c #windows #winapi #memory-management #memory-mapping
Вопрос:
Я пытаюсь разобраться в концепции 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(amp;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("%sn", buf);
}
// -------------------------------------------------------------------------
int
main(void)
{
size_t page_size;
ULARGE_INTEGER alloc_size;
get_page_size(amp;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 failedn");
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 failedn");
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 failedn");
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 failedn");
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 failedn");
print_last_error();
exit(0);
}
if (!VirtualFree(
placeholder_1,
(size_t)alloc_size.QuadPart * 2,
MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS
))
{
printf("placeholder_1: VirtualFree failedn");
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 failedn");
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 failedn");
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 failedn");
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 failedn");
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;
}
Ответ №1:
Согласно VirtualAlloc2, тип MEM_REPLACE_PLACEHOLDER
распределения заменяет заполнитель обычным частным распределением. Так что просто прокомментируйте placeholder_3
строку. Вы не можете MapViewOfFile3
после замены заполнителя.
Следующий код работает для меня.
int
main(void)
{
size_t page_size;
ULARGE_INTEGER alloc_size{};
get_page_size(amp;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 failedn");
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 failedn");
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 failedn");
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 failedn");
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 failedn");
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 failedn");
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 failedn");
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 failedn");
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 failedn");
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 failedn");
// print_last_error();
// exit(0);
//}
return 0;
}
Комментарии:
1. Спасибо @YangXiaoPo, это, кажется, отлично компилируется, однако, похоже, не сохраняет данные, которые записываются в первом разделе с использованием
touch_pages()
.2.
touch_pages()
это не касается. Я проверил.3. Хм, я тоже пытался, но некоторые ценности, казалось, отличались? Я отредактировал вопрос, добавив
check_pages()
функцию, которую я использую для проверки данных.4.При отладке я обнаружил, что после этого значения памяти изменились
UnmapViewOfFile2
view_1
.