c++内存映射文件方法 c++如何使用mmap读写文件

风强小哥_3693

风强小哥_3693

2026-03-29

406人浏览

原创

Linux下mmap文件需三步:open、ftruncate确保文件大小、mmap映射;注意MAP_SHARED回写、msync强持久化、fd须支持映射、错误检查依赖errno。

c++内存映射文件方法 c++如何使用mmap读写文件

Linux 下用 mmap 读写文件,核心就三步:打开 + 映射 + 访问

Windows 没有 mmap,那是 CreateFileMapping 和 MapViewOfFile 的事;C++ 标准库也不提供跨平台内存映射接口。所以谈 mmap,默认你跑在 Linux/Unix 类系统上,且用的是 POSIX 接口。

常见错误是直接对普通文件调用 mmap 却忘了先 ftruncate —— 映射区域不能超出文件当前大小,否则访问时会触发 SIGBUS(不是 SEGFAULT,这点容易误判)。

  • open() 要带 O_RDWR(写必须),如果只读用 O_RDONLY,但后续不能改写
  • ftruncate(fd, size) 必须在 mmap 前调用,确保文件有足够空间——哪怕你只想读,也要保证映射长度 ≤ 文件大小
  • mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0):注意 MAP_SHARED 才能回写到文件;MAP_PRIVATE 改动只在内存,不落盘

mmap 返回 MAP_FAILED 的典型原因

不是所有失败都报 errno = ENOMEM,很多是隐性条件不满足。比如:size 为 0、fd 已关闭、文件系统不支持(如某些 NFS 或 /proc 下的伪文件)、或映射长度超过 RLIMIT_AS 限制。

最容易被忽略的是:文件描述符 fd 必须指向一个**支持 mmap 的文件类型**。普通磁盘文件可以,但管道(pipe)、终端(/dev/tty)、socket、甚至某些 tmpfs 挂载点(取决于内核配置)可能拒绝映射,此时 mmap 直接返回 MAP_FAILED,errno 是 ENODEV 或 EINVAL。

  • 检查 errno 比检查返回值更关键:用 if (addr == MAP_FAILED) { perror("mmap"); }
  • 映射前用 lseek(fd, 0, SEEK_END) + lseek(fd, 0, SEEK_SET) 确认文件可定位,排除只读挂载或权限问题
  • 不要假设 size 可以任意传——内核要求页对齐,实际会向上按页(通常 4KB)取整,但传入非对齐值本身不报错

写完记得 msync 吗?大多数时候不用,但有些场景必须

MAP_SHARED 下,写内存一般会异步刷盘,类似 write() 后依赖内核回写。如果你需要**强持久化保证**(比如关键日志、数据库 WAL),就得调 msync(addr, size, MS_SYNC) —— 否则断电可能丢数据。

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载

但别滥用:MS_SYNC 是阻塞的,性能开销大;MS_ASYNC 只是“建议内核尽快刷”,不保证顺序或完成时间。多数应用只需依赖内核默认行为,除非你在实现存储引擎或做崩溃一致性校验。

  • msync 前不必 munmap,但 munmap 不触发刷盘
  • 映射区域被 fork() 后,子进程继承映射,但父子写同一地址是否冲突,取决于 MAP_SHARED 还是 MAP_PRIVATE
  • 多线程同时写同一映射区域,需自行加锁——mmap 不提供原子性

C++ 封装 mmap 时,RAII 管理比想象中难

表面看,用 unique_ptr + 自定义 deleter 就能自动 munmap,但漏掉两个关键点:一是 fd 本身也要关,二是 msync 时机不可控。强行塞进析构函数,会导致每次销毁都刷盘,性能崩坏。

更现实的做法是:把映射封装成类,但 msync 暴露为显式方法;fd 和 addr 分开管理生命周期;或者干脆不封装,只在局部作用域里直写三行原生调用——简单逻辑下,少一层抽象反而更稳。

  • 自定义 deleter 里调 munmap 是对的,但别在里面调 close(fd) —— fd 可能被其他地方复用
  • mmap 成功后,addr 是 void*,C++ 中转成具体类型指针要小心对齐和别名规则(比如用 reinterpret_cast 读结构体,确保该结构体无 padding 且按需 packed)
  • 调试时用 /proc/PID/maps 查看映射是否生效、权限位是否正确(rw 还是 r-)

内存映射不是银弹,小文件用 read/write 更轻量,频繁随机访问才显优势;而真正难的从来不是调通 mmap,是怎么在崩溃恢复、并发修改、跨进程同步这些边界上不翻车。

C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!

相关文章

c++速学教程(入门到精通)
c++速学教程(入门到精通)

c++怎么学习?c++怎么入门?c++在哪学?c++怎么学才快?不用担心,这里为大家提供了c++速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

c++

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

2025.06.09

5434

18

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

2025.07.04

5611

25

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.11.23

636

5

java中void的含义
java中void的含义

本专题整合了Java中void的相关内容,阅读专题下面的文章了解更多详细内容。

2025.11.27

391

13

C++ 智能指针与现代内存管理
C++ 智能指针与现代内存管理

深入讲解 C++ 现代内存管理的核心工具——智能指针,涵盖 unique_ptr 独占所有权语义、shared_ptr 引用计数机制与循环引用问题、weak_ptr 弱引用的应用场景、make_unique/make_shared 工厂函数的性能优势、自定义删除器的编写、RAII 资源管理思想的实践,以及从裸指针迁移到智能指针的重构策略,帮助开发者编写安全无泄漏的现代 C++ 代码。

2026.04.23

399

31

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

2023.10.19

3248

3

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

2025.10.17

5469

12

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2025.12.29

3909

9

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

2026.01.19

506

15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C++ Core Guidelines
C++ Core Guidelines

共0课时 | 0人学习

C++ Reference
C++ Reference

共0课时 | 0人学习

C++ 官方标准说明
C++ 官方标准说明

共0课时 | 0人学习