1

假设我有一个 prog1.c,它是作为 prog1.out 构建的。在 prog1.out 中有一个链接器信息,它将告诉精灵将被加载到哪里。这些地址将是一个虚拟地址。加载程序将查找这些信息并将其作为一个进程启动。像 DS,BSS 这样的每个部分都将加载到链接器中提到的虚拟地址上。例如,我有 prog2.out 也有相同的加载器地址、BSS、DS 等,那么它会冲突吗?我知道它不会发生冲突,但是会出现性能问题。由于两个进程具有相同的虚拟地址,但它们映射到不同的物理地址?我很困惑,它如何保护具有相同虚拟地址的两个进程。

4

3 回答 3

5

问题是当一个进程使用内存地址时,它正在谈论一个虚拟地址,它可能与相同的物理地址不同。这意味着两个进程可以引用相同的地址并且不会混合它们的数据,因为它将位于两个不同的物理位置。

下面我将描述虚拟地址如何在典型计算机上转换为物理地址(这在其他架构上略有不同,但想法相同)

了解 Intel x86 架构上的内存转换过程(3 级分页)

因此,一方面您有一个虚拟内存地址,并且您想获得一个物理内存地址(即:RAM 上的实际地址),工作流程主要是这样的:

虚拟地址 -> [分段单元] -> [分页单元] -> 物理地址

每个操作系统都可以定义分段单元和分页的工作方式。例如,Linux 使用Flat Segmentation Model,这意味着它被忽略了,所以我们现在也这样做。

现在,我们的虚拟地址通过称为分页单元的东西,并以某种方式转换为物理地址。这就是如何。

内存被分成一定大小的块,在英特尔上这个大小可能是 4KB 或 4MB。

每个进程在内存中定义了一组表,因此计算机知道它应该如何转换内存地址。这些表以分层方式组织,实际上,您要访问的内存地址在这些表的索引中分解。

我知道,这听起来令人困惑,但请跟我多说几句话。您可以使用此图像关注我的写作:

x86 32 位分页(来自维基百科)

有一个称为CR3的内部 CPU 寄存器,它存储第一个表的基地址(我们将此表称为页目录,它的每个条目称为页目录条目)。当一个进程被执行时,它的CR3被加载(除其他外)。

所以,现在你想访问,比如说,内存地址0x00C30404

分页单元说“Ok,让我们获取页面目录基址”,查看 CR3 寄存器并知道页面目录的基址在哪里,我们将这个地址称为 PDB(Page Directory Base)。

现在你想知道你应该使用哪个目录条目。正如我之前所说,地址被分解成一堆索引。最高有效的 10 位(位 22 到 31),对应于页面目录的索引。在这种情况下,0x00C30404在二进制中是 0000 0000 1100 0011 0000 0100 0000 0100,其最高有效 10 位是: 0000 0000 110x3。这意味着我们要寻找第 3 页的目录条目。

我们现在干什么?

请记住,这些表是分层的:每个Page Directory Entry都有下一个表的地址,称为Page Table(对于每个Page Directory Entry,此表可能不同)。

所以现在,我们得到了另一个表。地址的下 10 位将告诉我们要访问该表的哪个索引(我们称它们为页表条目)。

00 0011 0000 是接下来的 10 位,它们是数字:0x30。这意味着我们必须访问第 30页表条目..

最后,此页表条目保存所需页帧的偏移量(请记住,内存被划分为 4k 块)。最后,我们地址的最低有效 12 位是此PAGE FRAME的内存偏移量,请注意PAGE FRAME是实际的物理内存地址。

这称为 3 级分页,在 64 位(或使用 PAE)上非常相似,但还有一层分页。

你可能认为仅仅为了获取一个变量而获得所有这些内存访问是一件很糟糕的事情。这是真的。计算机中有一些机制可以避免所有这些步骤,其中一个是 TLB(表后备缓冲区),它存储了所有已完成翻译的缓存,因此它可以轻松获取内存。

此外,这些结构的每个条目都有一些关于权限的属性,例如“此页面可写吗?” “这个页面可执行吗?”。

所以,既然您了解了内存分页的工作原理,就很容易掌握 Linux 是如何处理内存的:

  • 每个进程都有自己的 CR3,当进程计划运行时,会加载 cr3 寄存器。
  • 每个进程都有自己的虚拟空间(表可能未满,一个进程可以从例如只有一个 4kb 的页面开始)。
  • 每个进程都映射了一些其他页面(操作系统内存),因此当它被中断时,中断处理程序会在同一任务上启动并处理该任务中的中断,执行所需的代码。

这种计划的一些动机

  • 您不必同时拥有所有进程的内存,您可以将一些存储在磁盘中。
  • 您可以安全地隔离每个进程内存,并给它一些权限。
  • 一个进程可能使用 10MB 的内存,但它们不需要在物理内存中是连续的。
于 2013-09-19T04:57:56.530 回答
1

虚拟地址和物理地址(在普通机器中)之间没有关系。映射总是发生 - 这是虚拟地址首先用于的一部分。

“性能问题”一直存在——如果有的话——隐藏在硬件中。是否可能存在实际冲突(如果这些地址是物理地址)。

它如何保护具有相同虚拟地址的两个进程。

你不需要,也不需要。每个进程的内存内容独立于任何其他进程,由操作系统和硬件来确保这一点。您可能会将术语“虚拟内存”视为“特定于进程的自身内存”。

请注意,所有这些讨论实际上与目标代码的内容或链接/加载过程几乎没有关系——您可以在运行时引用任何地址,而链接器/加载器不需要知道它。

于 2013-09-18T07:03:35.070 回答
1

每个进程“认为”它在计算机上单独运行并且只知道它的虚拟地址。然后内存管理单元 (MMU) 将虚拟地址转换为物理地址。MMU 当然必须确保两个虚拟地址没有映射到同一个物理地址。映射速度非常快,因为 CPU(大多数时候)对 MMU 有专用的硬件支持。

MMU 还必须弄清楚从哪里获取数据,例如主内存(可能是典型情况)、1 级缓存、2 级缓存、磁盘中的交换文件等。正如我所说,该过程本身并没有注意到也就是说,它“认为”它适用于一个平面内存文件,而不必担心任何冲突。

于 2013-09-18T07:11:27.303 回答