mmap更快是因为它绕过Python I/O缓冲层和多次数据拷贝,将文件页直接映射进进程虚拟内存,使读写变为内存地址访问,避免read()/write()系统调用;适合随机访问、小范围读写及多进程共享场景。

为什么 mmap 读写大文件比普通 open 更快?
因为 mmap 绕过了 Python 的 I/O 缓冲层和多次数据拷贝:内核直接把文件页映射进进程虚拟内存,读写操作变成对内存地址的访问,不触发系统调用(如 read()/write())。尤其适合随机访问、频繁小范围读写或需要多进程共享文件内容的场景。
但注意:它不适用于追加写(MAP_SHARED 下 truncate() 可能失效)、也不适合极小文件(映射开销反而更高),且 Windows 上对稀疏文件支持较弱。
如何正确创建可读写的内存映射对象?
关键在 mmap.mmap() 的参数组合——尤其是 length、access 和文件打开模式:
-
length=0表示映射整个文件(必须先用os.stat().st_size获取大小,且文件不能动态增长) - 写入需同时满足:文件以
'r+b'模式打开 +access=mmap.ACCESS_WRITE(或ACCESS_COPY)+flags=mmap.MAP_SHARED(若需同步到磁盘) - Windows 下必须指定
length,不能为0;Linux/macOS 支持0,但依赖内核版本
示例:
import mmap
import os
with open('large.bin', 'r+b') as f:
size = os.stat(f.fileno()).st_size
mm = mmap.mmap(f.fileno(), length=size, access=mmap.ACCESS_WRITE)
如何安全地修改映射区域并确保落盘?
写入后是否立刻写回磁盘,取决于 flags 和手动刷盘动作:
立即学习“Python免费学习笔记(深入)”;
-
MAP_PRIVATE:写时复制,修改不落盘,且其他进程/映射不可见 -
MAP_SHARED:修改会最终同步到文件(但受内核延迟写策略影响,不一定立即) - 强制刷盘用
mm.flush()(等价于msync(MS_SYNC)),不带参数默认刷全部;也可指定偏移和长度:mm.flush(offset, size) - Python 退出前未
flush()或未close(),可能导致数据丢失(尤其MAP_SHARED)
常见崩溃错误和规避方式
最典型的是 ValueError: mmap length is greater than file size 或 WindowsError: [Error 87] 参数错误:
- 映射前确保文件已
truncate()到足够长度(尤其新建文件):f.seek(size-1); f.write(b'\x00') - 避免在映射期间用
os.truncate()或f.truncate()改变文件大小(Linux 可能导致 SIGBUS) - 多线程访问同一
mmap对象需自行加锁(mmap本身不提供线程安全) - 使用
mm[0:100]切片读取时,若越界会抛IndexError,而非静默截断
复杂点在于:映射生命周期必须严格绑定文件描述符生命周期。文件对象关闭后,mmap 对象可能仍可用(取决于平台),但行为未定义——务必先 mm.close(),再 f.close()。


















