虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。 ——维基百科
为什么要使用虚拟内存
- 安全:如何用户程序可以直接访问物理内存,会造成安全问题。虚拟内存技术使得同时驻留在内存的多个用户进程相互之间不会发生干扰,也不会访问操作系统所占有的空间。
- 扩大程序的地址空间:利用虚拟存储技术,从逻辑上对内存空间进行扩充,从而可以使用户在较小的内存里运行较大的程序。好像每个程序都拥有足够大的私有的物理内存。
- 为用户提供方便:逻辑地址和物理地址分开,用户只在各自的逻辑地址空间编写程序,不必关心物理地址的细节,地址的转换由底层硬件和操作系统自动完成。
1. 分段
将程序分为逻辑段,比如代码、堆和栈。在分段的情况才,虚拟地址分为两部分(段号,段内偏移量)。每个段都有一堆用于计算真实物理地址的东东(基址,界限)。在地址转换时,先根据“段号”确定属于哪个段,然后根据该段的(基址,界限)计算物理地址。段内偏移量+基址=物理地址,而界限的作用是防止地址越界。分段的地址转换是由寄存器硬件实现的,因此很快。
- 优点:地址转换开销小;因为是按代码逻辑分段,所以支持代码共享
- 缺点:会产生外部碎片,虽然可以紧凑内存,但是这个过程需要大量的拷贝工作,所以会占用大量的CPU时间。此外,如果有一个很大但是很稀疏的堆,都在一个逻辑段中,整个堆仍然必须完整地加载到内存,势必会浪费内存。
2. 分页
将程序的地址空间分割成固定大小的单元,每个单元称为一页。相应地,物理内存也分为同样大小的页。在基于分页的虚拟内存中,地址转换的关键就是,找到虚拟页对应的物理帧。为此,需要建立一个页表,这个页表记录了虚拟页号和物理帧号的映射关系。虚拟地址分为两部分(虚拟页号,页内偏移量),在页表中根据虚拟页号找到相应的物理帧号,然后将原虚拟地址中的虚拟页号替换为物理帧号,即得到物理地址。
但是分页有一个缺点,它的页表可能很大,有多大呢?来看一下
页表可以变得非常大,比我们之前讨论过的小段表或基址/界限对要大得多。例如,想象一个典型的32位地址空间,带有4KB的页。这个虚拟地址分成20位的VPN和12位的偏移量。一个20位的VPN意味着,操作系统必须为每个进程管理个地址转换(大约一百万)。假设每个页表项需要4个字节,来保存物理地址转换和任何其他有用的东西,每个页表就需要巨大的4MB内存!这非常大。现在想象一下有100个进程在运行:这意味着操作系统会需要400MB内存,只是为了所有这些地址转换!
就是因为页表太大了,所以无法放在像寄存器这样的快速硬件上,只能放在内存中,这就意味着,访问页表会带来一次额外的访存。这会导致地址转换很慢。
至此,分页显现出了两个缺点
- 地址转换太慢
- 页表占用内存太大
3. 优化分页——加速地址转换
通过引入TLB来加速地址转换,TLB叫做地址转换旁路缓冲存储器(translation-lookaside buffer)。但更多是被叫做页表缓存或块表。对每次内存访问,硬件先检查TLB,看看其中是否有期望的转换映射,如果有,就完成转换(很快),不用访问页表(其中有全部的转换映射)。TLB带来了巨大的性能提升。TLB和其他缓存相似,前提是在一般情况下,转换映射会在缓存中(即命中)。如果是这样,只增加了很少的开销,因为TLB处理器核心附近,设计的访问速度很快。如果TLB未命中,就会带来很大的分页开销。必须访问页表来查找转换映射,导致一次额外的内存引用。如果这经常发生,程序的运行就会显著变慢。相对于大多数CPU指令,内存访问开销很大,TLB未命中导致更多内存访问。因此,我们希望尽可能避免TLB未命中。
4. 优化分页——解决页表太大的问题
多级页表的本质是去掉页表中的无效区域,而不是将它们全部保留在内存中。多级页表的基本思想很简单。首先,将页表分成页大小的单元。然后,如果页表中某个页的所有页表项都无效,那么这页表的这一页就不放在内存中。为了追踪页表的页是否有效(以及如果有效,它在内存中的位置),使用了名为页目录的新结构。页目录因此可以告诉你页表的页在哪里,或者页表的整个页不包含有效页。
应该指出,多级页表是有成本的。在TLB未命中时,需要从内存加载两次,才能从页表中获取正确的地址转换信息(一次用于页目录,另一次用于页表项本身)。因此,多级页表是一个时空折中的例子。我们想要更小的表(并得到了),但不是没代价。尽管在常见情况下(TLB命中),性能显然是相同的,但TLB未命中时,则会因较小的表而导致较高的成本。另一个明显的缺点是复杂性。无论是硬件还是操作系统来处理页表查找(在TLB未命中时),这样做无疑都比简单的线性页表查找更复杂。通常我们愿意增加复杂性以提高性能或降低管理费用。在多级表的情况下,为了节省宝贵的内存,我们使页表查找更加复杂。
二级页表可以不创建,也可以创建了但是放在磁盘中,需要时再加载进内存。