【问题标题】:How are same virtual address for different processes mapped to different physical addresses不同进程的相同虚拟地址如何映射到不同的物理地址
【发布时间】:2011-03-27 00:33:18
【问题描述】:

我已经学习了关于操作系统设计和概念的课程,现在我正在尝试彻底研究 Linux 内核。我有一个我无法摆脱的问题。在现代操作系统中,每个进程都有自己的虚拟地址空间(VAS)(例如,32 位系统中的 0 到 2^32-1)。这提供了许多优点。但在实施中,我在某些方面感到困惑。让我举个例子来解释一下:

假设我们有两个进程 p1, p2; p1 和 p2 有自己的 VAS。一个地址0x023f4a54映射到不同的物理地址(PA),怎么可能呢?这种翻译是怎么做到的。我的意思是我知道翻译机制,但我无法理解当涉及不同进程的地址空间时,相同的地址被映射到不同的物理地址。

0x023f4a54 in p1's VAS => PA 0x12321321
0x023f4a54 in p2's VAS => PA 0x23af2341 # (random addresses)

【问题讨论】:

标签: linux-kernel kernel virtual-address-space


【解决方案1】:

提供虚拟内存的 CPU 允许您设置 CPU 看到的内存地址到物理内存地址的映射,这通常由称为 MMU 的硬件单元完成。

OS 内核可以对 MMU 进行编程,通常不会细化到单个地址,而是以页面为单位(通常为 4096 字节)。这意味着可以对 MMU 进行编程以翻译例如虚拟地址 0x1000-0x2000 被转换为物理地址 0x20000-0x21000。

操作系统为每个进程保留一组这些映射,在调度进程运行之前,它会将该映射加载到 MMU 中,然后再将控制权切换回进程。这为不同的进程启用了不同的映射,并且没有什么能阻止这些映射将相同的虚拟地址映射到不同的物理地址。

就程序而言,这一切都是透明的,它只是在 CPU 上执行指令,并且由于 CPU 已设置为虚拟内存模式(分页模式),因此每次内存访问都由 MMU 进行翻译。在物理总线上输出到内存。

实际的实现细节很复杂,但这里有一些参考资料可能会提供更多见解;

【讨论】:

    【解决方案2】:

    您的问题将虚拟地址与使用地址作为识别方式混淆了,因此理解的第一步是分离概念。

    一个工作示例是 C 运行时库函数 sprintf()。当正确声明和调用时,它会作为共享对象模块与它所需的所有子功能一起被合并到程序中。 sprintf 的地址因程序而异,因为库加载在可用的空闲地址中。对于一个简单的 hello world 程序,sprintf 可能会加载到地址 0x101000。对于计算税收的复杂程序,它可能会在 0x763f8000 处加载(因为主程序包含的所有令人讨厌的逻辑都在它引用的库之前)。从系统的角度来看,共享库仅在一个位置加载到内存中,但每个进程看到该内存的地址窗口(地址范围)对于该可执行文件是唯一的。

    当然,Security Enhanced Linux (SELinux) 的一些特性使这更加复杂,这些特性随机化不同程序部分加载到内存中的地址,包括共享库映射。

    --- 澄清 --- 正如有人正确指出的那样,每个进程的虚拟地址映射是特定于每个进程的,与它的文件描述符集、套接字连接、进程父进程和子进程等没有什么不同。也就是说,p1 可能将地址 0x1000 映射到物理 0x710000 而 p2将地址 0x1000 映射到页面错误,并且 p3 映射到物理 0x9f32a000 处的某个共享库。虚拟地址映射受到操作系统的仔细监督,可以说是为了提供交换和分页等功能,也可以提供共享代码和数据以及进程间共享数据等功能。

    【讨论】:

    • 我的问题不在于分享。问题是,当 p1 尝试到达 VA 0x04 时,此 VA 被转换为物理地址,然后发生上下文切换,另一个进程 p2 尝试到达相同的 VA 0x04,此 VA 被转换为另一个物理地址,但它不能与 p1 相同。操作系统如何提供这种差异。它们可以到达相同的 VA,但这些 VA 未映射到相同的物理地址。我想知道翻译问题之外的机制。操作系统如何防止这些进程与其他进程发生冲突?
    • @Pushdown 如前所述,操作系统为每个进程保留一个单独的映射,并在进程之间切换时交换这些映射。
    【解决方案3】:

    有两种重要的数据结构处理分页:页表和 TLB。操作系统为每个进程维护不同的页表。 TLB 只是页表的缓存。

    现在,不同的 CPU 是不同的。 x86 使用一个名为 CR3 的特殊寄存器直接访问页表,该寄存器指向正在使用的页表。 MIPS 处理器对页表一无所知,因此操作系统必须直接使用 TLB。

    一些 CPU(例如:MIPS)在 TLB 中保留一个标识符以将不同的进程分开,因此操作系统可以在进行上下文切换时更改控制寄存器(除非它需要重用标识符)。其他 CPU 在每次上下文切换中都需要完整的 TLB 刷新。因此,基本上,操作系统需要更改一些控制寄存器,并且可能需要清除 TLB(进行 TLB 刷新)以允许来自不同进程的虚拟地址映射到它们应该映射到的任何物理地址。

    【讨论】:

      【解决方案4】:

      感谢所有答案。我不知道的实际一点是,不同进程的相同虚拟地址如何不会与彼此的物理通信对象发生冲突。我在下面的链接中找到了答案,每个进程都有自己的页表。

      http://tldp.org/LDP/tlk/mm/memory.html

      【讨论】:

        【解决方案5】:

        这个映射(虚拟地址到物理地址)由操作系统和 MMU 处理(参见@nos 的回答);这种抽象的意义在于,p1“认为”它正在访问0x023f4a54,而实际上它正在访问0x12321321

        如果你回到你的课堂上学习程序如何在机器代码级别上工作,p1 会期望一些变量/函数/任何东西在每次加载时都在同一个地方(例如0x023f4a54)。操作系统将物理地址映射到虚拟地址提供了这种抽象。实际上,它并不总是被加载到相同的物理地址,但只要它位于相同的虚拟地址,您的程序并不关心。

        【讨论】:

        • 神奇的 internals 是 MMU(实际上是硬件,而不是操作系统),请参阅 nos 的答案。操作系统确实决定了映射并对 MMU 进行编程以实际实现转换。
        【解决方案6】:

        我认为重要的是要记住每个进程都有自己的一组页表。当我认为整个系统只有一个页表时,我也很难理解这一点。

        当特定进程引用其页表并尝试访问尚未映射到页框的页面时,操作系统会为该特定进程分配不同的物理内存,并将其映射到虚拟地址。

        【讨论】:

          猜你喜欢
          • 2017-05-06
          • 1970-01-01
          • 2012-02-18
          • 2012-10-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多