内存映射文件写入时htop显示读IO占用带宽的原因及规避方案问询
读IO产生的原因
- 操作系统对内存映射IO的操作以内存页为最小单位(默认页大小通常为4KB,大页模式下为2MB/1GB)。当你首次写入某个映射地址时,如果该地址对应的页尚未加载到内核页缓存,内核会先触发一次磁盘读,将对应磁盘页的完整内容载入内存,再允许你修改页内数据,修改后的脏页后续会被内核异步刷回磁盘。
- 你的测试代码是顺序覆盖整个4GB的映射空间,每处理一个未缓存的新页就会对应1次读IO + 1次写IO,因此统计下来读写带宽各占一半,这是默认配置下的预期行为。
可采用的规避方案
如果不需要保留原文件的内容,可通过以下方式消除或降低读IO开销:
方案1:打开文件时清空原有内容
调用open时添加O_TRUNC标志,清空文件原有内容后再设置文件大小,此时整个文件为文件系统空洞,内核知道空洞对应的页内容全为0,无需从磁盘读取,写入时直接分配零页即可,可消除绝大多数读IO。
代码修改示例:int fd = open("./mmapfile", O_RDWR | O_CREAT | O_TRUNC, 0644);注意:该操作会删除原有文件的所有数据,仅适用于不需要保留原文件内容的场景
方案2:预分配文件空间并标记为零内容
针对ext4、XFS等主流Linux文件系统,可使用fallocate系统调用结合FALLOC_FL_ZERO_RANGE标志预分配文件空间,并标记所有块内容为0,内核会跳过这些块的读操作:
代码修改示例(在ftruncate后添加):fallocate(fd, FALLOC_FL_ZERO_RANGE, 0, capacity);方案3:调整页缓存预读策略
调用mmap后立即通过madvise告知内核你的访问模式,让内核提前批量预读页,降低随机读的开销:
代码修改示例:madvise(ptr, capacity, MADV_SEQUENTIAL | MADV_WILLNEED);该方案不能完全消除读IO,但可以降低读操作对整体写入性能的影响。
方案4:使用大页映射
配置系统开启透明大页,或在mmap时指定大页映射标志,增大单页大小,降低页操作的总次数,进而减少读IO的总开销。方案5:改用直接IO写入
如果你的场景不需要内存映射的随机访问能力,可改为使用open加O_DIRECT标志,按页大小对齐的块进行写入,完全绕过页缓存,不会产生额外读IO。
如果你的业务场景需要保留原有文件的部分内容,仅做局部写入,这类读IO属于必要操作,无法完全规避。
内容的提问来源于stack exchange,提问作者Abator Abetor
相关产品推荐
相关产品推荐

