深入剖析mmap原理 - 从三个关键问题说起

2023-11-02

作者:招财二师兄
链接:https://www.jianshu.com/p/eece39beee20
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

对于mmap,您是否能从原理上解析以下三个问题:

1:mmap比物理内存+swap空间大情况下,是否有问题?
2:MAP_SHARED,MAP_PRIVATE,MAP_ANONYMOUS,MAP_NORESERVE到底有什么区别?
3:常听说mmap的读写比传统的系统调用(read, write)快,但真的是这样子吗?原因是什么?
要解决这些疑问,可能还需要在操作系统层面多了解。本文将尝试通过这些问题深入剖析,希望通过这篇文章,能使大家对mmap有较深入的认识,也能在存储引擎的设计中,有所参考

背景
最近在研发分布式日志存储系统,这是一个基于Raft协议的自研分布式日志存储系统,Logstore则是底层存储引擎。

Logstore中,使用mmap对数据文件进行读写。Logstore的存储结构简化如下图:

在这里插入图片描述
Logstore使用了Segments Files + Index Files的方式存储Log,Segment File是存储主体,用于存储Log数据,使用定长的方式,默认每个512M,Index File主要用于Segment File的内容检索。

Logstore使用mmap的方式读写Segment File,Segments Files的个数,主要取决于磁盘空间或者业务需求,一般情况下,Logstore会存储1T~5T的数据。

什么是mmap
我们先看看什么是mmap。

在<<深入理解计算机系统>>这本书中,mmap定义为:Linux通过将一个虚拟内存区域与一个磁盘上的对象(object)关联起来,以初始化这个虚拟内存区域的内容,这个过程称为内存映射(memory mapping)。

在Logstore中,mapping的对象是普通文件(Segment File)。

mmap的原理
mmap在进程虚拟内存做了什么
我们先来简单看一下mapping一个文件,mmap做了什么事情。如下图所示:

在这里插入图片描述
假设我们mmap的文件是FileA,在调用mmap之后,会在进程的虚拟内存分配地址空间,创建映射关系。

这里值得注意的是,mmap只是在虚拟内存分配了地址空间,举个例子,假设上述的FileA是2G大小

[dragon@xxx.xxx] ls -lat FileA

2147483648 Apr 25 10:22 FileA

在mmap之后,查看mmap所在进程的maps描述,可以看到

[dragon@xxx.xxx] cat maps
....
7f35eea8d000-7f366ea8d000 rw-s 00000000 08:03 13110516 FileA
....

由上可以看到,在mmap之后,进程的地址空间7f35eea8d000-7f366ea8d000被分配,并且map到FileA,7f366ea8d000减去7f35eea8d000,刚好是2147483648(ps: 这里是整个文件做mapping)

mmap在物理内存做了什么
在Linux中,VM系统通过将虚拟内存分割为称作虚拟页(Virtual Page,VP)大小固定的块来处理磁盘(较低层)与上层数据的传输,一般情况下,每个页的大小默认是4096字节。同样的,物理内存也被分割为物理页(Physical Page,PP),也为4096字节。

上述例子,在mmap之后,如下图:

在这里插入图片描述
在mmap之后,并没有在将文件内容加载到物理页上,只上在虚拟内存中分配了地址空间。当进程在访问这段地址时(通过mmap在写入或读取时FileA),若虚拟内存对应的page没有在物理内存中缓存,则产生"缺页",由内核的缺页异常处理程序处理,将文件对应内容,以页为单位(4096)加载到物理内存,注意是只加载缺页,但也会受操作系统一些调度策略影响,加载的比所需的多,这里就不展开了。
(PS: 再具体一些,进程在访问7f35eea8d000这个进程虚拟地址时,MMU通过查找页表,发现对应内容未缓存在物理内存中,则产生"缺页")

缺页处理后,如下图:在这里插入图片描述
mmap的分类
我认为从原理上,mmap有两种类型,一种是有backend,一种是没有backend。

有backend在这里插入图片描述
这种模式将普通文件做memory mapping(非MAP_ANONYMOUS),所以在mmap系统调用时,需要传入文件的fd。这种模式常见的有两个常用的方式,MAP_SHARED与MAP_PRIVATE,但它们的行为却不相同。

  1. MAP_SHARED

这个方式我认为可以从两个角度去看:

进程间可见:这个被提及太多,就不展开讨论了
写入/更新数据会回写backend,也就是回写文件:这个是很关键的特性,是在Logstore设计实现时,需要考虑的重点。Logstore的一个基本功能就是不断地写入数据,从实现上看就是不断地mmap文件,往内存写入/更新数据以达到写入文件的目的。但物理内存是有限的,在写入数据超过物理内存时,操作系统会进行页置换,根据淘汰算法,将需要淘汰的页置换成所需的新页,而恰恰因为是有backend的,所以mmap对应的内存是可以被淘汰的(若内存页是"脏"的,则操作系统会先将数据回写磁盘再淘汰)。这样,就算mmap的数据远大于物理内存,操作系统也能很好地处理,不会产生功能上的问题。
2) MAP_PRIVATE

这是一个copy-on-write的映射方式。虽然他也是有backend的,但在写入数据时,他会在物理内存copy一份数据出来(以页为单位),而且这些数据是不会被回写到文件的。这里就要注意,因为更新的数据是一个副本,而且不会被回写,这就意味着如果程序运行时不主动释放,若更新的数据超过可用物理内存+swap space,就会遇到OOM Killer。

无backend
无backend通常是MAP_ANONYMOUS,就是将一个区域映射到一个匿名文件,匿名文件是由内核创建的。因为没有backend,写入/更新的数据之后,若不主动释放,这些占用的物理内存是不能被释放的,同样会出现OOM Killer。

mmap比内存+swap空间大情况下,是否有问题
到这里,这个问题就比较好解析了。我们可以将此问题分离为:

虚拟内存是否会出问题
物理内存是否会出问题
– 虚拟内存是否会出问题:

回到上述的"mmap在进程虚拟内存做了什么",我们知道mmap会在进程的虚拟内存中分配地址空间,比如1G的文件,则分配1G的连续地址空间。那究竟可以maping多少呢?在64位操作系统,寻址范围是2^64 ,除去一些内核、进程数据等地址段之外,基本上可以认为可以mapping无限大的数据(不太严谨的说法)。

– 物理内存是否会出问题
回到上述"mmap的分类",对于有backend的mmap,而且是能回写到文件的,映射比内存+swap空间大是没有问题的。但无法回写到文件的,需要非常注意,主动释放。

MAP_NORESERVE
MAP_NORESERVE是mmap的一个参数,MAN的说明是"Do not reserve swap space for this mapping. When swap space is reserved, one has the guarantee that it is possible to modify the mapping."。

我们做个测试:

场景A:物理内存+swap space: 16G,映射文件30G,使用一个进程进行mmap,成功后映射后持续写入数据
场景B:物理内存+swap space: 16G,映射文件15G,使用两个进程进行mmap,成功后映射后持续写入数据
在这里插入图片描述
从上述测试可以看出,从现象上看,NORESERVE是绕过mmap的校验,让其可以mmap成功。但其实在RESERVE的情况下(序列4),从测试结果看,也没有保障。

mmap的性能
mmap的性能经常与系统调用(write/read)做对比。

我们将读写分开看,先尝试从原理上分析两者的差异,然后再通过测试验证。

mmap的写性能
我们先来简单讲讲write系统调用写文件的过程:

在这里插入图片描述
Step1:进程(用户态)调用write系统调用,并告诉内核需要写入数据的开始地址与长度(告诉内核写入的数据在哪)。
Step2:内核write方法,将校验用户态的数据,然后复制到kernel buffer(这里是Page Cache)。
[ ps: 特意查了ext4 write的内核实现,write是直接将user buffer copy到page中 ]
Step3: 由操作系统调用,将脏页回写到磁盘(通常这是异步的)
再来简单讲讲使用mmap时,写入文件流程:

Step1:进程(用户态)将需要写入的数据直接copy到对应的mmap地址(内存copy)
Step2:
2.1) 若mmap地址未对应物理内存,则产生缺页异常,由内核处理
2.2) 若已对应,则直接copy到对应的物理内存
Step3:由操作系统调用,将脏页回写到磁盘(通常这是异步的)
系统调用会对性能有影响,那么从理论上分析:

若每次写入的数据大小接近page size(4096),那么write调用与mmap的写性能应该比较接近(因为系统调用次数相近)
若每次写入的数据非常小,那么write调用的性能应该远慢于mmap的性能。
下面我们对两者进行性能测试:

场景:对2G的文件进行顺序写入(go语言编写)

每次写入大小 | mmap 耗时 | write 耗时
--------------- | ------- | -------- | --------
| 1 byte | 22.14s | >300s
| 100 bytes | 2.84s | 22.86s
| 512 bytes | 2.51s | 5.43s
| 1024 bytes | 2.48s | 3.48s
| 2048 bytes | 2.47s | 2.34s
| 4096 bytes | 2.48s | 1.74s
| 8192 bytes | 2.45s | 1.67s
| 10240 bytes | 2.49s | 1.65s

可以看到mmap在100byte写入时已经基本达到最大写入性能,而write调用需要在4096(也就是一个page size)时,才能达到最大写入性能。

从测试结果可以看出,在写小数据时,mmap会比write调用快,但在写大数据时,反而没那么快(但不太确认是否go的slice copy的性能问题,没时间去测C了)。

测试结果与理论推导吻合。

mmap的读性能
我们还是来简单分析read调用与mmap的流程:

在这里插入图片描述
从图中可以看出,read调用确实比mmap多一次copy。因为read调用,进程是无法直接访问kernel space的,所以在read系统调用返回前,内核需要将数据从内核复制到进程指定的buffer。但mmap之后,进程可以直接访问mmap的数据(page cache)。

从原理上看,read性能会比mmap慢。

接下来实测一下性能区别:

场景:对2G的文件进行顺序读取(go语言编写)
(ps: 为了避免磁盘对测试的影响,我让2G文件都缓存在pagecache中)

每次读取大小 | mmap 耗时 | write 耗时
--------------- | ------- | -------- | --------
| 1 byte | 8215.4ms | > 300s
| 100 bytes | 86.4ms | 8100.9ms
| 512 bytes | 16.14ms | 1851.45ms
| 1024 bytes | 8.11ms | 992.71ms
| 2048 bytes | 4.09ms | 636.85ms
| 4096 bytes | 2.07ms | 558.10ms
| 8192 bytes | 1.06ms | 444.83ms
| 10240 bytes | 867.88µs | 475.28ms

由上可以看出,在read上面,mmap比write的性能差别还是很大的。测试结果与理论推导吻合。

结束语
对mmap的深入了解,能帮助我们在设计存储系统时,更好地进行决策。
比如,假设需要设计一个底层的数据结构是B+ Tree,node操作以Page单位的单机存储引擎,根据上述推论,写入使用系统调用,而读取使用mmap,可以达到最优的性能。而LMDB就是如此实现的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入剖析mmap原理 - 从三个关键问题说起 的相关文章

随机推荐

  • SQL注入绕过的姿势

    1 注释符绕过 常用的注释符有 1 注释内容 2 注释内容 3 注释内容 eg union select 1 2 union select 1 2 构造闭合 union select 1 2 2 大小写绕过 常用于 waf的正则对大小写不敏
  • 搭建和部署nuxt项目

    说在前面的话 vue js开发的SPA是不利于seo的 搜索引擎对它支持的并不是太好 百度根本就不可以在SPA应用的页面抓取数据 这对很看重seo优化的网站来说肯定是不能容忍的 而使用nuxt开发的网站就可以让爬虫爬取 而且它是基于vue
  • 神经网络轮廓特征是什么,神经网络轮廓特征图

    神经网络 的四个基本属性是什么 神经网络 的四个基本属性 1 非线性 非线性是自然界的普遍特征 脑智能是一种非线性现象 人工神经元处于两种不同的激活或抑制状态 它们在数学上是非线性的 由阈值神经元组成的网络具有更好的性能 可以提高网络的容错
  • 游学电子教您:如何给原子的imx6开发板烧录Linux系统

    义县游学电子科技有限公司官方帐号 科技爱好者 今天游学电子带您一起学习下imx6开发板如何烧录系统 使用的开发板是原子的 这里有个注意的地方是我们烧录的系统是到emmc中 而非sd卡中 01 步骤方法 把开发板的启动拨码开关拨到 USB 模
  • FPN、PAN在计算机视觉(CV)领域的意思

    FPN Feature Pyramid Network的首字母缩写 即特征金字塔网络的意思 PAN Pixel Aggregation Network的首字母缩写 即像素聚合网络的意思 名词出处 Path Aggregation Netwo
  • 2022-03-14

    一 你在工作中用到了什么设计模式 怎么用的 1 单例模式 编写kafka共用sdk写入的时候 使用了单例模式 不管new多少次kafkaProducer实例 最终都是一个 采用了静态内部类初始化方式 使用阿里云oss sdk的时候 创建的c
  • 【Git系列】Git下载与安装教程

    Git下载与安装教程 1 下载 2 安装 其他系列 Git最详细的体系化教程 1 下载 官网下载地址 https git scm com downloads 淘宝镜像下载地址 http npm taobao org mirrors git
  • YOLOv2论文理解

    YOLO9000 Better Faster Stronger 论文YOLO9000 Better Faster Stronger的主要内容有三点 1 作者提出了YOLOv2 YOLOv2在YOLOv1的基础上 使用新的网络结构 darkn
  • 西瓜书学习笔记第5章【神经网络】

    西瓜书学习笔记第5章 神经网络 5 1神经元模型 5 2 感知机与多层网络 一 感知机 二 多层功能神经元 多层网络 5 3误差逆传播算法 反向传播 BP 算法 对各个参数更新公式的推导 早停 early stopping 正则化 regu
  • SQL Server修改数据

    本篇主要讲解的是SQL Server 中修改数据的几种语句 INSERT语句 INSERT INTO SELECT语句 UPDATE语句 DELETE语句 一 INSERT语句 INSERT语句向表中添加新行 以下是INSERT语句的最基本
  • 比较IP代理与路由器获取IP地址的三大差异

    在今天的文章中 我们将与大家一起探讨IP代理与路由器获取IP地址的差异 这两种方式在获取IP地址上有一些区别 而这些区别会对我们的网络使用体验产生影响 今天我们深入分析并提供一些实际的例子与操作经验 稳定性差异 通过路由器获取IP地址时 我
  • 字段明明存在,用Web API使用该字段进行查询报错?

    我是微软Dynamics 365 Power Platform方面的工程师罗勇 也是2015年7月到2018年6月连续三年Dynamics CRM Business Solutions方面的微软最有价值专家 Microsoft MVP 欢迎
  • (一)MyBatis

    一 MyBatis特性 1 MyBatis 是支持定制化 SQL 存储过程以及高级映射的优秀的持久层框架 2 MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集 3 MyBatis可以使用简单的XML或注解用于配置
  • Unity——射线检测

    1 new Raw cube0 transform Vector3 forward 射线 第一个参数 射线的起始点 第二参数 射线的方向 myray new Ray this gameObject transform position Ve
  • Flutter系列之Navigator组件使用

    PS 想做一件事很容易 真正去做一件事很困难 同系列文章如下 Flutter系列之Navigator使用详解 Flutter系列之Flex布局详解 Flutter系列之图片加载详解 Flutter系列之Widget生命周期 Flutter系
  • 基于AT指令开发短信程序

    基于AT指令开发短信程序 本人的专职工作是做手机底层软件中SMS和CBS的功能模块软件 对SMS的PDU格式可以说是比较了解 在网上查找了一下感觉目前国内公开的软件大多功能比较单一 主要特点如下 1 支持分页短信 最大可以支持15个分页 可
  • Python利用selenium+Beautifulsoup破解动态class/id并提取相应文本的方法

    最近小白掌柜接了领导一项任务 要全程自动化的注册一个网站并登录网站后逗留一段时间再离开 起初觉得这个应该难度不会太大 就欣然接受了 谁知 拿到具体需求后一分析纳尼 这个里面其实有好多难点 but本着我就是进阶的小白还是决定挑战下去 今天先不
  • 【Python】如何在Python中绘制带有连接线的双饼图?

    文章目录 一 导入所需的库 二 准备数据 三 绘制双饼图 3 1 创建画布和子图对象 3 2 绘制大饼图 3 3 绘制小饼图 3 4 连接线1 连接大饼图的上边缘和小饼图的饼块 3 5 连接线2 连接大饼图的下边缘和小饼图的饼块 3 6 添
  • 强化学习笔记

    强化学习笔记 简介 本文是根据 Sutton的经典书籍 Reinforcement Learning An Introduction 前三章内容整理的笔记 枯燥预警 本文侧重对强化学习概念的理论分析 在基本概念上的剖析较为详细 也就是说会比
  • 深入剖析mmap原理 - 从三个关键问题说起

    作者 招财二师兄 链接 https www jianshu com p eece39beee20 来源 简书 著作权归作者所有 商业转载请联系作者获得授权 非商业转载请注明出处 对于mmap 您是否能从原理上解析以下三个问题 1 mmap比