《操作系统导论》 知识总结 第15章 机制:地址转换

本文介绍了CPU虚拟化中的地址转换技术,特别是受限直接访问(LDE)原理。通过硬件支持的基址加界限机制,实现虚拟内存,确保进程在正确地址执行。操作系统在关键点介入,管理内存,保证安全性。动态重定位利用基址和界限寄存器进行地址转换,硬件自动处理大部分内存访问,仅在异常时操作系统介入。这种方法虽然存在内部碎片问题,但为更复杂的内存管理奠定了基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实现 CPU 虚拟化时,我们遵循的一般准则被称为受限直接访问(Limited Direct Execution,LDE)。即让程序运行的大部分指令直接访问硬件,只在一些关键点(如进程发起系统调用或发生时钟中断)由操作系统介入来确保“在正确时间,正确的地点,做正确的事”。

高效控制是现代操作系统的两个主要目标。

为了高效地实现内存虚拟化,并提供应用程序所需的灵活性,我们利用基于硬件的地址转换技术。通过该技术,硬件对每次内存访问进行处理(即指令获取、数据读取或写入),将指令中的虚拟地址转换为数据实际存储的物理地址。在每次内存引用时,硬件都会进行地址转换,将应用程序的内存引用重定位到物理内存中实际的位置。

当然,仅仅依靠硬件不足以实现虚拟内存,因为它只是提供了底层机制来提高效率。操作系统必须在关键的位置介入,设置好硬件,以便完成正确的地址转换。因此它必须管理内存,记录被占用和空闲的内存位置,并明智而谨慎地介入,保持对内存使用的控制。

假设

我们对内存虚拟化的第一次尝试非常简单,先假设用户的地址空间必须连续地放在物理内存中。为了简单,我们假设地址空间不是很大,即小于物理内存。最后,假设每个地址空间的大小完全一样。在后续章节的讨论中,我们会逐步地放宽这些假设,从而得到现实的内存虚拟化。

一个例子

为了更好地理解地址转换,我们先来看一个例子。假设我们有如下代码,它从内存中加载一个值,对它加3,再将它放回内存。

void func() {
    int x ;
    x = x + 3;
    ...
}

编译器将这段代码转化为汇编语句,可能像下面这样。它假定x的地址已经存入寄存器ebx,之后通过movl指令将这个地址的值加载到通用寄存器eax,下一条指令对eax的内容加3,最后一条指令将eax中的值写回到内存的同一位置。

128: movl 0x0(%ebx), %eax    ;load 0+ebx into eax
132: addl $0x03, %eax        ;add 3 to eax register
135: movl $eax, 0x0(%ebx)    ;store eax back to mem

假设该进程的地址空间如下所示,可以看到代码和数据都位于进程的地址空间,3条指令序列位于地址128,变量x的值位于地址15KB,其初始值为3000。
在这里插入图片描述
如果这3条指令执行,从进程的角度来看,发生了以下几次内存访问:

  • 从地址128获取指令
  • 执行指令(从地址15KB加载数据)
  • 从地址132获取命令
  • 执行命令(没有内存访问)
  • 从地址135获取指令
  • 执行指令(新值存入地址15KB)

从程序的角度来看,它的地址空间从0开始到16KB结束,它包含的所有内存引用都应该在这个范围内。然而,对虚拟内存来说,操作系统希望将这个进程地址空间放在物理内存的其他位置,并不一定从地址0开始。因此我们遇到了如下问题:怎样在内存中重定位这个进程,同时对该进程透明?怎么样提供一种虚拟地址空间从0开始的假象,而实际上地址空间位于另外某个物理地址?下图展示了一个例子,说明这个进程的地址空间被放入物理内存后可能的样子。可以看到,操作系统将第一块物理内存留给了自己,并将上述例子中的进程地址空间重定位到从32KB开始的物理内存地址。剩下的两块内存空闲(16~32KB和48~64KB)。
在这里插入图片描述

动态(基于硬件)重定位

基于硬件的地址转换的第一次应用首次出现在时分机器中,思想很简单,称为基址加界限机制(base and bound),有时又称为动态重定位(dynamic relocation)。

每个 CPU 需要两个硬件寄存器:基址(base)寄存器和界限(bound)寄存器,有时称为限制(limit)寄存器。这组基址和界限寄存器,让我们能够将地址空间放在物理内存的任何位置,同时又能确保进程只能访问自己的地址空间。

采用这种方式,在编写和编译程序时假设地址空间从开始。但是,当程序真正执行时,操作系统会决定其在物理内存中的实际加载地址,并将起始地址记录在基址寄存器中。在上面的例子中,操作系统决定加载在物理地址32KB的进程,因此将基址寄存器设置为这个值。

进程运行时所产生的所有内存引用,都会被处理器通过虚拟地址加上基址的方式转化为物理地址。

physical address = virtual address + base

进程中使用的内存引用都是虚拟地址(virtual address),硬件接下来将虚拟地址加上基址寄存器中的内容,得到物理地址(physical address),再发给内存系统。

举例来说,对于指令

128: movl 0x0(%ebx), %eax

程序计数器首先被设置为128。当硬件需要获取这条指令时,它先将这个值加上基址寄存器中的32KB(32768),得到实际的物理地址32896,然后硬件从这个物理地址获取指令。接下来,处理器开始执行该指令。这时,进程发起从虚拟地址15KB的加载,处理器同样将虚拟地址加上基址寄存器内容(32KB),得到最终的物理地址47KB,从而获得需要的数据。

虚拟地址转换为物理地址,这正是所谓的地址转换(address translation)技术。

界限寄存器为地址空间提供了访问保护。在上面的例子中,界限寄存器被置为16KB。如果进程需要访问超过这个界限或者为负数的虚拟地址,CPU将触发异常,进程最终可能被终止。界限寄存器的用处在于,它确保了进程产生的所有地址都在进程的地址“界限”中。界限寄存器通常有两种使用方式。

  • 在一种方式中(像上面那样),它记录地址空间的大小,硬件在将虚拟地址与基址寄存器内容求和前,就检查这个界限。
  • 另一种方式是界限寄存器中记录地址空间结束的物理地址,硬件在转化虚拟地址到物理地址之后才去检查这个界限。

基址寄存器配合界限寄存器的硬件结构是芯片中的(每个CPU一对)。有时我们将CPU的这个负责地址转换的部分统称为内存管理单元(Memory Management Unit,MMU)。随着我们开发更复杂的内存管理技术,MMU也将有更复杂的电路和功能。

转换示例

为了更好地理解基址加界限的地址转换的详细过程,我们来看一个例子。假设一个进程拥有4KB大小地址空间,它被加载到从16KB开始的物理内存中,一些地址转换结果见下图。
在这里插入图片描述
从例子中可以看到,通过基址加虚拟地址(可以看作是地址空间的偏移量)的方式,很容易得到物理地址。虚拟地址“过大”或者为负数时,会导致异常。

硬件支持:总结

现在来总结一下需要的硬件支持(见下表)。首先,正如在CPU虚拟化的章节中提到的,我们需要两种CPU模式。操作系统在特权模式(或内核模式),可以访问整个机器资源。应用程序在用户模式运行,只能做有限的操作。只要一个位,也许保存在处理器状态字中,就能说明当前的CPU运行模式。在一些特殊的时刻(如系统调用异常中断),CPU会切换状态。
在这里插入图片描述
硬件必须提供基址界限寄存器,因此每个CPU的内存管理单元都需要这两个额外的寄存器。用户程序运行时,硬件会转换每个地址,将用户程序产生的虚拟地址加上基址寄存器的内容。硬件也必须能检查地址是否有用,通过界限寄存器和CPU内的一些电路来实现。硬件也应该提供一些特殊的指令,用于修改基址寄存器和界限寄存器,允许操作系统在切换进程时改变它们。这些指令是特权指令,只有在内核模式下,才能修改这些寄存器。

在用户程序尝试非法访问内存(越界访问)时,CPU必须能够产生异常。在这种情况下,CPU应该阻止用户程序的执行,并安排操作系统的“越界”异常处理程序去处理。操作系统的处理程序会做出正确的响应,比如在这种情况下终止进程。类似地,如果用户程序尝试修改基址或者界限寄存器时,CPU也应该产生异常,并调用“用户模式尝试执行特权指令”的异常处理程序。CPU还必须提供一种方法,来通知它这些处理程序的位置,因此又需要另一些特权指令。

操作系统的问题

此部分建议看书上106-107页内容,比较详细,此处简单总结一下即可
为了支持动态重定位,硬件添加了新的功能,使得操作系统有了一些必须处理的新问题。硬件支持和操作系统管理结合在一起,实现了一个简单的虚拟内存。具体来说,在一些关键的时刻操作系统需要介入,以实现基址和界限方式的虚拟内存,见下表。

在这里插入图片描述

  1. 在进程创建时,操作系统必须采取行动,为进程的地址空间找到内存空间。
  2. 在进程终止时(正常退出,或因行为不端被强制终止),操作系统也必须做一些工作,回收它的所有内存,给其他进程或者操作系统使用。
  3. 上下文切换时,操作系统也必须执行一些额外的操作。
  4. 操作系统必须提供异常处理程序(exception handler),或要一些调用的函数,像上面提到的那样。操作系统在启动时加载这些处理程序(通过特权命)。

下面三幅图按时间线展示了大多数硬件与操作系统的交互。可以看出,操作系统在启动时做了什么,为我们准备好机器,然后在进程(进程A)开始运行时发生了什么。请注意,地址转换过程完全由硬件处理,没有操作系统的介入。在这个时候,发生时钟中断,操作系统切换到进程B运行,它执行了“错误的加载”(对一个非法内存地址),这时操作系统必须介入,终止该进程,清理并释放进程B占用的内存,将它从进程表中移除。从图中可以看出,我们仍然遵循受限直接访问的基本方法,大多数情况下,操作系统正确设置硬件后,就任凭进程直接运行在CPU上,只有进程行为不端时才介入。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

小结

本章通过虚拟内存使用的一种特殊机制,即地址转换(address translation),扩展了受限直接访问的概念。利用地址转换,操作系统可以控制进程的所有内存访问,确保访问在地址空间的界限内。这个技术高效的关键是硬件支持,硬件快速地将所有内存访问操作中的虚拟地址(进程自己看到的内存位置)转换为物理地址(实际位置)。所有的这一切对进程来说都是透明的,进程并不知道自己使用的内存引用已经被重定位,制造了美妙的假象。

我们还看到了一种特殊的虚拟化方式,称为基址加界限的动态重定位。基址加界限的虚拟化方式非常高效,因为只需要很少的硬件逻辑,就可以将虚拟地址和基址寄存器加起来,并检查进程产生的地址没有越界。基址加界限也提供了保护,操作系统和硬件的协作,确保没有进程能够访问其地址空间之外的内容。保护肯定是操作系统最重要的目标之一。没有保护,操作系统不可能控制机器(如果进程可以随意修改内存,它们就可以轻松地做出可怕的事情,比如重写陷阱表并完全接管系统)。

遗憾的是,这个简单的动态重定位技术有效率低下的问题。例如,从图 15.2 (在上面)中可以看到,重定位的进程使用了从 32KB 到 48KB 的物理内存,但由于该进程的栈区和堆区并不很大,导致这块内存区域中大量的空间被浪费。这种浪费通常称为内部碎片(internal fragmentation),指的是已经分配的内存单元内部有未使用的空间(即碎片),造成了浪费。在我们当前的方式中,即使有足够的物理内存容纳更多进程,但我们目前要求将地址空间放在固定大小的槽块中,因此会出现内部碎片。所以,我们需要更复杂的机制,以便更好地利用物理内存,避免内部碎片。第一次尝试是将基址加界限的概念稍稍泛化,得到分段(segmentation)的概念,我们接下来将讨论。

参考

https://www.cnblogs.com/shuo-ouyang/p/12775683.html

https://github.com/2w1nd/os-study/tree/main/%E8%99%9A%E6%8B%9F%E5%8C%96

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值