37

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

这是我的理解:

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

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

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

任何帮助都深表感谢。

4

9 回答 9

68

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

地址类型

关于问题3:

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

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

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

  • 物理地址:RAM 芯片中物理位置的地址。
  • 逻辑/虚拟地址:你的程序用来访问它的东西的地址。它通常稍后由硬件芯片转换为物理地址(大多数情况下,甚至 CPU 都没有真正意识到这种转换)。

虚拟/逻辑地址

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

显然,如果您同时运行多个程序(您总是这样做,GUI、Init 进程、Shell、时钟应用程序、日历等),这将不起作用。

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

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

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

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

现在,如果您的程序要求 (oftenNotUsed) 变量会发生什么?MMU+OS 会注意到这个“未命中”并将其从硬盘中提取到 RAM 中供 CPU 使用,然后将其交给 CPU,就好像它在地址 (0xaa) 中一样;此获取意味着 RAM 中存在的一些数据将被发送回硬盘。

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

概括

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

物理地址:如果您在操作系统之上运行,您将永远无法到达的地址。它是您的数据(无论其虚拟地址如何)驻留在 RAM 中的位置。如果您的数据来回发送到硬盘以容纳更多空间用于其他进程,这将改变。

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

该系统的后果

  • 进程无法访问彼此的内存,每个人都有各自的虚拟地址,并且每个进程都会获得不同的翻译到不同的区域,即使有时您可能会发现两个进程试图访问相同的虚拟地址。
  • 该系统作为缓存系统运行良好,您通常不会使用可用的全部 4GB,那么为什么要浪费它呢?让其他人分享它,让他们也使用它;当您的进程需要更多时,操作系统将从 HD 中获取您的数据并替换其他进程的数据,当然这是有代价的。
于 2013-04-06T13:21:51.653 回答
10

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

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

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

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

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

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

于 2013-04-06T13:10:27.280 回答
6

我指的是基于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 的基线性地址) LDT 来自 ldtr 寄存器)。

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

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

现在,分页单元的工作是从虚拟地址转换物理地址。

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

于 2014-04-12T09:05:42.040 回答
5

通常发出的每个地址(对于 x86 架构)都是一个逻辑地址,它通过段表转换为线性地址。转换成线性地址后,再通过页表转换成物理地址。
一篇很好的文章深入解释了这一点:http:
//duartes.org/gustavo/blog/post/memory-translation-and-segmentation/

于 2014-07-01T09:50:44.890 回答
4

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

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

总线地址 外围总线和内存之间使用的地址。通常它们与处理器使用的物理地址相同,但不一定如此。当然,总线地址高度依赖于体系结构。

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

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

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

参考

于 2018-09-28T12:23:18.950 回答
1

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

于 2014-02-02T14:17:52.250 回答
1

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

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

于 2016-03-30T17:43:40.067 回答
0

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

于 2013-04-06T13:23:50.017 回答
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 开始并增长到相应的大小,直到达到标题“大小”下的大小---->这些是逻辑地址。*/

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

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

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

于 2016-07-08T21:19:17.030 回答