修改内存映射文件大小的正确方法是什么?
修改内存映射文件大小的正确方法
Linux的man mmap手册明确警告:
修改映射文件的底层文件大小,对文件新增或删除区域对应的页的影响是未定义的。
有观点认为只要不访问超出文件边界的数据,直接修改文件大小就可行,但这种做法存在潜在风险,内核行为不可预期。而如果采用先msync、munmap、ftruncate再重新mmap的流程,频繁操作会导致数据在磁盘与内存间反复传输,产生极高开销。针对不同场景,正确的处理方式如下:
扩展文件大小
- 先通过
ftruncate()(或直接向文件末尾写入数据)扩展底层文件的大小 - 调用
mremap()系统调用调整现有映射的内存区域大小,将新增的文件区域映射到内存中。该调用支持MREMAP_MAYMOVE标志,允许内核调整内存区域的位置,能大幅提高映射调整的成功率,且无需解除原有映射,避免了数据来回同步的开销。
缩小文件大小
截断区域在映射末尾
- 若需要保留截断前的末尾数据,先调用
msync()同步对应区域的内存数据到磁盘 - 用
munmap()解除超出新文件大小的那部分内存映射 - 最后调用
ftruncate()缩小文件至目标大小
这种方式仅解除部分映射,无需重新映射整个文件,开销远低于全量解除再映射的流程。
截断区域在映射中间
内核不支持直接截断映射区域的中间部分,这种场景下如果必须操作,仍需走msync→munmap→ftruncate→mmap的流程,但频繁操作这种场景会有较高开销,建议重新设计数据存储结构(比如采用分段映射)来规避此类需求。
额外注意事项
- 确保操作文件时持有正确的权限(如读写权限),避免系统调用失败
- 必须处理所有系统调用的返回值,比如
mremap()可能因内存不足等原因失败,此时需降级到全量解除再映射的方案 - 多线程/多进程共享映射场景下,要做好同步控制,避免其他进程在映射调整期间访问到未定义的内存区域
内容的提问来源于stack exchange,提问作者Fyodor
相关产品推荐
相关产品推荐

