我参加了一个关于操作系统设计和概念的课程,现在我正在努力学习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)
答案 0 :(得分:22)
提供虚拟内存的CPU允许您在CPU将其视为物理内存地址时设置内存地址的映射,通常由称为MMU的硬件单元完成。
OS内核可以对MMU进行编程,通常不是单个地址,而是以页为单位(通常为4096字节)。这意味着可以对MMU进行编程以进行翻译,例如虚拟地址0x1000-0x2000转换为物理地址0x20000-0x21000。
操作系统为每个进程保留一组这些映射,在调度进程运行之前,它会在将控制权切换回进程之前将该映射加载到MMU中。这为不同的进程启用了不同的映射,并且没有任何东西阻止这些映射将相同的虚拟地址映射到不同的物理地址。
就程序而言,所有这些都是透明的,它只是在CPU上执行指令,并且由于CPU已设置为虚拟内存模式(分页模式),因此MMU会在每次访问内存之前对其进行转换在物理总线上到记忆中。
实际的实现细节很复杂,但是这里有一些可能提供更多见解的参考资料;
答案 1 :(得分:6)
您的问题会将虚拟地址与使用地址作为识别方式混淆,因此理解的第一步是将概念分开。
一个工作示例是C运行时库函数sprintf()
。正确声明和调用后,它将作为共享对象模块以及所需的所有子功能合并到程序中。 sprintf
的地址因程序而异,因为库是在可用的空闲地址中加载的。对于简单的 hello world 程序,sprintf可能会加载到地址0x101000。对于计算税收的复杂程序,可能会加载到0x763f8000(因为主程序包含的所有令人讨厌的逻辑都在它引用的库之前)。从系统的角度来看,共享库仅在一个地方加载到内存中,但每个进程看到该内存对于该可执行文件是唯一的地址窗口(地址范围)。
当然,Security Enhanced Linux (SELinux)的一些功能进一步复杂化,它将不同程序段加载到内存中的地址随机化,包括共享库映射。
---澄清--- 正如有人正确指出的那样,每个进程的虚拟地址映射都是特定于每个进程的,与其文件描述符集,套接字连接,进程父进程和子进程等不同。也就是说,p1可能将地址0x1000映射到物理0x710000而p2将地址0x1000映射到页面错误,并将p3映射到物理0x9f32a000处的某个共享库。虚拟地址映射由操作系统仔细监督,可以说是提供交换和分页等功能,但也提供共享代码和数据以及进程间共享数据等功能。
答案 2 :(得分:5)
有两个处理分页的重要数据结构:页表和TLB。操作系统为每个进程维护不同的页表。 TLB只是页表的缓存。
现在,不同的CPU是不同的。 x86使用一个名为CR3的特殊寄存器直接访问页表,该寄存器指向正在使用的页表。 MIPS处理器对页表没有任何了解,因此操作系统必须直接使用TLB。
某些CPU(例如:MIPS)在TLB中保留一个标识符以将不同的进程分开,因此OS可以在执行上下文切换时更改控制寄存器(除非需要重用标识符)。其他CPU需要在每个上下文切换中进行完整的TLB刷新。因此,基本上,操作系统需要更改一些控制寄存器,并且可能需要清除TLB(执行TLB刷新)以允许来自不同进程的虚拟地址映射到它们应该的任何物理地址。
答案 3 :(得分:2)
感谢所有答案。我不知道的实际观点是,不同进程的相同虚拟地址不会与彼此的物理通信者发生冲突。我在下面的链接中找到了答案,每个进程都有自己的页表。
答案 4 :(得分:1)
这个映射(虚拟地址到物理地址)由OS和MMU处理(参见@nos'的答案);这个抽象的重点是p1“认为”它正在访问0x023f4a54
,而实际上它正在访问0x12321321
。
如果你回到课堂上讨论程序如何在机器代码级别上工作,那么p1每次加载时都会期望某些变量/函数/无论在同一个地方(例如0x023f4a54
)。 OS映射物理到虚拟地址提供了这种抽象。实际上,它并不总是被加载到相同的物理地址,但只要它位于相同的虚拟地址中,您的程序就不会关心。
答案 5 :(得分:0)
我认为请务必记住,每个进程都有自己的页表集。当我想到整个系统只有一个页面表时,我也很难理解这一点。
当特定进程引用其页表并尝试访问尚未映射到页框架的页面时,OS为该特定进程分配另一块物理内存,并将其映射到虚拟地址。