Linux零拷贝之mmap

mmap内存映射原理

在这里插入图片描述  mmap内存映射的实现过程,总的来说可以分为三个阶段:

  1. 创建虚拟空间:应用进程启动映射,在进程的虚拟地址空间中,寻找一段空闲的满足要求的连续的虚拟地址作为映射区域;
  2. 建立地址映射:调用系统函数mmap(),创建页表,存储文件物理地址和进程虚拟地址的映射关系;
  3. 应用进程对映射区域进行读写;

mmap读流程

  1. 应用进程发起读请求,访问虚拟地址空间这一段映射地址,通过查询页表,发现这一段地址并不在物理页面上。因为目前只建立了地址映射,真正的硬盘数据还没有拷贝到内存中,因此引发缺页异常;
  2. CPU接受到缺页中断后,向DMA控制器发出指令;
  3. DMA控制器开始将文件数据拷贝到物理内存;
  4. 拷贝完成后,更新页表,建立虚拟地址到物理内存的映射;
  5. 重新执行步骤1,获取到数据;

在这里插入图片描述

mmap写流程

在这里插入图片描述

  1. 应用进程发起写请求,访问虚拟地址空间这一段映射地址,通过页表查看是否存在对应的物理内存,如果不存在,则通过缺页异常加载对应的页;
  2. 页表中查询到对应物理内存后,直接写入数据;
  3. 用户主动触发刷盘,或者内核按照某种规则触发刷盘;
  4. DMA控制器将内核缓冲区数据写入磁盘;

适用/不适用场景

不适用场景

  由于mmap()的实现很复杂,调用mmap()将会带来额外的开销,因此在一些情况下,没有使用mmap()的必要:

  1. 访问小文件时,直接使用read()或write()将更加高效;
  2. 单个进程对文件执行顺序访问时(sequential access),使用mmap()几乎不会带来性能上的提升。比如,使用read()顺序读取文件时,文件系统会使用 read-ahead 的方式提前将文件内容缓存到文件系统的缓冲区,因此使用read()将很大程度上可以命中缓存;

适用场景

  1. 对文件执行随机访问时,如果使用read()或write(),则意味着较低的 cache 命中率。这种情况下使用mmap()通常将更高效;
  2. 多个进程同时访问同一个文件时(无论是顺序访问还是随机访问),如果使用mmap(),那么 OS 缓冲区的文件内容可以在多个进程之间共享,从操作系统角度来看,使用mmap()可以大大节省内存。

Java应用

  Java NIO中的FileChannel.map()方法是基于mmap()函数实现的,常用函数关系如下所示:
在这里插入图片描述

mmap注意点

  1. 对于大文件而言,内存映射比普通IO流要快,小文件则未必;
  2. 不要经常调用MappedByteBuffer.force()方法,这个方法强制操作系统将内存中的内容写入硬盘,所以如果你在每次写内存映射文件后都调用force()方法,你就不能真正从内存映射文件中获益,而是跟disk IO差不多。
  3. 读写内存映射文件是操作系统来负责的,因此,即使你的Java程序在写入内存后就挂掉了,只要操作系统工作正常,数据就会写入磁盘
  4. 如果电源故障或者主机瘫痪,有可能内存映射文件还没有写入磁盘,意味着可能会丢失一些关键数据。

参考:

  1. 认真分析mmap:是什么 为什么 怎么用
  2. 内存映射函数remap_pfn_range学习——示例分析(1)
  3. Java文件映射[mmap]全接触
  4. 支撑百万并发的“零拷贝”技术,你了解吗?
展开阅读全文
©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值