内存虚拟化-机制:地址转换
2023-1-10
| 2023-8-2
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
在实现CPU 虚拟化时,遵循受限直接访问,让程序运行的大部分指令直接访问硬件,只在一些关键点(如进程发起系统调用或发生时钟中断)由操作系统介入来确保”在正确时间,正确的地点,做正确的事”。
 
在实现虚拟内存时,追求类似的战略,在实现高效和控制的同时,提供期望的虚拟化。
  • 高效决定了要利用硬件的支持
  • 控制意味着操作系统要确保应用程序只能访问它自己的内存空间,要保护应用程序不会相互影响,也不会影响操作系统
  • 最后,对虚拟内存还有一点要求,灵活性,希望程序能以任何方式访问它自己的地址空间,从而让系统更容易编程
 
 
如何实现高效的内存虚拟化?如何提供应用程序所需的灵活性?如何保持控制应用程序可访问的内存位置,从而确保应用程序的内存访问受到合理的限制?
利用了一种通用技术,有时被称为基于硬件的地址转换(hardware-based address translation),简称为地址转换。它可以看成是受限直接执行这种一般方法的补充。利用地址转换,硬件对每次内存访问进行处理(即指令获取、数据读取或写入),将指令中的虚拟(virtual)地址转换为数据实际存储的物理(physical)地址。在每次内存引用时,硬件都会进行地址转换,将应用程序的内存引用重定位到内存中实际的位置。
仅仅依靠硬件不足以实现虚拟内存,因为它只是提供了底层机制来提高效率。操作系统必须在关键的位置介入,设置好硬件,以便完成正确的地址转换。因此它必须管理内存(manage memory),记录被占用和空闲的内存位置,并明智而谨慎地介入,保持对内存使用的控制。
 
所有这些工作都是为了创造一种美丽的假象:每个程序都拥有私有的内存,那里存放着它自己的代码和数据。虚拟现实的背后是丑陋的物理事实:许多程序其实是在同一时间共享着内存,就像CPU(或多个CPU)在不同的程序间切换运行。通过虚拟化,操作系统(在硬件的帮助下)将丑陋的机器现实转化成一种有用的、强大的、易于使用的抽象。
 
 
假设
  • 用户的地址空间必须连续地放在物理内存中
  • 地址空间不是很大,小于物理内存的大小
  • 每个地址空间的大小完全一样
 
 
从内存中加载一个值,对它加3,然后将它存回内存:
编译器将这行代码转化为汇编语句:
假定x的地址已经存入寄存器ebx,之后通过movl 指令将这个地址的值加载到通用寄存器eax(长字移动)。下一条指令对eax 的内容加3。最后一条指令将eax 中的值写回到内存的同一位置。
                 进程及其地址空间
进程及其地址空间
代码和数据都位于进程的地址空间,3 条指令序列位于地址128(靠近头部的代码段),变量x的值位于地址15KB(在靠近底部的栈中)。如图所示,x的初始值是3000。
 
如果这3 条指令执行,从进程的角度来看,发生了以下几次内存访问:
  1. 从地址128 获取指令
  1. 执行指令(从地址15KB 加载数据)
  1. 从地址132 获取命令
  1. 执行命令(没有内存访问)
  1. 从地址135 获取指令
  1. 执行指令(新值存入地址15KB)
从程序的角度来看,它的地址空间(address space)从0 开始到16KB 结束。它包含的所有内存引用都应该在这个范围内。然而,对虚拟内存来说,操作系统希望将这个进程地址空间放在物理内存的其他位置,并不一定从地址0 开始。因此,怎样在内存中重定位这个进程,同时对该进程透明(transparent)?怎么样提供一种虚拟地址空间从0 开始的假象,而实际上地址空间位于另外某个物理地址?
下图展示了一个例子,说明这个进程的地址空间被放入物理内存后可能的样子。操作系统将第一块物理内存留给了自己,并将上述进程地址空间重定位到从32KB 开始的物理内存地址。剩下的两块内存空闲(16~32KB 和48~64KB)
                              物理内存和单个重定位的进程
物理内存和单个重定位的进程
 
 

静态(基于软件)重定位

早期,在硬件支持重定位之前,一些系统曾采用纯软件的重定位方式。其中一个名为加载程序(loader)的软件接手将要运行的可执行程序,将它的地址重写到物理内存中期望的偏移位置。
例如,程序中有一条指令是从地址1000 加载到寄存器(即movl 1000,%eax),当整个程序的地址空间被加载到从3000(不是程序认为的0)开始的物理地址中,加载程序会重写指令中的地址(即movl 4000, %eax),从而完成简单的静态重定位。
然而,静态重定位有许多问题,首先也是最重要的是不提供访问保护,进程中的错误地址可能导致对其他进程或操作系统内存的非法访问,一般来说,需要硬件支持来实现真正的访问保护。静态重定位的另一个缺点是一旦完成,稍后很难将内存空间重定位到其他位置。
 
 

动态(基于硬件)重定位

在20世纪50年代后期,它在首次出现的时分机器中引入,那时只是一个简单的思想,称为基址加界限机制(base and bound),有时又称为动态重定位(dynamic relocation)。
每个CPU 需要两个硬件寄存器:基址(base)寄存器界限(bound)寄存器(限制寄存器)。这组基址和界限寄存器,让我们能够将地址空间放在物理内存的任何位置,同时又能确保进程只能访问自己的地址空间。
编写和编译程序时假设地址空间从零开始,当程序真正执行时,操作系统会决定其在物理内存中的实际加载地址,并将起始地址记录在基址寄存器中。在上面的例子中,操作系统决定加载在物理地址32KB 的进程,因此将基址寄存器设置为这个值。进程产生的所有内存引用,都会被处理器通过以下方式转换为物理地址:
 
界限寄存器提供了访问保护。在上面的例子中,界限寄存器被置为16KB。如果进程需要访问超过这个界限或者为负数的虚拟地址,CPU 将触发异常,进程最终可能被终止。界限寄存器的用处在于,它确保了进程产生的所有地址都在进程的地址“界限”中。
 
这种基址寄存器配合界限寄存器的硬件结构是芯片中的(每个CPU 一对)。有时将CPU 的这个负责地址转换的部分统称为内存管理单元(Memory Management Unit,MMU)
 
硬件支持
notion image
 
操作系统的要求
notion image
 
 
大多数硬件与操作系统的交互
notion image
notion image
 
遗憾的是,这个简单的动态重定位技术有效率低下的问题。例如,重定位的进程使用了从32KB 到48KB 的物理内存,但由于该进程的栈区和堆区并不很大,导致这块内存区域中大量的空间被浪费。这种浪费通常称为内部碎片(internal fragmentation),指的是已经分配的内存单元内部有未使用的空间(即碎片),造成了浪费。在我们当前的方式中,即使有足够的物理内存容纳更多进程,但我们目前要求将地址空间放在固定大小的槽块中,因此会出现内部碎片。所以,需要更复杂的机制,以便更好地利用物理内存,避免内部碎片。
 
  • 计算机基础
  • 操作系统
  • 内存虚拟化-地址空间内存虚拟化-分段
    目录