linux系统编程——文件IO——文件映射
1. 什么是文件映射
将文件加载到页缓存,并将页缓存映射到用户虚拟空间,让应用程序直接访问页缓存。
2. api
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(void *addr, size_t length);
@addr : 多传NULL,给内核提建议
@length :
@prot : 内存保护方式,PROT_READ PROT_WRITE PROT_EXEC
@flags : 映射类型
MAP_FIXED : 要求将addr作为必要条件,若无法映射则返回错误。
MAP_PRIVATE : 不与其他进程共享映射,采用 copy-on-write 操作方式。因此此进程在内存上的修改不会反映在实际文件或其他进程映射中
MAP_SHARED : 与其他进程共享此映射,写映射等效于写文件。
你必须指定 MAP_SHARED 或 MAP_PRIVATED
3. 页对齐
页缓存的基本单位是页大小,所以 映射的起始地址必须在页大小的整数倍上(addr + offset)。
且映射的页面大小为页的整数倍,所以len若不足一个页面,则额外字节补0,且写额外字节不会影响文件。只有len个字节会被写回到文件。
4. mmap的优缺点
相对于 read write 有优点:
- 读写时,避免read write在应用空间的副本
- 写页面不会陷入内核,所以无上下文切换和系统调用开销
- 能实现多进程共享
- 查找映射文件,只需要指针操作,避免lseek
缺点:
- 内存映射是页面大小的整数倍,若小文件则浪费空间
- 内存映射必须放入进程虚拟地址空间,若使用32bit空间,内存映射非常大时,会导致地址空间碎裂,从而很难找到连续的大型可用空间。在64bit上极少出现这种问题
- 创建和维护内存映射以及内核内部相关数据结构是要付出代价的。此开销通常会和双重副本开销相抵,尤其是对大型及频繁被访问的文件而言。
总结:
映射大型文件(浪费的空间占整个映射相当小的比例)或文件映射的大小为页面的大小的整数倍(无浪费空间),则可以从mmap的优点获得好处
5. 如何获得页大小
5.1 sysconf
long sysconf(int name);
sysconf是获得页大小的POSIX方法。用于获得系统特有信息。
name为配置项的值,若name无效则返回-1。
获得页大小:
long page_size = sysconf(_SC_PAGESIZE);
5.2 getpagesize()
int getpagesize(void);
linux提供 getpagesize返回页大小
5.3 PAGE_SIZE
页大小被静态定义在 PAGE_SIZE宏(<asm/page.h>)
int page_size = PAGE_SIZE;
由于未来内核可能不会导出宏到用户空间,所以不应该使用宏,sysconf是最好方法。
6. 写回
页缓存被写后,成了脏页,那什么时候写回呢?
和普通的write写回情况一样:要么内存用完了,被释放。要么时间阈值到了。
若想立即写回需要 msync
int msync(void *addr, size_t len, int flags);
flags:
MS_ASYNC : 异步化指定写回操作。即调用msync后立即返回,但会等会进行写回操作
MS_SYNC : 同步化指定写回操作。即msync将不会返回,直到写回完成。
通常使用 MS_ASYNC即可
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?