虚拟内存在操作系统里一直是一个很重要的概念,之前学过的都差不多忘记了在这里记录一下。首先要理解虚拟这个词的含义。即在应用程序运行中看到的地址并不是真正的物理地址,而是经过操作系统经过映射后的虚拟地址。那为什么要做呢?主要有以下几个好处。
虚拟地址的意义
(1)给每个应用程序提供了虚拟的独立的地址空间,程序员不需要关注内存空间的地址结构,简化了链接和加载的过程(2)提供了安全性保证,在地址转换的时候可以做安全性检查,防止越权访问。比如这个机制使得一个用户态的程序试图访问内核态的地址空间数据的时候MMU在映射时会检测并阻止操作(3)提供了一定的共享机制。虽然虚拟内存使得程序隔离开来,但是我们可以使得不同的虚拟地址映射到同一块物理地址实现共享。
现在我们需要了解一下这个映射的方式。现在的操作系统是使用的带有多级页表的段页式映射。
分段
首先介绍一下什么是分段。在一个程序运行的时候可以分为数段比如代码段,堆段,栈段等等。分段本质就是把不同段映射到不同的物理地址,因为这些段关联性较小没有必要使用连续的地址空间。因此分段就诞生了,他本质是一个逻辑分段,分段在使用的时候需要指定段号和偏移量。在进行映射的时候使用哪一个段,再加上后面的偏移量就可以得到真正的物理地址。每一个段的大小不一定一样。
分页
分页将物理地址分为了大小一致的帧(frame),并使用页表(page table)来存储每一个frame的起始位置。在将虚拟地址翻译成物理地址的过程中先用高位作为虚拟页号查询页表中的物理帧的地址再使用低位作为物理帧中的偏移量来获得真实的物理地址。与分段不同的是虚拟页和物理帧都是定长的。
页表在使用的过程中存在着一些问题,具体原因有这两个。1.增加了访问内存的次数(因为使用了页表,增加了访问内存的次数)--> 解决方案是引入缓存TLB。页表也是需要存储的,占用了额外的内存。由于这些段表页表都是针对一个进程存在的,如果进程很多仅仅用来存储页表项的空间就要使用很大一部分空间。 --> 使用多级页表方案解决。
TLB
TLB本质就是缓存了从页号到物理帧号的映射关系。如果TLB命中则不需要查询页表直接获得帧号。如果没有命中则需要更新TLB中的缓存项。少了一次查询页表的访问内存流程因此会更快。至于TLB缓存的淘汰机制留在下次记录。
多级页表
首先要知道多级页表并不会扩大寻址的空间,它存在的意义是提供更细粒度的控制减少页表存储的空间。具体操作就是在使用多级页表的时候如果某一项没有对应的地址映射则后面更低级的就不再存储。有效的节省了页表占用的空间。下面用图来展示具体的流程。首先先看线性的页表。
在线性页表里即使不存在相关的项线性页表也需要存储导致空间的浪费。而在多级页表中再第一级页表中不存在的项相应的二级页表就不存在了,这就是多级页表节省页表大小的方式。多级页表的第一级页表一般是驻留在内存里的。
多级页表的计算问题
在使用页寻址机制的时候规定了逻辑的虚拟页表和实际的帧大小一致,正是这个限制了寻址空间的大小。首先确定物理帧的大小决定了虚拟页的大小,虚拟页的大小又限制了页表里能够存储的页表的项的大小。多级页表本质是一个树的结构。如果页太小的话单级页表并不能并不能寻址到足够多的空间。涉及到一些计算问题在此记录一下。
首先要会算寻址空间的大小。
比如一个页表4KB每一个PTE 4B如果使用单级页表能够寻址的空间为多少? 首先我们可以知道一个页表存储了1K个页表项。每一个页表项可以表示4K的空间。则寻址空间为1K * 4K = 4M。那二级页表呢?不论多少级,页表的大小是一样的。那这样第一级页表也可以存储1K的页表项,而在一级页表中的页表项可以寻址4M,那么二级页表就可以寻址1K * 4M = 4G的地址空间了。
如果知道了页表大小和地址空间又应该如何计算需要多少级页表呢?
比如一个64的操作系统,页表大小为4K,每一个PTE为4B的话需要多少级页表能够覆盖所有的地址空间?
首先还是可以知道一个页表可以存储1K个记录。那我们需要多少个页表记录呢?首先由于物理帧和页大小是一样的。如果页表有4K,那么每一个物理帧也是4K。我么知道PTE存的是frame的开始地址具体要到哪个地址需要offset。那这样就需要有4K也就是2^12作为offset位。剩下的还有2^64/2^12 = 2 ^ 52。这个数据就是我们需要映射的页表项的数目了。我们一级页表可以映射1K也就是2^10个项,每加一级页表寻址空间都会扩大1K倍。6 * 10 > 52。所以我们需要6级页表才能覆盖所有的地址空间。