虚拟内存映射本质是绕过标准i/o,将文件或内存页直接映射到进程地址空间,返回void*可强转为任意类型指针实现零开销内存式访问,但需手动管理生命周期,否则易泄漏或崩溃。

直接用指针操作虚拟内存映射,本质是绕过标准 I/O,让操作系统把文件或内存页映射进进程地址空间——返回的 void* 可强制转为任意类型指针,后续访问就像读写普通内存数组,零系统调用开销。但这不是 C++ 语言特性,而是 OS API 的能力封装,必须手动管理生命周期,否则极易泄漏或崩溃。
Windows 下用 CreateFileMapping + MapViewOfFile 获取可读写指针
映射后拿到的是裸指针,类型转换自由,但每一步都需校验:
-
CreateFile必须带GENERIC_READ | GENERIC_WRITE权限,即使只读映射也得可写打开,否则CreateFileMapping失败 -
CreateFileMapping的dwMaximumSizeHigh/dwMaximumSizeLow要填真实文件大小(64 位需拆),设为 0 会返回INVALID_HANDLE_VALUE -
MapViewOfFile的dwFileOffsetLow和dwFileOffsetHigh必须页对齐(通常是 4096 字节),否则返回nullptr - 成功后得到
void*,可直接reinterpret_cast<mystruct>(ptr)</mystruct>访问,但结构体字段布局、字节序、填充必须与文件二进制严格一致 - 必须配对调用
UnmapViewOfFile(ptr)和CloseHandle(hMap),漏掉任一都会导致句柄/映射泄漏
Linux 下用 mmap 映射并避免 SIGBUS 和权限陷阱
mmap 返回的指针同样可强转,但参数语义和错误表现更隐蔽:
-
prot不能只写PROT_READ:若后续用memcpy往里填数据,必须设PROT_READ | PROT_WRITE,且flags中要有MAP_SHARED(否则写入不落盘) -
offset必须是页对齐值,可用getpagesize()获取,硬写 4096 在某些 ARM 或 RISC-V 系统上会失败 - 映射后若文件被其他进程
truncate()缩小,访问超出新长度的部分会触发SIGBUS(不是SEGFAULT),需注册信号处理器或提前检查文件大小 - 映射成功后,原始
fd仍需显式close(),不关会导致文件描述符耗尽 - 必须调
munmap(ptr, len),否则虚拟内存泄漏——比堆泄漏更难排查,/proc/self/status的VmSize会持续上涨
为什么不能用 std::shared_ptr<void></void> 自动管理映射内存
看似方便,实则危险:映射内存不是堆内存,shared_ptr 析构时默认调 delete 或自定义 deleter,而 UnmapViewOfFile(Windows)和 munmap(Linux)既不是 free 语义,也不能传给 default_delete。
- 若用
shared_ptr并传UnmapViewOfFile作 deleter,Windows 上需将void*强转为LPCVOID,但该函数不接受void*类型参数,编译不过 - 若自己写 lambda deleter,容易忽略平台差异:Windows 需两个句柄(映射句柄 + 视图指针),Linux 只需地址+长度,混用会导致未定义行为
- 正确做法是 RAII 封装:构造函数完成映射并保存所有必要状态(句柄/地址/长度/OS 标志),析构函数按平台调对应解映射 API
- 禁止跨平台共享同一份
shared_ptr实例——Windows 和 Linux 的清理逻辑无法统一
跨平台读取前必做的三件事:路径、大小、权限
映射失败常因前置检查缺失,而非 API 调用本身出错:
- 用
std::filesystem::is_regular_file(path)排除符号链接、目录、设备文件,否则 WindowsCreateFile可能静默失败,Linuxmmap直接返回EINVAL - 用
std::filesystem::file_size(path)获取真实大小,空文件(0 字节)映射在 Windows 可能返回nullptr,Linux 返回MAP_FAILED - 用
std::filesystem::status(path).permissions()检查是否含owner_read,权限不足时错误码不直观(如 Windows 返回ERROR_ACCESS_DENIED,易误判为路径错)
裸指针本身没有问题,问题在于它把所有责任——对齐、权限、生命周期、跨平台差异——全推给了开发者。最容易被忽略的是:映射区域的起始偏移和长度必须页对齐,而这个“页”在不同系统、甚至同系统不同架构下都可能不同;一旦越界或未对齐,API 不报错,只在首次访问时崩,调试成本极高。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











