物理/逻辑/虚拟内存地址之间的差异

时间:2013-04-06 12:56:02

标签: linux-kernel operating-system virtual-memory

我对操作系统中的物理/逻辑/虚拟地址这个术语感到有点困惑(我使用Linux-open SU​​SE)

以下是我的理解:

  1. 物理地址 - 当处理器处于系统模式时,处理器使用的地址是物理地址。

  2. 逻辑地址 - 当处理器处于用户模式时,使用的地址是逻辑地址。通过添加带有偏移值的基址寄存器,这些都可以映射到某个物理地址。它在某种程度上提供了一种内存保护。

  3. 我遇到过讨论,虚拟和逻辑地址/地址空间是相同的。这是真的吗?

  4. 非常感谢任何帮助。

9 个答案:

答案 0 :(得分:43)

对于在现代Linux系统上运行的英特尔CPU,我的答案是正确的,我说的是用户级进程,而不是内核代码。不过,我认为它会给你足够的洞察力来考虑其他可能性

地址类型

关于问题3:

  

我遇到了虚拟和逻辑的讨论   地址/地址空间是相同的。这是真的吗?

据我所知,它们是相同的,至少在现代操作系统中运行在英特尔处理器之上。

在我解释更多之前,让我尝试定义两个概念:

  • 物理地址:物理位于RAM芯片中的地址。
  • 逻辑/虚拟地址:程序用于访问其内容的地址。它通常由硬件芯片转换为物理地址(大多数情况下,甚至CPU都不知道这种转换)。

虚拟/逻辑地址

虚拟地址很好,一个虚拟地址,操作系统和一个叫做MMU(内存管理单元)的硬件电路使你的程序无法在系统中运行,它有整个地址空间(有32位)系统意味着你的程序会认为它有4 GB的RAM;粗略地说。)

显然,如果你当时有多个程序运行(你总是这样做,GUI,初始化程序,Shell,时钟应用程序,日历等等),这将无效。

操作系统会将大部分程序内存放在硬盘中,你使用最多的部分会出现在RAM中但是嘿,这并不意味着他们会拥有你知道的地址

示例:您的进程可能有一个名为(counter)的变量,该变量给定虚拟地址0xff(可以想象...)和另一个名为(oftenNotUsed)的变量给定虚拟地址(0xaa)。

如果在所有链接发生后读取已编译代码的程序集,您将使用这些地址访问它们,但是,(常常未使用的)变量将不会真正存在于0xaa的RAM中,它将在硬盘,因为你没有使用它。

此外,变量(计数器)可能不会在物理上处于(0xff),它会在RAM中的其他地方,当你的CPU试图获取0xff中的内容,MMU和操作系统的一部分时,做一个映射并从RAM中真正可用的位置获取该变量,你甚至不会注意到它不在0xff中。

如果您的程序要求(通常不使用)变量,会发生什么? MMU +操作系统会注意到这个“未命中”并将它从硬盘中取出并送到RAM中,然后将它交给你,就好像它在地址(0xaa)中一样;这个提取意味着RAM中存在的一些数据将被发送回硬盘。

现在想象一下这个系统中的每个进程都在运行。每个人都认为他们有4GB的RAM,没有人真的拥有它,但一切正常,因为每个人都有他们的程序的一些部分在RAM中物理可用,但大多数程序驻留在HardDisk中。不要将放入HD的程序存储器的这一部分与您可以通过文件操作访问的程序数据混淆。

摘要

虚拟地址:您在程序中使用的地址,即CPU用于获取数据的地址,不是真实的,并通过MMU转换为某个物理地址;每个人都有一个,其大小取决于您的系统(Linux运行32位有4GB地址空间)

实际地址:如果您在操作系统上运行,则无法访问该地址。这是您的数据,无论其虚拟地址如何,都驻留在RAM中。如果您的数据来回发送到硬盘以便为其他进程提供更多空间,则会发生这种情况。

我上面提到的所有内容,虽然它是整个概念的简化版本,但却被称为计算机系统的内存管理部分。

该系统的后果

  • 进程无法访问彼此的内存,每个人都有自己独立的虚拟地址,每个进程都会对不同的区域进行不同的转换,即使有时您可能会查看并发现两个进程尝试访问同一个虚拟地址。
  • 这个系统很适合作为缓存系统,你通常不使用你所拥有的整个4GB,那么为什么要浪费呢?让其他人分享并让他们也使用它;当你需要更多时,你将从高清中取出你的数据并取代他们的数据,当然是要付出代价。

答案 1 :(得分:10)

  

物理地址 - 当处理器处于系统模式时,处理器使用的地址是物理地址。

不一定是真的。这取决于特定的CPU。在x86 CPU上,一旦启用了页面转换,所有代码都停止使用物理地址或可以轻松转换为物理地址的地址(SMM,AFAIK除外,但这在这里并不重要)。

  

逻辑地址 - 当处理器处于用户模式时,使用的地址是逻辑地址。通过添加带偏移值的基址寄存器,这些都可以映射到某个物理地址。

逻辑地址不一定仅适用于用户模式。在x86 CPU上,它们也存在于内核模式中。

  

我遇到过讨论,虚拟和逻辑地址/地址空间是相同的。这是真的吗?

这取决于特定的CPU。可以配置x86 CPU,以便不显式使用段。隐式使用它们,它们的基数始终为0(线程局部存储段除外)。从逻辑地址中删除段选择器时剩下的是32位(或64位)偏移量,其值与32位(或64位)虚拟地址重合。在这种简化的设置中,您可以认为两者相同或逻辑地址不存在。事实并非如此,但对于大多数实际目的来说,这已经足够近似了。

答案 2 :(得分:5)

我指的是基于intel x86 CPU的以下答案

逻辑地址与虚拟地址的区别

每当程序正在执行时,CPU会为包含(16位段选择器和32位偏移)的指令生成逻辑地址。基本上,使用逻辑地址字段生成虚拟(线性地址)。

段选择器是16位字段,其中第一个13位是索引(这是指向段描述符的指针驻留在GDT中,如下所述),1位TI字段(TI = 1,参考LDT,TI = 0参考GDT )

现在段选择器或者说段标识符是指代码段或数据段或堆栈段等.Linux包含一个GDT / LDT(全局/本地描述符表),其中包含每个段的8字节描述符并保存基(虚拟)该段的地址。

因此,对于每个逻辑地址,使用以下步骤计算虚拟地址。

1)检查段选择器的TI字段以确定哪个描述符 表存储段描述符。该字段表示描述符是 或者在GDT中(在这种情况下,分割单元获得基线性 来自gdtr寄存器的GDT地址)或有源LDT中的GDT地址(在这种情况下为 分段单元从ldtr寄存器中获取该LDT的基本线性地址。

2)从Segment的索引字段计算Segment Descriptor的地址 选择。索引字段乘以8(段描述符的大小), 并将结果添加到gdtr或ldtr寄存器的内容中。

3)将逻辑地址的偏移量添加到段描述符的Base字段, 从而获得线性(虚拟)地址。

现在,Pagging单位的工作是从虚拟地址转换物理地址。

参考:了解linux内核,第2章内存寻址

答案 3 :(得分:3)

通常,每个发布的地址(对于x86体系结构)都是一个逻辑地址,通过段表转换为线性地址。在转换成线性地址后,它会通过页面表转换为物理地址 一篇很好的文章深入解释了相同的内容:
http://duartes.org/gustavo/blog/post/memory-translation-and-segmentation/

答案 4 :(得分:2)

用户虚拟地址 这些是用户空间程序看到的常规地址。用户地址的长度为32位或64位,具体取决于底层的硬件体系结构,并且每个进程都有自己的虚拟地址空间。

物理地址 处理器和系统内存之间使用的地址。物理地址为32位或64位;在某些情况下,甚至32位系统也可以使用64位物理地址。

公交地址 外设总线和内存之间使用的地址。通常,它们与处理器使用的物理地址相同,但不一定是这种情况。当然,总线地址高度依赖于体系结构。

内核逻辑地址 这些组成内核的普通地址空间。这些地址映射大部分或全部主内存,并且经常被视为物理地址。在大多数体系结构上,逻辑地址及其关联的物理地址仅相差一个恒定的偏移量。逻辑地址使用硬件的本机指针大小,因此可能无法访问配备齐全的32位系统上的所有物理内存。逻辑地址通常存储在unsigned long或void *类型的变量中。从kmalloc返回的内存具有逻辑地址。

内核虚拟地址 这些与逻辑地址的不同之处在于它们不必直接映射到物理地址。所有逻辑地址都是内核虚拟地址; vmalloc分配的内存还具有虚拟地址(但没有直接的物理映射)。功能kmap返回虚拟地址。虚拟地址通常存储在指针变量中。

如果您有逻辑地址,则宏__pa()(在中定义)将返回其关联的物理地址。可以使用__va()将物理地址映射回逻辑地址,但仅适用于低内存页面。

Reference

答案 5 :(得分:1)

物理地址是存储器单元所看到的地址,即加载到存储器地址寄存器中的地址。 逻辑地址是CPU生成的地址。 用户程序永远不会看到真实的物理地址。内存映射单元将逻辑地址转换为物理地址。 用户进程生成的逻辑地址必须在使用之前映射到物理内存。

答案 6 :(得分:1)

逻辑存储器相对于相应的程序,即(程序的起点+偏移)

虚拟内存使用映射到ram和磁盘的页表。通过这种方式,每个进程可以为每个进程承诺更多内存。

答案 7 :(得分:0)

在Usermode或UserSpace中,程序看到的所有地址都是虚拟地址。 在内核模式下,内核看到的地址仍然是虚拟的,但称为逻辑,因为它们等于物理+页面偏移。  物理地址是RAM可以看到的地址。 使用虚拟内存程序中的每个地址都通过页表。

答案 8 :(得分:0)

当你写一个小程序时,例如:

int a=10;
int main()
{
 printf("%d",a);
}   


compile: >gcc -c fname.c
>ls 
fname.o //fname.o is generated
>readelf -a fname.o >readelf_obj.txt

/ readelf是一个命令,用于理解目标文件和可执行文件,它们将是0和1。输出写在readelf_onj.txt文件 /

`>vim readelf_obj.txt`

/ *在"部分标题"你会看到你的目标文件的.data .text .rodata部分。每个起始地址或基地址从0000开始并逐渐增长到相应的大小,直到达到标题下的大小" size" ---->这些是逻辑地址。* /

>gcc fname.c
>ls
a.out //your executabe
>readelf -a a.out>readelf_exe.txt
>vim readelf_exe.txt 

/ *这里所有部分的基地址都不为零。它将从特定地址开始,最终到达特定地址。链接器将为所有部分提供连续的地址(在readelf_exe.txt文件中观察。观察每个部分的基址和大小。它们连续启动)所以只有基地址不同.--->这称为虚拟地址空间。* /

物理地址 - >内存ll具有物理地址。当您的可执行文件加载到内存中时,它将具有物理地址。实际上,虚拟地址被映射到执行的物理地址。