因为std::vector默认内存分配不保证页对齐,更不满足2MB大页要求,mmap(..., MAP_HUGETLB)会因地址未对齐返回EINVAL;必须先用posix_memalign分配对齐内存,再拷贝数据,最后映射。

为什么 mmap + MAP_HUGETLB 不适合直接写 vector 数据
直接拿 std::vector 的 .data() 去做 mmap 映射并写盘,大概率失败——因为 std::vector 内存由 new 或 malloc 分配,默认不保证页对齐,更别说 2MB 大页。系统会拒绝把非对齐地址传给 mmap(..., MAP_HUGETLB),报错 Invalid argument。
真正要做的,是「先按页对齐分配内存 → 把 vector 数据拷过去 → mmap 映射这块对齐内存 → 走 msync 刷盘」。中间不能跳步,尤其不能指望 std::vector 自己对齐。
-
std::vector默认分配器不支持对齐控制(C++17aligned_alloc也不被std::allocator直接封装) - 用
posix_memalign或_aligned_malloc(Windows)手动分配,再用std::uninitialized_copy搬数据 - 分配大小必须向上取整到页边界(如 4KB),且起始地址本身也要对齐
如何用 posix_memalign 分配 4KB 对齐内存并安全搬运 vector 数据
这是最轻量、跨 Linux/macOS 的方案。注意:不是所有 vector 都能无脑搬——若元素含非平凡析构函数或自定义拷贝逻辑,得用 std::uninitialized_copy 而非 memcpy。
void* aligned_ptr = nullptr;
size_t page_size = sysconf(_SC_PAGESIZE); // 通常是 4096
size_t aligned_size = ((vec.size() * sizeof(T)) + page_size - 1) & ~(page_size - 1);
int ret = posix_memalign(&aligned_ptr, page_size, aligned_size);
if (ret != 0) { /* handle error */ }
<p>// 安全搬运:假设 T 是 trivially copyable
std::uninitialized_copy(vec.begin(), vec.end(), static_cast<t>(aligned_ptr));
</t></p>
- 别用
std::vector::data()直接 cast 成对齐指针——地址不对齐,memcpy可能不报错但mmap会失败 -
aligned_size必须 ≥ 实际数据长度,否则msync刷盘时可能截断 - Windows 下换用
_aligned_malloc(size, page_size),释放用_aligned_free
用 O_DIRECT + 对齐 buffer 写文件比 mmap 更可控
如果目标是“高速流式写入”,且你不需要 mmap 提供的随机读/共享映射能力,O_DIRECT 往往更稳——它绕过 page cache,直接进块层,只要 buffer 地址和长度都按 512B(或文件系统逻辑块大小)对齐即可。
关键点:O_DIRECT 要求 buffer 地址、偏移、长度三者都对齐;Linux 下常用 512B,但某些 XFS/ext4 启用大块时需 4KB。
- 用
posix_memalign(..., 4096, ...)分配 buffer,确保地址对齐 - 每次
write前检查offset % 4096 == 0且len % 4096 == 0 - 打开文件必须带
O_DIRECT标志,且文件需已存在(O_TRUNC不行)、不能是 pipe/socket - 出错时 errno 可能是
EINVAL(对齐不符)或ENOTBLK(文件系统不支持)
msync 刷盘时机与 MAP_SYNC 的现实限制
即使你成功 mmap 了对齐内存,调 msync(addr, len, MS_SYNC) 也不等于立刻落盘——它只保证数据进入 page cache 并标记为脏;真刷到磁盘还得靠内核回写线程,或加 MS_INVALIDATE 强制刷新底层设备缓存(需要硬件支持)。
MAP_SYNC 看起来理想,但它依赖 CONFIG_FS_DAX 和 DAX-capable 文件系统(如 XFS + dax mount),普通 ext4 不支持。误用会静默退化成普通 mmap,毫无加速效果。
- 确认是否启用 DAX:
mount | grep dax,输出应含dax=always或dax=iflag - 写完后优先用
fsync(fd)(针对 fd)而非依赖msync,更可靠 - 若追求极致吞吐,考虑预分配文件大小(
fallocate)+O_DSYNC,减少元数据更新开销
对齐不是目的,是绕过内核路径的必要条件;而“高速”取决于你是否真的避开了 page cache、是否匹配了存储栈的真实瓶颈——比如 NVMe 上 O_DIRECT 可能不如 buffered write,因为后者利用了内核的 I/O 合并与预读。动手前,先 iostat -x 1 看清 await 和 %util 卡在哪。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











