认识虚拟内存

什么是虚拟内存

虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。目前,大多数操作系统都使用了虚拟内存,如Windows家族的“虚拟内存”;Linux的“交换空间”等。

为什么需要虚拟内存

我们知道程序执行指令的时候,程序计数器是顺序地一条一条指令执行下去,这一条条指令就需要连续地存储在一起,所以就需要这块内存是连续的。物理内存是有限的,如果多个程序同时运行的话,访问同一个物理地址的话,就有可能内存地址冲突,怎么办呢?

这时就需要虚拟内存发挥的作用了,程序里有指令和各种内存地址,系统从物理内存申请一段地址,与这个程序指令里用到的内存地址建立映射关系,这样实际程序指令执行的时候,会通过虚拟内存地址,找到对应的物理内存地址执行。对于任何一个程序来说,它看到的都是同样的内存地址。我们只需要维护一个虚拟内存到物理内存的映射表即可。

这种从物理内存申请一段地址建立映射的方法,我们称其为内存分段

看似解决了上面的问题,但这里又引起了新的问题,即内存碎片。由于物理内存每次申请一段地址的时候,都是连接的,如果有三个程序分别执行,中间的程序执行完后,内存也释放回了可用物理内存,此时又来第四个程序,有可能出现内存不足的情况,因为物理内存已经没有了连续的可用地址,如下图所求,共1G内存的情况。

57211af3053ed621aeb903433c6c10d1

假如程序X需要内存为256MB的话,可以看到两个128MB内存是非连续的,程序无法运行,白白浪费掉了中间的128MB空间,这种情况就是内存碎片

怎么解决呢,只能将Python程序运行信息先存储到硬盘上,先释放最下面的256MB空间,然后再重新载入,这样会腾出256MB的空间。

这种将页面写入磁盘的行为我们称为换出,从磁盘载入内存称为换入。

虽然解决了内存碎片的总是,但由于内存和磁盘两者的速度相差实在太大了,看来这种办法效率太差了,还有其它好的办法没有呢?

内存分页

从上面的介绍可以看出主要问题是因为内存碎片内存交换粒度太大,导致大量的内存交换成本过高,如果我们把这个交换粒度变小一些是不是就好多了呢。

我们将物理内存和虚拟内存都按固定大小进行分页,从虚拟内存到物理内存的映射由原来的段映射调整为了按页映射。

由于内存空间都是预先划分好的,也就没有了不能使用的碎片,而只有被释放出来的很多 4KB 的页。即使内存空间不够,需要让现有的、正在运行的其他程序,通过内存交换释放出一些内存的页出来,一次性写入磁盘的也只有少数的一个页或者几个页,不会花太多时间,让整个机器被内存交换的过程给卡住。

内存映射

上面我们讲过程序运行时,需要将虚拟内存地址即程序指令中用到的地址通过页表来转换成对应的物理内存地址,那么两者之间又是如何实现转换的呢?

每个进程都对应自己的虚拟地址,虚拟地址可分为“虚拟页号”和“偏移量”两部分。

我们的程序看到的内存地址,都是虚拟内存地址。

程序运行时通过三步来实现数据读取

  1. 读取虚拟内存地址的“虚拟页号”值
  2. 在“页表”中根据虚拟页号查找对应的物理地址页号(每个页表,保存每个物理内存页的起始地址)
  3. 将物理内存页基地址+虚拟内存地址的偏移量得到真正的物理内存地址
07cd4c3344690055240f215404a286dd

在加载程序的时候,不再需要一次性都把程序加载到物理内存中。我们完全可以在进行虚拟内存和物理内存的页之间的映射之后,并不真的把页加载到物理内存里,而是只在程序运行中,需要用到对应虚拟内存页里面的指令和数据时,再加载到物理内存里面去。

如果发现尚未为虚拟内存分配物理内存,则申请新的内存,此现象称为“缺页”,等使用完后再释放内存。

对于物理内存,操作系统把它分成一块一块大小相同的页,这样更方便管理,例如有的内存页面长时间不用了,可以暂时写到硬盘上,称为换出。一旦需要的时候,再加载进来,叫作换入。这样可以扩大可用物理内存的大小,提高物理内存的利用率。其中换入与换出是以页为单元,每页大小一般为4KB。

0cf2f08e1ceda473df71189334857cf0

每个进程都有操作系统为自己分配的一块地址连接的虚拟内存,同时也有自己的页表。页表中所有页表项必须提前建好,并且要求是连续的。如果不连续,就没有办法通过虚拟地址里面的页号找到对应的页表项了。

为了节省内存,则使用一种类似于多叉树的“多级页表”数据结构来存储虚拟内存和物理内存的对应关系。

多级页表将虚拟内存的“虚拟页号“分成了4段,从高到低,分成 4 级到 1 级这样 4 个页表索引。

614034116a840ef565feda078d73cb76
5ba17a3ecf3f9ce4a65546de480fcc4e

每级页表包含多个条目,每级条目又都保存了下级条目的地址,按4-1级顺序查找虚拟内存对应的物理页基地址。

参考

https://time.geekbang.org/column/article/95209

https://time.geekbang.org/column/article/110474

https://time.geekbang.org/column/article/95223

https://www.cnblogs.com/zhenbianshu/p/10300769.html