需绕过标准库缓冲,用页对齐内存与系统调用提升i/o性能:一、posix_memalign+write;二、mmap+msync;三、io_uring异步写;四、o_direct直写vector.data();五、aligned_alloc+pwritev2分散写。

如果您需要将std::vector中的数据以页边界对齐方式写入磁盘,从而提升I/O吞吐量并减少内核拷贝开销,则需绕过标准库缓冲机制,直接使用底层系统调用与内存对齐策略。以下是实现该目标的多种方法:
一、使用posix_memalign分配页对齐内存并配合write系统调用
该方法通过分配物理页对齐的内存块,确保向文件写入时满足内核直接I/O(O_DIRECT)或零拷贝路径的前提条件,避免因地址未对齐导致的额外内存拷贝或系统调用失败。
1、调用posix_memalign申请页对齐缓冲区,对齐大小设为getpagesize()返回值。
2、将std::vector中原始数据memcpy至该对齐缓冲区,确保源数据连续且长度不超缓冲容量。
3、以O_DIRECT | O_SYNC标志打开目标文件,获得文件描述符。
4、调用write(fd, aligned_ptr, size)执行写入,其中size必须是页大小的整数倍,不足部分需补零或截断。
5、写入完成后调用fsync(fd)确保数据落盘(若未使用O_SYNC)。
二、使用mmap + msync实现页对齐映射写入
该方法将文件映射为进程虚拟内存,并借助mmap的MAP_HUGETLB或显式对齐参数使映射起始地址落在页边界上,后续通过指针赋值完成写入,由msync触发同步,规避用户态拷贝。
1、预先用truncate扩展目标文件至所需大小,确保空间足够容纳vector数据并向上对齐至页边界。
2、调用mmap,传入MAP_SHARED | MAP_LOCKED标志,并指定offset为0;若需大页支持,添加MAP_HUGETLB及对应hugepage-size参数。
3、检查返回地址是否为页对齐:使用((uintptr_t)addr & (getpagesize()-1)) == 0验证。
4、将std::vector数据逐字节复制到映射地址起始处,或使用memcpy批量填充。
5、调用msync(mapped_addr, length, MS_SYNC)强制将脏页刷入磁盘。
三、使用Linux专用io_uring异步I/O配合对齐缓冲区
该方法利用io_uring提交无阻塞的对齐写请求,将准备好的页对齐缓冲区地址与长度封装为sqe结构体,交由内核异步处理,极大降低上下文切换开销,适用于高吞吐场景。
1、初始化io_uring实例,调用io_uring_queue_init(256, &ring, 0)创建环形队列。
2、分配posix_memalign页对齐缓冲区,并将std::vector内容拷贝进去。
3、从sqring获取一个空闲sqe,调用io_uring_prep_write设置文件描述符、对齐缓冲区指针、数据长度及偏移量。
4、确保写入长度为页大小整数倍,否则在缓冲区末尾补零并调整length字段。
5、调用io_uring_submit提交请求,随后可轮询cqring或等待事件完成。
四、使用O_DIRECT标志配合std::vector.data()的条件直写
当std::vector内部存储已天然满足页对齐(如通过自定义allocator分配),可跳过中间拷贝,直接以data()指针发起write,但需严格校验对齐性与长度约束,否则系统调用将失败并返回EINVAL。
1、获取当前页大小:long page_size = sysconf(_SC_PAGESIZE)。
2、验证vector.data()地址是否页对齐:(reinterpret_cast<uintptr_t>(vec.data()) % page_size) == 0</uintptr_t>。
3、验证vec.size()是否为页大小整数倍,非整除时需截断或扩展缓冲区。
4、以O_DIRECT打开文件,调用write(fd, vec.data(), vec.size())。
5、捕获返回值,若为-1且errno == EINVAL,说明对齐或长度不满足要求,需回退至其他方法。
五、基于aligned_alloc与pwritev2的矢量分散写入
该方法适用于需将vector分段写入文件不同偏移位置的场景,结合pwritev2的RWF_DSYNC标志与对齐iov_base,实现多段页对齐原子写入,避免seek开销。
1、为每段数据分别调用aligned_alloc(getpagesize(), segment_size)分配对齐内存。
2、将vector中对应区间数据复制到各对齐段中。
3、构造iovec数组,每个元素指向一段对齐内存及其长度。
4、调用pwritev2(fd, iovec_array, iovcnt, offset, RWF_DSYNC),其中offset为文件内起始写入位置。
5、确认返回值等于所有segment_size之和,否则部分写入失败,需记录错误位置并重试。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











