x86 PM 架构上的交换、分页、分段和虚拟内存

x86 PM 架构上的交换、分页、分段和虚拟内存

好吧,这似乎是一个常见的或已经被问过的问题,但是在搜索了各种书籍、在线教程甚至在 SU 上之后,我仍然对这四个野兽如何在 x86 保护模式系统上协同工作感到困惑。

讨论这些事情时可以使用什么正确的术语?

据我所知,这 4 个概念完全不同,但在讨论保护内存时却有关联。这就是我搞混的地方!

我首先从交换开始。

交换:

进程必须在物理内存中才能执行。进程可以暂时从物理内存交换到后备存储器,然后再返回到内存中继续执行。

这特别适用于需要同时执行多个进程的多任务环境,因此需要实现一个 CPU 调度程序来决定将哪个进程交换到备用存储。

分页:又称简单分页:

假设一个进程使用/访问的所有地址都在 0 到 16MB 的范围内。我们可以称之为进程的逻辑地址空间,因为地址是由该过程生成的。

请注意,根据此定义,一个进程的逻辑地址空间可以与另一个进程的逻辑地址空间不同,因为进程可能更大或更小。

现在我们将进程的逻辑地址空间划分为相同大小的块,称为页面. 还将物理内存划分为固定大小的块,称为框架

按照定义,逻辑地址 = 页码:该页的偏移量

当一个进程被 CPU 调度程序选中执行时,它的页面会从后备存储加载到任何可用的记忆框架。

注意全部在调度程序将控制权移交给此进程之前,属于此进程的页面的其中一部分已加载到内存中。当此进程要交换到后备存储时,全部其页面均存储在后备存储器中。

后备存储器被分成与物理内存帧大小相同的固定大小的块。

这简化了交换过程,因为我们交换的是页面而不是字节。这减少了后备存储的碎片,因为我们不需要为某些字节寻找空间,而是查看是否有空间可用于页面。

由于我们将一个页面保留在内存中,分页技术还减少了物理内存的碎片。

主存储器必须有足够的空间全部属于某个进程的页面,以便将该进程加载到内存中执行。如果内存中只有几个页面可供此进程使用,则其他进程(即全部必须将属于进程的所有页面(例如,属于进程的页面)交换到备用存储,然后只需将要执行的进程的所有页面加载到内存中。

因此,分页技术比简单的交换具有更好的性能。

因此,交换允许我们运行多个进程而无需购买太多内存,相反,我们可以使用少量的内存(这个数量必须是这样的全部在 PC 上运行的最大程序/进程的页面可以加载到内存中 - 即,您必须在运行程序之前知道它需要多少内存。)加上额外的备用存储器(通常是磁盘),其成本非常低,但容量比主存储器大得多。

所以交换+分页允许有效管理内存,以便可以在系统上运行多个进程。

请求分页:

但系统中安装的物理内存不一定与进程的要求相同。而且需要运行多个进程。

解决方案是加载仅限流程的某些页面进入内存,当进程访问不在内存中的页面地址时,就会产生页面错误,操作系统会加载该页面一经请求 这样进程就可以继续执行。这样就节省了在将控制权移交给该进程之前加载该进程所有页面的时间——就像分页 + 交换的情况一样。

这种将进程的一部分保存在内存中,其余部分保存在磁盘等后备存储器中的技术称为请求调页

因此按需调页 = 分页 + 交换 + 仅将进程的某些页面(而非全部)保留在内存中。


这就是我所知道的有关分页和交换的全部内容。如果上面有些地方我理解有误,请随时纠正我。

现在我的问题是:

  1. 虚拟内存和虚拟地址空间(又名线性地址空间)术语与 x86 保护模式下的请求分页究竟有何关系。

  2. “进程的虚拟内存”是正确的术语吗?虚拟内存的定义是什么?全部多任务系统中当前正在运行的进程?

  3. 我是否正确:进程可用的虚拟内存==进程的虚拟地址空间(又名线性地址空间)中的最高地址+ 1?

  4. 这是关于分段的:在 x86 保护模式下,我们被告知每个进程可以拥有 4GB 的虚拟地址空间(VAS),这意味着由于 x86 架构上存在分段,我们可以划分该 VAS分成两个或更多个段。 在 x86 扁平模型中,我们在进程的 VAS 中创建段,它们都完全重叠,因此实际上分段被禁用 - 没有段。但是,如果在某个进程的 VAS 中的虚拟地址上存在一些 CPU 指令,那么我们有可能在分配内存(在此 VAS 中)或创建变量或数组时覆盖这些指令。我们如何确保不会发生这种情况。描述符中的保护位不会区分区域,因为在平面模式下所有段都重叠。这些位只能防止读取代码或执行数据,而且这也只是因为这些段是通过选择器访问的。

  5. 或者说,每个部分都被视为自己的 VAS。但在这种情况下进程可用的总虚拟内存(或总 VAS)在平面模式下,则为:“属于进程的段数 x 单个段的虚拟内存”。对于 x86 保护模式,这将转换为 6 x 4GB = 24GB 的 VAS!假设 CS、DS、ES、GS、FS、SS 寄存器指向 6 个段。这是正确的吗?

  6. 支持简单分页(而非按需分页)但不支持虚拟内存的环境如何确保平面内存模型中各个段之间的保护?这里我们有两种情况 - 单任务系统和多任务系统。

更新:2012-07-29

如果我理解正确的话:

虚拟内存是一种概念是的实施的在 x86 架构上经过使用请求分页技术+一些保护位(特别是U位和W位)。

换句话说,一个过程的 VAS 是分成几页,然后用于需求分页。

虚拟内存机制基本两种用途在多任务环境中:

  1. 程序的大小可能超出其可用的物理内存量。操作系统将程序当前正在使用的部分保存在主内存中,其余部分保存在磁盘上。这是通过请求分页实现的,每个页面在其页表条目中都有一个关联的“存在位”和“访问位”。

  2. 通过为每个进程提供自己的虚拟地址空间来提供内存保护,因此一个进程无法访问其他进程的 VAS。这是通过设置一些保护位来实现的与每个页面相关联具体来说,页表项中的‘用户/管理员位-U位’、读/写位‘W位’用于页面访问保护

虚拟内存在以下方面很有用:两个都单任务系统和多任务系统。对于单任务系统,仅有的使用#1是相关的。

页面访问保护有2个方面特权级保护写保护。它们分别由 U 位(用于权限)和 W 位(用于写入)实现。这些位存在于该页的页表条目中。

内存保护有两个方面:保护程序不互相访问,并保护程序不覆盖自身,以防该过程/程序的 VAS 中出现段重叠。

现在以前的问题可以通过 VAS 或虚拟内存概念来解决,但是后者怎么样

页面访问保护方案没有据我所知,可以防止后者。换句话说,虚拟内存技术不能防止程序覆盖自身,以防进程的 VAS 中的段重叠。

但在我看来甚至段级保护 无法防止后者(覆盖自身)的内存保护问题。

x86处理器总是评估段级保护执行页面级保护检查 - 无论是平面模型还是多段模型 - 因为无法在 x86 CPU 上禁用分段。

考虑一个平面模型场景:

考虑由 CS:off 引用的虚拟地址。现在 DS:off 也将引用相同的虚拟地址参照 CS:off,如果两种情况下的“off”值完全相同。SS:off 也是如此。

这也意味着虚拟/线性地址位于,由于它不知道分段,所以被分页单元简单地视为一个页面。

假设平面模式下程序的所有段都属于同一特权级别,例如 ring0。

现在,如果我们尝试在 CS:off = DS:off = SS:off 时写入或执行数据,会发生什么情况。

假设该地址不属于进程的 VAS 中映射的 OS 代码 - 为了简单起见,请将 OS 放在一边,我说的是硬件级保护!

首先,将通过段级保护,然后在访问此页面(包含 CS:off 或 DS:off 或 SS:off 的页面)时将通过特权级别检查,因为所有段都属于同等特权这里,但是这个页面的 W 位呢?应该将其设置为 1 以允许写入,否则数据段将无法在其页面上执行写入。所以这意味着这个页面也是可写的。

这意味着我们可以在这个虚拟(线性)地址读取/写入/执行数据:CS:off = DS:off = SS:off。?

我不明白 x86 硬件如何能针对此问题提供保护以防片段重叠。

答案1

好吧,我承认有很多术语和令人困惑的措辞,但我会尽力回答。据我所知,你的大部分理解都是正确的,但有些地方需要重申一下。

从硬件角度理解分页和虚拟内存的工作原理非常重要。如果没有硬件支持,分页将是不切实际的,因为进程必须与内存布局无关,操作系统也不应该使用软件来照看系统上的每个进程。这就是内存管理单元 (MMU) 的作用所在。该单元基本上由操作系统编程,用于在虚拟地址空间中排列页面,并且可以由操作系统随意控制。操作系统可以告诉该单元哪些页面实际上在物理 RAM 中,哪些页面尚未加载或被换出。

那么,我们如何防止程序干扰内存管理呢?我们称之为保护。我们可以将进程置于沙盒中,这样它们就不会干扰操作系统和其他进程。为什么所有这些术语被放在一起令人困惑,因为它们确实是相互关联的。代码所具有的权限由页表指定。页表告诉 MMU 虚拟空间的布局方式,还告诉 MMU 页面是否 A) 存在 B) 是读/写 C) 被允许执行代码以及 D) 所述页面上的代码可以执行什么特权级别(环)。

当调度程序调度进程时,不会重新创建页表,也不需要安排新的内存,操作系统只是告诉 MMU 使用不同的页表,这是一个 O(1) 进程,换句话说,不依赖于进程的大小或它使用的内存量。整个进程很少一次换入或换出内存,通常每次只换入或换出页面,因此术语“交换”通常被澄清为“页面交换”。

好的,有了这些背景知识,我将尝试回答您的每个问题:

  1. 线性地址空间只是意味着您可以访问从 0 到 2^32 的内容。无需像 16 位处理器时代那样进行复杂的分段。虚拟内存只是意味着进程的线性地址空间不是由主内存定义的,而是由操作系统定义的,这意味着操作系统可以在这个地址空间中任意安排页面,例如将自身置于高级别,将进程置于低级别。此外,处理器可以指定此虚拟地址空间的哪些部分可以通过哪些权限访问。操作系统(内核)加载到每个虚拟地址空间中,以便进程可以执行系统调用,并且在被抢占时有地方可去。但是,它们无法读取或写入此区域,因为操作系统将其标记为“仅限特权代码”。它们只能通过处理器中的系统调用机制(即软件中断)访问它。请求分页只是意味着进程期望该虚拟地址空间的某些部分具有特定内容(可能是文件,甚至是其自身的一部分),但实际上并不存在,操作系统已在页表中将该区域标记为“不存在”。当进程最终访问该区域时,由于不存在,CPU 会抛出一个错误,该错误会被操作系统捕获。然后操作系统会非常聪明地加载该页面并从中断处重新启动进程。结果是进程甚至不知道出现问题,并且只会在需要时加载内容,从而节省内存。

  2. 虚拟内存是整个机制的名称,该机制指定页表及其保护,以及可能位于磁盘等其他介质上的页面,因此称为分页。虚拟内存可能是您标题的统称,也许除了分段之外。当提到特定进程时,我个人会使用“进程的虚拟地址空间”之类的词,因为它明确地指的是特定进程的虚拟内存布局。

  3. 不。正如我之前提到的,操作系统可以任意将实际内存映射到进程虚拟地址空间中的任何位置。这意味着,例如,它可能会出现这样的情况:进程代码位于地址 0x0,但堆(向下增长)从 0xFFFFFFF 开始,在地址空间的另一侧清除。由于设备驱动程序需要特定的硬件地址区域,因此可能实际上对映射位置存在限制,但为了理解虚拟内存,没有限制。

  4. 分段只是一种寻址方案。在 286 中,它也被用作实现保护的机制,但事实证明这种机制过于不灵活,因此在 32 位处理器中,保护始终通过分页完成(尽管据我所知,286 保护方案在分页禁用时保留)。由于保护是由分页机制定义的,因此分段不会比平面内存模式更容易或更不容易覆盖数据。对于大多数可执行文件格式,代码段与数据段明显分开。由于我们可以预期代码永远不会改变,操作系统通常会将代码段的页面标记为只读,因此任何写入代码的尝试都会导致错误并退出程序。如果在现代操作系统中所有变量和数组都通过堆栈或堆分配,则永远不会发生这种情况。但是,如果程序开始在堆栈之外四处探索,它将在能够覆盖任何代码之前崩溃。更大的风险(曾经是一个大问题)是您的堆栈在缓冲区溢出时被覆盖。某些人可以利用这一点将代码放入堆栈,然后导致未经授权执行代码。作为修复,在页表“No eXecute”(NX) 位中放置了一个新位。这可以防止页面被执行。

  5. 这根本不是事实。段只是充当指向原始 2^32 字节地址空间区域(段)的指针。其背后的想法最初是让指针保持较小,因为您可以拥有一个段指针和一个小于整个地址空间的段内指针。例如,在 286(16 位处理器)中,将指针保持在 16 位是有意义的,但这带来了一个问题,因为 286 可以寻址 2^24 字节的内存。解决方案?使用分段。一个段可以是 2^16 字节大,它们可以指向地址空间中的任何位置。然后,当代码必须运行时,它将仅在该段内使用 16 位指针。这更快、更高效。当 32 位处理器问世时,这种机制不再是必要的,但由于之前的代码大量使用它,程序员也习惯了它们,所以他们保留了分段。较新的 64 位处理器根本不使用分段。

  6. 令人困惑的是,虚拟内存是许多不同机制的术语。多任务处理需要虚拟内存,以保护一个进程免受另一个进程地址空间的影响。分页以及扩展的抢占式多任务处理只有使用虚拟内存功能才有可能。但是,许多这些功能都可以有效地禁用。也许您不想要地址转换?那么将每个页面映射到其自身。也许您不想要内存保护但想要地址转换?那么将所有权限授予每个页面。在 DOS 和其他单处理器系统中,当提到“保护模式”时会产生混淆。通常这是指 32 位模式,而不是 16 位实模式,因此尽管名称如此,但它并不一定意味着使用保护,只是在该模式下可以启用保护。可能有许多单进程系统在这种“保护模式”下运行,但不使用虚拟内存或保护。原始 Xbox 就是一个很好的例子。禁用所有这些功能后,性能可能会略有提高。但是,在 DOS 中,使用其中许多功能可能仍然有利。最值得注意的是页面交换,因为在 DOS 无处不在的早期,RAM 很难获得,因此任何节省 RAM 的机制都受到欢迎并得到广泛使用。保护在单进程系统中也有其优势,因为它可以防止程序以丑陋的方式崩溃,允许更好的调试,并防止由于硬件访问不当而导致数据损坏。

我希望这回答了你的问题。

相关内容