虚拟内存

463 阅读15分钟

一、分页存储

  • 将内存划分成为一个个大小相等的分区,每一个分区都是一个内存块,称之为页面,如同数组一样,每一个分区都由一个 页号

image.png

1、地址结构:

image.png

  • 页内偏移量:12位,可以表示 2^12 个字节, 相当于 4K,可以表示到 4KB内部的每一个字节
  • 页号 :20位数, 说明可以表示 2^20 个页面

2、页表

  • 操作系统为每一个进程创建了一张页表,父进程 fork 子进程的时候,复制的就是这个页表。

image.png

3、快表

  • 当不存在快表时候的访存过程
    • 页号 = 逻辑地址 / 页面长度
    • 偏移量 = 逻辑地址 % 页面长度
    • 找到当场线程对应的页表(第一次访存),加载到CPU里面
    • 通过页表,查询到页号,偏移量,得到地址,访问内存的实际地址(第二次访存)
  • 当存在快表时候的访存
    • 页号 = 逻辑地址 / 页面长度
    • 偏移量 = 逻辑地址 % 页面长度
    • 快表存在 寄存器中,不需要访存
      • 如果命中,直接访问该地址
      • 如果不命中,继续访问页表--》访问主存(2次访存) image.png

image.png

4、二级页表

  • 假设是32位的计算机,采用32位的逻辑地址,采用分页管理,页面大小是 4KB
  • 4KB = 2^12 B 所以如果需要表示每一个字节,偏移量需要 12 位,32位的计算机,寻址长度就是32位,所以 前面的 页号是 20位,所以进程最多可以占用 2^20 个页号 2^20 = 1M
  • 一个 页号的大小是 32位,所以存储 1M 个 页号需要 1M * 4B = 2 ^ 22 B ,
  • 一个 页面可以提供 4KB 个存储单元 所以 2 ^ 22 / 2 ^ 12 = 2^10个页框 等于 1024 个页框,还得是连续的才能存储,才能实现随机访问。

  • 页面大小是 4KB ,页号的大小是 4B ,所以一个页面可以存放 1024 个页号,
  • 对于一个进程来说,支持查找的范围是 2的32次方,一个页面大小是 4KB,所以需要需要有 2的20次方个页号, 除以 一个页面只可以存放1024个,所以需要连续的1024个内存块才可以存放。如果内存的大小是 4GB = 4 的 30 次方块。

  • 由于局部性原理,进程再一段时间内大概率只会访问某几个页面,所以没必要将所有的页面都放在内存中
  • 以此,一次性占用连续内存块有点浪费,

基于上述原因,产生了二级页表

image.png

这样子,页表就不需要连续存放了,同时也可以将部分页表转移到外存,节省内存空间。

  • 几个小细节注意下
    • 采用多级页表,各级的页表大小不能超过一个页面,
    • 采用多级页表,需要多次访存,一级页表去寻找二级页表的内存块号,二级页表的内存快好去寻找下一级的

案例:

image.png

二、分段存储

分段存储是二维的,分页是一维的

image.png

1、逻辑地址结构

image.png

2、段表

image.png

3、地址计算

image.png

  • 换算成为(2,1024),说明段号是 2 段内地址是1024
  • 2 的 基址地址是40kB,加上1024B,就是需要访问的地址 image.png

比较分页和分段

image.png

  • 分页地址是一维的:只需要给出一个逻辑地址,逻辑地址/4B 就是页号,逻辑地址%4B 就是偏移量

  • 分段地址是二维的,需要给出段号,再给出段内的偏移量

  • 分页的大小是固定的 4B

  • 分段的大小是不固定的,

  • 页是信息的物理单位,分页仅仅是有系统决定的

  • 端是信息的逻辑单位,一个端通常包含了一个逻辑块的信息

三、段页存储

  • 先按照逻辑进行分段,再将各个页进行分页。 image.png

image.png

  • 和多级页表有点相似,同样需要三次访存

四、虚拟内存

  • 虚拟内存为每个进程提供了一个一致的、私有的地址空间,它让每个进程产生了一种自己在独享主存的错觉(每个进程拥有一片连续完整的内存空间)

  • 理解不深刻的人会认为虚拟内存只是“使用硬盘空间来扩展内存“的技术,这是不对的。

  • 虚拟内存的重要意义是它定义了一个连续的虚拟地址空间,使得程序的编写难度降低。

  • 并且,把内存扩展到硬盘空间只是使用虚拟内存的必然结果,虚拟内存空间会存在硬盘中,并且会被内存缓存(按需),有的操作系统还会在内存不够的情况下,将某一进程的内存全部放入硬盘空间中,并在切换到该进程时再从硬盘读取(这也是为什么Windows会经常假死的原因...)。

  • 功能:

  • 它把主存看作为一个存储在硬盘上的虚拟地址空间的高速缓存,并且只在主存中缓存活动区域(按需缓存)。

  • 它为每个进程提供了一个一致的地址空间,从而降低了程序员对内存管理的复杂性。

  • 它还保护了每个进程的地址空间不会被其他进程破坏。

1、 CPU寻址

  • 物理寻址:内存可以比作一个字节大小相等的数组,每一个数组里面的位置都有一个唯一的地址,作为数组的索引,CPU访问内存可以字节通过物理地址进行访问

  • 虚拟寻址:CPU需要虚拟地址翻译成为物理地址,而后访问真实的物理内存

image.png

  • 通过 MMU(内存管理单元),将虚拟地址转为物理地址,其中 MMU需要借助存放再 内存中的页表,查询虚拟地址和物理地址的对应关系。

2、 页表

页表:一个存放在物理内存中的数据结构,它记录了虚拟页与物理页的映射关系

  • 虚拟内存: 空间被组织为一个存放在硬盘上的M个连续的字节大小的单元组成的数组,每个字节都有一个唯一的虚拟地址,作为到数组的索引(这点其实与物理内存是一样的)。

操作系统通过将虚拟内存分割为大小固定的块来作为硬盘和内存之间的传输单位,这个块被称为虚拟页(Virtual Page, VP),每个虚拟页的大小为P=2^p字节。物理内存也会按照这种方法分割为物理页(Physical Page, PP),大小也为P字节

CPU在获得虚拟地址之后,需要通过MMU将虚拟地址翻译为物理地址。而在翻译的过程中还需要借助页表,所谓页表就是一个存放在物理内存中的数据结构,它记录了虚拟页与物理页的映射关系。

页表是一个元素为页表条目(Page Table Entry, PTE)的集合,每个虚拟页在页表中一个固定偏移量的位置上都有一个PTE。下面是PTE仅含有一个有效位标记的页表结构,该有效位代表这个虚拟页是否被缓存在物理内存中。

image.png

虚拟页VP 0VP 4VP 6VP 7被缓存在物理内存中,虚拟页VP 2VP 5被分配在页表中,但并没有缓存在物理内存,虚拟页VP 1VP 3还没有被分配。

在进行动态内存分配时,例如malloc()函数或者其他高级语言中的new关键字,操作系统会在硬盘中创建或申请一段虚拟内存空间,并更新到页表(分配一个PTE,使该PTE指向硬盘上这个新创建的虚拟页)

由于CPU每次进行地址翻译的时候都需要经过PTE,所以如果想控制内存系统的访问,可以在PTE上添加一些额外的许可位(例如读写权限、内核权限等) ,这样只要有指令违反了这些许可条件,CPU就会触发一个一般保护故障,将控制权传递给内核中的异常处理程序。一般这种异常被称为“段错误(Segmentation Fault)”。

页命中

image.png

  • 如上图所示,MMU根据虚拟地址在页表中寻址到了PTE 4,该PTE的有效位为1,代表该虚拟页已经被缓存在物理内存中了,最终MMU得到了PTE中的物理内存地址(指向PP 1)。

缺页

image.png

  • 如上图所示,MMU根据虚拟地址在页表中寻址到了PTE 2,该PTE的有效位为0,代表该虚拟页并没有被缓存在物理内存中。虚拟页没有被缓存在物理内存中(缓存未命中)被称为缺页。

image.png

image.png

  • CPU 遇见缺页,触发缺页异常,将控制权转向操作系统内核,通过内核调用缺页异常处理程序进行处理,这时候CPU会再内存中选择一个页面。

    • 如果内存中存在 空闲块,则为该进程分配一个空闲块,所缺页面放入当前块,修改页表对应的页表项信息
    • 如果内存中不存在空闲块,则需要由页面置换算法选择页面进行淘汰,如果页面是修改过,则需要将其写回外存,如果页面没有修改过,则不需要
  • 当缺页异常处理程序返回时,它会重新启动导致缺页的指令,该指令会把导致缺页的虚拟地址重新发送给MMU。由于现在已经成功处理了缺页异常,所以最终结果是页命中,并得到物理地址。

  • 这种在硬盘和内存之间传送页的行为称为页面调度(paging):页从硬盘换入内存和从内存换出到硬盘。当缺页异常发生时,才将页面换入到内存的策略称为按需页面调度(demand paging),所有现代操作系统基本都使用的是按需页面调度的策略。

  • 虚拟内存跟CPU高速缓存(或其他使用缓存的技术)一样依赖于局部性原则。虽然处理缺页消耗的性能很多(毕竟还是要从硬盘中读取),而且程序在运行过程中引用的不同虚拟页的总数可能会超出物理内存的大小,但是局部性原则保证了在任意时刻,程序将趋向于在一个较小的活动页面(active page)集合上工作,这个集合被称为工作集(working set) 。根据空间局部性原则(一个被访问过的内存地址以及其周边的内存地址都会有很大几率被再次访问)与时间局部性原则(一个被访问过的内存地址在之后会有很大几率被再次访问),只要将工作集缓存在物理内存中,接下来的地址翻译请求很大几率都在其中,从而减少了额外的硬盘流量。

  • 如果一个程序没有良好的局部性,将会使工作集的大小不断膨胀,直至超过物理内存的大小,这时程序会产生一种叫做抖动(thrashing)的状态,页面会不断地换入换出,如此多次的读写硬盘开销,性能自然会十分“恐怖”。所以,想要编写出性能高效的程序,首先要保证程序的时间局部性与空间局部性。

image.png

五、Linux中的虚拟内存系统

  • Linux为每个进程维护了一个单独的虚拟地址空间。虚拟地址空间分为内核空间与用户空间,用户空间包括代码、数据、堆、共享库以及栈,内核空间包括内核中的代码和数据结构,内核空间的某些区域被映射到所有进程共享的物理页面。
  • Linux也将一组连续的虚拟页面(大小等于内存总量)映射到相应的一组连续的物理页面,这种做法为内核提供了一种便利的方法来访问物理内存中任何特定的位置。

image.png

  • Linux将虚拟内存组织成一些区域(也称为段)的集合,区域的概念允许虚拟地址空间有间隙。一个区域就是已经存在着的已分配的虚拟内存的连续片(chunk) 。例如,代码段、数据段、堆、共享库段,以及用户栈都属于不同的区域,每个存在的虚拟页都保存在某个区域中,而不属于任何区域的虚拟页是不存在的,也不能被进程所引用。

内核为系统中的每个进程维护一个单独的任务结构(task_struct)。任务结构中的元素包含或者指向内核运行该进程所需的所有信息(PID、指向用户栈的指针、可执行目标文件的名字、程序计数器等)。

image.png

  • mm_struct:描述了虚拟内存的当前状态。pgd指向一级页表的基址(当内核运行这个进程时,pgd会被存放在CR3控制寄存器,也就是页表基址寄存器中),mmap指向一个vm_area_structs的链表,其中每个vm_area_structs都描述了当前虚拟地址空间的一个区域。

  • vm_starts:指向这个区域的起始处。

  • vm_end:指向这个区域的结束处。

  • vm_prot:描述这个区域内包含的所有页的读写许可权限。

  • vm_flags:描述这个区域内的页面是与其他进程共享的,还是这个进程私有的以及一些其他信息。

  • vm_next:指向链表的下一个区域结构。

1、内存映射技术

  • Linux通过将一个虚拟内存区域与一个硬盘上的文件关联起来,以初始化这个虚拟内存区域的内容,这个过程称为内存映射(memory mapping)。这种将虚拟内存系统集成到文件系统的方法可以简单而高效地把程序和数据加载到内存中。

  • 一个区域可以映射到一个普通硬盘文件的连续部分,例如一个可执行目标文件。文件区(section)被分成页大小的片,每一片包含一个虚拟页的初始内容。由于按需页面调度的策略,这些虚拟页面没有实际交换进入物理内存,直到CPU引用的虚拟地址在该区域的范围内。如果区域比文件区要大,那么就用零来填充这个区域的余下部分。

  • 一个区域也可以映射到一个匿名文件,匿名文件是由内核创建的,包含的全是二进制零。当CPU第一次引用这样一个区域内的虚拟页面时,内核就在物理内存中找到一个合适的牺牲页面,如果该页面被修改过,就先将它写回到硬盘,之后用二进制零覆盖牺牲页并更新页表,将这个页面标记为已缓存在内存中的。

  • 简单的来说:

    • 普通文件映射就是将一个文件与一块内存建立起映射关系,对该文件进行IO操作可以绕过内核直接在用户态完成(用户态在该虚拟地址区域读写就相当于读写这个文件)。

    • 匿名文件映射一般在用户空间需要分配一段内存来存放数据时,由内核创建匿名文件并与内存进行映射,之后用户态就可以通过操作这段虚拟地址来操作内存了。匿名文件映射最熟悉的应用场景就是动态内存分配(malloc()函数)。

共享对象

  • 内存映射提供了共享对象的机制,来避免内存资源的浪费。一个对象被映射到虚拟内存的一个区域,要么是作为共享对象,要么是作为私有对象的。
  • 对于共享对象的修改,会触发写时复制技术

image.png

  • 一个典型的例子就是fork()函数,该函数用于创建子进程。当fork()函数被当前进程调用时,内核会为新进程创建各种必要的数据结构,并分配给它一个唯一的PID。为了给新进程创建虚拟内存,它复制了当前进程的mm_structvm_area_struct和页表的原样副本。并将两个进程的每个页面都标为只读,两个进程中的每个区域都标记为私有区域(写时复制)。

  • 父进程和子进程的虚拟内存空间完全一致,只有当这两个进程中的任一个进行写操作时,再使用写时复制来保证每个进程的虚拟地址空间私有的抽象概念。

参考