跨平台内存映射封装不能直接桥接mmap与CreateFileMapping,因语义差异大;应返回std::span而非裸指针或vector;open_mode与map_access需分层设计;同步行为必须显式暴露且默认不自动刷盘。

为什么不能直接用 mmap 或 CreateFileMapping 写业务逻辑
跨平台内存映射封装最常踩的坑,是把 Windows 的 CreateFileMapping/MapViewOfFile 和 POSIX 的 mmap 当成语义等价接口直接桥接。它们根本不是一回事:mmap 可以映射普通文件、设备、匿名内存,还能设 MAP_SHARED/MAP_PRIVATE;Windows 的映射对象必须是句柄,且 FILE_MAP_WRITE 不自动同步到磁盘,得手动 FlushViewOfFile + FlushFileBuffers。直接套一层“统一接口”反而会让 Linux 用户意外丢数据,或让 Windows 用户遇到权限失败却报 EINVAL 这种模糊错误。
std::span<:byte></:byte> 是唯一安全的返回视图类型
封装类暴露映射内容时,绝不能返回裸指针或 void* —— 它们无法携带长度信息,调用方极易越界读写。也别用 std::vector:内存映射不归你管生命周期,拷贝数据就失去映射意义。正确做法是返回 std::span<:byte></:byte>(C++20)或自定义轻量视图类(C++17 可用 gsl::span 或手写)。它只存指针+大小,无拷贝开销,且编译期/运行期都可做边界检查(配合 at() 或断言)。
示例关键片段:
class MemMap {
public:
std::span<std::byte> view() const noexcept {
return {static_cast<std::byte*>(mapped_ptr_), size_};
}
private:
void* mapped_ptr_ = nullptr;
size_t size_ = 0;
};打开模式和保护标志必须分两层设计
POSIX 的 prot(PROT_READ/PROT_WRITE)和 Windows 的 dwProtect(PAGE_READONLY/PAGE_READWRITE)看着像,但实际约束粒度不同:Windows 的保护在 CreateFileMapping 阶段就定死,而 mmap 的 prot 可后续用 mprotect 动态改。封装层若强行统一,会丢失灵活性或引入平台特有 bug。
立即学习“C++免费学习笔记(深入)”;
建议拆成两个枚举:
-
open_mode:控制文件打开权限(read_only,read_write,create_if_missing),影响底层open()/CreateFile() -
map_access:控制映射后内存页的访问能力(read,read_write,copy_on_write),对应mmap的prot和flags组合,或 Windows 的dwDesiredAccess+dwProtect
这样 Linux 用户能明确区分“文件可写”和“映射页可写”,Windows 用户也能避免传错 PAGE_EXECUTE_READ 这类非法组合。
同步行为必须显式暴露且默认保守
跨平台封装最容易被忽略的点:写入是否立即落盘?mmap 默认 MAP_SHARED 下,修改内存后需 msync(MS_SYNC) 才保证刷盘;Windows 下即使 FILE_MAP_WRITE,也必须配对 FlushViewOfFile + FlushFileBuffers。封装类如果隐藏这些,用户会在 macOS 上发现数据没写进文件,或在 Windows 上遇到映射区突然不可写。
解决方法:
- 提供
flush()成员函数,内部按平台调用对应同步原语 - 构造时加
auto_flush_on_destruct参数,默认false—— 强制用户思考持久化时机 - 文档里明确写清:“本类不自动
msync/FlushViewOfFile,除非你调用flush()”
否则,一个以为“写完内存就等于写完文件”的用户,会在程序崩溃后发现最后 5 秒数据全丢了。


















