c - 合并 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;
}
解决方案
根据VirtualAlloc2,MEM_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;
}
推荐阅读
- entity-framework - Add-Migration 在包管理器控制台中有效,但在 CLI 中无效
- ios - 尝试从 Firebase 存储下载文件时出现 SSL 错误 - iOS
- node.js - 在生产中验证谷歌云运行应用程序
- c# - 拖动图片框
- bash - BASH 如果条件总是返回“真”
- javascript - 无法在 Heroku 上部署,我的未知错误消息
- python - 如何使用 .ui 文件使用 PyQt5 更改窗口/小部件?
- javascript - 在 GraphQL 查询中传递自定义变量
- django - 使用 Web3py 在 coinpayments 成功支付上发送代币
- lua - 如何在 Lua 中测试 None 值?