虚拟内存和 MMU:何时使用,何时不使用?

虚拟内存和 MMU:何时使用,何时不使用?

我想知道当需要在地址空间中维护一些“固定”地址时,虚拟内存系统如何与 MMU 一起管理。

例如,当机器启动时,CPU 开始从固定地址(映射到某种 ROM)读取第一条指令,然后将地址提供给外围设备(如果使用内存映射 I/O 系统),然后启动操作系统。我还知道中断例程和类似的东西需要位于内存中的“固定”位置,这些东西由操作系统加载。

我可能认为 MMU 在这样的过程中被禁用,然后在操作系统加载后启用它。

我可能认为上述过程使用系统地址空间,并且系统地址空间并未虚拟化,尽管用户地址空间实际上是虚拟化的。

这将产生一个保持不变的物理地址池,以便访问 I/O 外设、中断例程等,以及由 MMU 管理的虚拟用户空间,其中进程可以在其中阐述它们需要阐述的所有数据,并要求操作系统访问 I/O 外设。

但我不确定这些事情。所以我问你,MMU 什么时候真正启用?它处理所有地址,还是只处理用户空间的地址?即使在系统运行时,某些地址是否确实可以绕过 MMU,以访问固定的内存位置?还是我遗漏了一些重要的线索?

答案1

抱歉,但所选答案中的推测具有误导性,并且忽略了最重要的方面,即通过页表进行地址转换。

确实,任何兼容 PC 的机器启动时都会以“实模式”开始。而现代 x86 上的 32 位操作系统确实以“保护模式”运行,其中包括由 GDT 定义的分段寻址。然而,它们还启用了基于页表的地址翻译通过设置 CR0 中的 PG(分页)位(第 31 位)。在操作系统的整个生命周期中,此功能永远不会关闭。

此外,在大多数现代 32 位操作系统中,基于 GDT 的分段寻址基本上被绕过:所有常用的 GDTE 都设置为基地址 0,大小为 40 亿字节。因此,尽管 MMU 确实会将相关段“基地址”添加到来自指令的“位移”,但这实际上是无操作。环 0 和环 3 使用不同的 GDTE 集,但它们都具有相同的基地址和大小。“特权级别”字段(0 和 3)几乎完全不同。这使得页表条目中的“特权访问”位有效,允许逐页保护内存以进行内核模式或用户+内核模式访问。段描述符无法做到这一点;它们的粒度太粗了。

在 x64 CPU 中,在长模式下,分段机制基本上消失了。当然,只要设置了 PG 位,基于页表的地址转换仍然会发生,而 PG 位在操作系统的整个生命周期中都是如此。在内核模式下,MMU 绝对不会被禁用,“SO”(或任何东西)也不会在虚拟地址和物理地址之间使用 1:1 映射。

访问已知物理地址(例如分配给 PCI 类外围设备的地址)是通过分配未使用的虚拟地址并使用所需的物理页码设置相应的页表条目来完成的。然后,设备驱动程序中的代码将使用虚拟地址。

是的,DMA 主要作用于物理地址。一个愚蠢/廉价的 DMA 控制器实际上只是将数据传输到具有给定起始地址和长度的物理连续缓冲区。为了支持此类设备,操作系统或设备驱动程序将分配物理连续的“反弹缓冲区”供 DMA 设备访问,并在这些缓冲区和用户缓冲区之间复制数据。

智能/更昂贵的 DMA 控制器可以处理占用不连续物理地址范围的缓冲区(称为“分散-集中映射”)。这些是高性能设备的首选。

IOMMU 可让愚蠢/廉价的 DMA 控制器访问物理上不连续的缓冲区,就像它是连续的一样。但是,具有 IOMMU 的平台还不够普及,不足以说“您的平台必须为我们的操作系统配备 IOMMU”。因此,目前 IOMMU 主要由虚拟机监视器使用。

答案2

x86 CPU 以“实模式”启动 - 基本上是 16 位模式,其中 CPU 只能看到 RAM 的前 1MB。BIOS 引导加载程序(或 UEFI 可以直接执行此操作)的首要任务之一是将 CPU 切换到“受保护”模式。受保护的内存在此模式下可用,并且 CPU 在此模式下具有特权级别 - 通常是“内核”和“用户”。

我对此有点模糊,但 MMU 如何映射内存是由全局描述符表 (GDT) 控制的。内核模式可以更改 GDT,而用户模式则不能。

因此,当进入内核模式时,它可以将 GDT 设置为内存映射,该映射要么标识映射所有内存(即,表现得像根本没有映射一样),要么以允许其访问所有设备的方式进行映射,等等。当它返回到用户模式时,它可以在交还控制权之前加载更具限制性的 GDT。

我可能错了——可能是当 CPU 进入内核模式时它只是禁用 MMU,但我相信它也可以以这种方式被内核模式使用。

相关内容