应使用 mmap 的场景是:①频繁随机访问大文件(如解析 elf、数据库索引);②文件大小超物理内存 1/3,需内核按需分页避免 oom;③无需修改文件且可接受额外文件句柄。

mmap 是 Python 读大二进制文件最直接的提速手段,但不是所有场景都适用——它只在「随机访问 + 文件远大于内存」时显著胜出;顺序读取反而可能更慢。
什么时候该用 mmap 而不是 open().read()?
核心判断依据是访问模式和文件大小:
- 你要频繁跳转读取不同偏移位置(比如解析 ELF、数据库索引、自定义二进制协议),
mmap省去反复seek()和系统调用开销 - 文件大小超过物理内存的 1/3(例如 20GB 文件跑在 64GB 内存机器上),
mmap让内核按需分页加载,避免一次性read()触发 OOM 或 swap - 你不需要修改文件内容,且不介意多一个文件句柄(
mmap会保持open()句柄打开) - 反例:纯顺序扫描 10GB 日志文件 → 用
for line in f:更稳;小文件(read() 更快,mmap 启动成本反而高
mmap.mmap() 的关键参数怎么选?
最常踩坑的是 length 和 access 参数:
-
length=0表示映射整个文件(推荐),但必须确保文件不被外部截断,否则访问末尾会触发ValueError: mmap length is greater than file size -
access=mmap.ACCESS_READ(默认)足够读取;若要写入,必须用ACCESS_WRITE且文件以r+模式打开,否则报PermissionError -
offset要对齐到系统页大小(通常是 4096),否则抛ValueError: offset must be a multiple of page size;可用os.stat().st_size & ~(mmap.PAGESIZE-1)手动对齐 - Windows 下注意:不能映射空文件,会直接报
WindowsError: [Error 87]
如何安全地读取结构化二进制数据(如 uint32、float64)?
别直接用 mmap 对象切片后丢给 struct.unpack() —— 它不保证字节对齐,容易触发 struct.error: unpack requires a buffer of at least X bytes:
- 用
mmap_obj[offset:offset+size]切出子段后,先转成bytes或memoryview再解包:data = mmap_obj[1024:1024+4] val = struct.unpack('<i bytes uint32></i> - 更高效的方式是用
numpy.frombuffer()(如果已用 NumPy):arr = np.frombuffer(mmap_obj, dtype=np.uint32, offset=1024, count=1000)
- 务必检查
len(mmap_obj)是否足够覆盖你要读的范围,越界读不会报错但返回零填充或脏数据
为什么程序退出后文件还被占用(Windows 常见)?
根本原因是没显式关闭 mmap 对象或底层文件对象:
-
mmap对象不支持上下文管理器(with语句),必须手动调用.close() - 即使
mmap关闭了,原始open()返回的文件对象也得关,否则 Windows 会锁住文件:f = open('big.bin', 'rb') mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) # ... use mm ... mm.close() f.close() # 这行不能省 - 更稳妥写法是用
try/finally包裹,或者把mmap和文件对象封装进自定义类里统一清理
真正麻烦的是跨进程共享 mmap(比如父子进程通信),这时得用 mmap.mmap(-1, size) 创建匿名映射,再通过 fork() 继承;文件映射在 fork 后默认不共享写入,这点容易误判。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











