今日阅读《深入理解操作系统》,回顾了一下虚拟空间和mmap的原理知识,又有了新的角度去认识它们,对于它们原理的理解更具现了。

1. OS存储的金字塔4~6层结构

如上图所示,CPU在执行任何操作时,涉及到的数据都要从寄存器中直接读取,所以寄存器速度是最快的,相应的,SRAM速度仅次于寄存器,而存储容量递增,即L1比L0容量大,L2比L1容量大。。。。。。
换句话说,上层的存储器作为下层存储器的缓存而工作的,明白这一层概念,就会得到一种新的角度去看待主存,我们一直觉得主存就是一个很快的非持久化存储器,通常用来缓存一些磁盘的数据或者将要写入磁盘的脏数据,甚至是根本不会持久化的临时数据,因为这样的作用导致我们惯性思维认为主存就是一个OS在运行时的高速存储罢了,很多programer也会着重于提高SRAM的命中。
然而,在新的角度去看待主存时,可以发现,主存与磁盘的关系并没有想象中那么离得远,反而是很紧密的,这一点在UNIX当中尤为突出,众所周知,UNIX是一个以文件系统为根向上建立起来的OS,他们(UNIX作者们)对文件很“执着”。在主存的设计中,一个程序运行伊始,主存对磁盘的缓存工作就已经开始,CPU要读取程序的机器指令集(这是程序文件(或者说可执行文件)的概念),才能开始一一执行,那么就需要将磁盘上的这个文件从最底层(目前不考虑L6的远程存储)二级存储将数据一步步复制到寄存器中,此时,主存就会缓存住这个可执行文件,另外的,可执行文件的链接库之类的也会缓存。
接下来,我要阐述一下主存工作中让我们产生误会的那一块工作,就是不会持久化的临时数据放在主存中,为何能说主存时作为磁盘的缓存?UNIX给我们了答案,在UNIX的设计中,通过malloc等手段申请的内存区域,对应的会在虚拟文件系统中创建backup文件,这个文件匿名,并不是真正的文件,但它拥有真实文件的基本元信息例如文件描述符等。
由此,我们可以更好地理解主存是磁盘的缓存。

2. 从虚拟空间到物理内存

虚拟地址的虚拟页与物理页相映射但并非一一映射,虚拟页通常(大部分OS)比物理页多,即虚拟存储器比物理存储器大,如32位(2GB)OS运行在512MB的主存机器上,或者64位(8EB)运行在512GB主存机器上。虚拟空间的页有三种状态:

  • 已缓存:已将磁盘数据缓存在主存上,此时数据可以是匿名文件映射,匿名文件数据为0,而物理页或许被写入了脏数据,这不影响,后面会介绍。
  • 为缓存:虚拟页与文件建立了映射,但是还未缓存到主存上,所以主存所有物理页都不包含这段数据。
  • 未分配:虚拟页未分配即并没有与文件建立映射。

上图中,PTE(页表条目)0~7为当前页表,虚拟存储器VP(虚拟页)1,2,3,4,6,7为已分配的页(已分配的内存),VP1,2,4,7已缓存,所以物理页已经有数据,VP0,5还未分配,VP3,6分配了但还未缓存。每个PTE有一个标志位,标志此页已缓存还是为缓存(或未分配)。
虚拟地址在进程访问内存时的指针存储,虚拟地址通过MMU转换得到物理地,转换过程之前的文章也提到了页式存储、段式存储、段页式存储等,访问的物理地址所在的内存页未命中则发生缺页,此时:

  1. 经过一系列处理(包括Linux验证页是否为合法页,以及规划物理页的空间等等前置操作)完成准备
  2. 有空闲页则会将已分配的页数据拷贝到物理页上
  3. 无空闲页则会根据一些淘汰策略选中牺牲页,将数据拷贝到牺牲页位置
    1. 如果牺牲页为脏数据,则会触发flush(匿名文件则释放数据)
    2. Linux还会有一个计时任务负责定时将脏页刷盘
  4. 最终更新页表

    3. mmap的性能因素

    mmap可以更明显地看出就是虚拟存储器的一部分功能,将匿名文件换成了磁盘的真实文件,根据上一节的虚拟地址通过页表访问物理页,以及通过缺页读取磁盘数据的原理,我们可以分析出mmap的一些机制。

  5. mmap可以映射(或者通过参数申请)比主存还大的文件吗?

答:可以。
上一节可以发现,主存只存的下4个页,而虚拟空间有6页,甚至还能再开更多的页,通过缺页、换页来访问超出主存大小限制的数据。

  1. mmap速度的影响因素
    • 显而易见,换页,频繁换页会使得速度大大降低,由于Linux比较可靠(不绝对可靠)淘汰机制,信息的局部性原理等,可以在一定程度上保证换页速率。
    • 局部性所指的频繁访问活动页面称之为工作集或者驻集,但是如果驻集大小都超过了(或者很接近)主存大小,那么局部性也救不了频繁换页,这种情况称之为颠簸(thrashing)
  2. 关于mmap与传统IO的速度差

按传统观念认为,由于mmap的“一次复制”,所以快于传统IO,但现今的机器,已经不是这样了。目前仍存在这样一个规律:小文件IO快,大文件mmap快。原因并非一次复制,而是缓存大小,mmap会尽可能缓存不超过主存大小的更多的数据,而IO只会缓存很少量的数据。那为什么“一次复制”不再是mmap的有点了呢?因为如今的主存设备(如ddr4内存条),吞吐率(速度)已经非常大了。