course2610_lab19虚存映射mmap()及读写性能探究(上)

虚拟内存系统通过将虚拟内存分割为称作虚拟页(Virtual Page,VP)大小固定的块,一般情况下,每个虚拟页的大小默认是4096字节。

同样的,物理内存也被分割为物理页(Physical Page,PP),也为4096字节。

背景

在研发分布式日志存储系统,基于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的数据。

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

1. mmap() 的定义与原理

1.0 mmap 的定义

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

在LINUX中我们可以使用mmap用来在进程虚拟内存地址空间中分配地址空间,创建和物理内存的映射关系。
在这里插入图片描述

  • mmap() 系统调用的原型

函数原型:void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
配套函数原型:int munmap(void *addr, size_t length);
头文件:#include <sys/mman.h>
返回值:成功返回创建的映射区的首地址;失败返回宏 MAP_FAILED。

参数介绍:
addr: 建立映射区的首地址,由 Linux 内核指定。使用时,直接传递 NULL。
length: 欲创建映射区的大小。
prot: 映射区权限 PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE。
flags: 标志位参数(常用于设定更新物理区域、设置共享、创建匿名映射区);
MAP_SHARED: 会将映射区所做的操作反映到物理设备(磁盘)上。
MAP_PRIVATE: 映射区所做的修改不会反映到物理设备。
fd: 用来建立映射区的文件描述符。
offset: 映射文件的偏移(4k 的整数倍)。
munmap 函数:同 malloc 函数申请内存空间类似的,mmap 建立的映射区在使用结束后也应调用类似 free 的函数来释放。

返回值:成功:0; 失败:-1

1.1调用 malloc() 时,内核做了什么

当我们在用户程序申请内存时调用 malloc(),内核做了什么,实际上,它负责为进程动态的申请一块内存,操作系统从堆中分配一块内存,并把首地址返回给用户。malloc()申请内存大小不一样,最终调用的系统调用也不一样,当申请的内存大小小于 128kb 时,调用 brk 系统调用,并从堆栈分配内存,当大于 128kb 时调用 mmap,从映射区分配,如图所示,那么,内核是不是立即为进程分配了物理内存?答案是否定的,依然是通过请页机制。
在这里插入图片描述

1.2 映射方式

  • 文件映射
    磁盘文件映射进程的虚拟地址空间,使用文件内容初始化物理内存。
  • 匿名映射
    初始化全为0的内存空间。

而对于映射方式是否共享又分为

  • 私有映射(MAP_PRIVATE)
    多进程间数据共享,修改不反应到磁盘实际文件,是一个copy-on-write(写时复制)的映射方式。

  • 共享映射(MAP_SHARED)
    多进程间数据共享,修改反应到磁盘实际文件中。

因此总结起来有4种组合
1、私有文件映射
多个进程使用同样的物理内存页进行初始化,但是各个进程对内存文件的修改不会共享,也不会反应到物理文件中

2、私有匿名映射
mmap会创建一个新的映射,各个进程不共享,这种使用主要用于分配内存(malloc分配大内存会调用mmap)。

例如开辟新进程时,会为每个进程分配虚拟的地址空间,这些虚拟地址映射的物理内存空间各个进程间读的时候共享,写的时候会copy-on-write。

3、共享文件映射
多个进程通过虚拟内存技术共享同样的物理内存空间,对内存文件的修改会反应到实际物理文件中,他也是进程间通信(IPC)的一种机制。

4、共享匿名映射
这种机制在进行fork的时候不会采用写时复制,父子进程完全共享同样的物理内存页,这也就实现了父子进程通信(IPC).

这里值得注意的是,mmap只是在虚拟内存分配了地址空间,只有在第一次访问虚拟内存的时候才分配物理内存

在mmap之后,并没有将文件内容加载到物理页上,只上在虚拟内存中分配了地址空间。当进程在访问这段地址时,通过查找页表,发现虚拟内存对应的页没有在物理内存中缓存,则产生"缺页",由内核的缺页异常处理程序处理,将文件对应内容,以页为单位(4096)加载到物理内存,注意是只加载缺页,但也会受操作系统一些调度策略影响,加载的比所需的多。

1.3 mmap() 在 read() 和 write 时的运作

  • write
  1. 进程(用户态)将需要写入的数据直接copy到对应的mmap地址(内存copy)
  2. 若mmap地址未对应物理内存,则产生缺页异常,由内核处理
  3. 若已对应,则直接copy到对应的物理内存
  4. 由操作系统调用,将脏页回写到磁盘(通常是异步的)

因为物理内存是有限的,mmap在写入数据超过物理内存时,操作系统会进行页置换,根据淘汰算法,将需要淘汰的页置换成所需的新页,所以mmap对应的内存是可以被淘汰的(若内存页是"脏"的,则操作系统会先将数据回写磁盘再淘汰)。这样,就算mmap的数据远大于物理内存,操作系统也能很好地处理,不会产生功能上的问题。

  • read
    读的过程对比:
    在这里插入图片描述

从图中可以看出,mmap要比普通的read系统调用少了一次copy的过程

因为read调用,进程是无法直接访问kernel space的,所以在read系统调用返回前,内核需要将数据从内核复制到进程指定的buffer。但mmap之后,进程可以直接访问mmap的数据(page cache)。

2. mmap() 与虚拟内存和物理内存

2.1mmap() 在进程虚拟地址空间的 运作机制

我们先来简单看一下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] ls -lat FileA

2147483648 Apr 25 10:22 FileA

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

2.2mmap() 在物理内存的 运作机制

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

同样的,物理内存也被分割为物理页(Physical Page,PP),也为4096字节。

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

缺页处理后,如下图:

在这里插入图片描述

3. mmap() 的分类

个人观点: 从原理上,mmap有两种类型,
一种是有backend,
一种是没有backend。

3.1 有backend

在这里插入图片描述
这种模式将普通文件做memory mapping(非MAP_ANONYMOUS),所以在mmap系统调用时,需要传入文件的fd。

这种模式常见的有两个常用的方式,MAP_SHAREDMAP_PRIVATE,但它们的行为却不相同。

  1. MAP_SHARED

可以从两个角度去看:

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

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

3.2 没有backend

无backend通常是MAP_ANONYMOUS,就是将一个区域映射到一个匿名文件,匿名文件是由内核创建的。

因为没有backend,写入/更新的数据之后,若不主动释放,这些占用的物理内存是不能被释放的,同样会出现OOM Killer。

3.3 mmap比内存+swap空间大情况下,是否有问题

到这里,这个问题就比较好解析了。我们可以将此问题分离为:

  1. 虚拟内存是否会出问题
  2. 物理内存是否会出问题
  • 虚拟内存是否会出问题:
    回到上述的"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,成功后映射后持续写入数据

场景序列映射类型结果
A1MAP_PRIVATEmmap报错
A2MAP_PRIVATE + MAP_NORESERVEmmap成功,在持续写入情况下,遇到OOM Killer
A3MAP_SHAREDmmap成功,在持续写入正常
B4MAP_PRIVATEmmap成功,在持续写入情况下,有一个进程会遇到OOM Killer
B5MAP_PRIVATE + MAP_NORESERVEmmap成功,在持续写入情况下,有一个进程会遇到OOM Killer
B6MAP_SHAREDmmap成功,在持续写入正常

从上述测试可以看出,从现象上看,NORESERVE是绕过mmap的校验,让其可以mmap成功。但其实在RESERVE的情况下(序列4),从测试结果看,也没有保障。

reference
https://www.jianshu.com/p/755338d11865
https://www.jianshu.com/p/eece39beee20
https://www.cnblogs.com/yanlingyin/archive/2012/08/04/2617209.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值