你看到的所有地址都不是真的 | 虚拟地址与物理地址

先解释下一个困扰了我很久的问题:虚拟地址(vitural address)和逻辑地址(logical address)的区别。

大部分操作系统的书籍要么写的是虚拟地址,要么写的是逻辑地址,看的我一脸懵逼。

在《深入理解 Linux 内核》这本书中终于找到了确切的答案,这里我就不写出来了,扣概念的话这俩确实是有些区别的,不过对于我们日常使用以及理解操作系统来说的话,暂且可以把虚拟地址和逻辑地址理解为同一个意思

你看到的所有地址都不是真的

下面这段 C 代码摘录自《操作系统导论 - [美] 雷姆兹·H.阿帕希杜塞尔》,依次打印出 main 函数的地址,由 malloc(类似于 Java 中的 new 操作)返回的堆空间分配的值,以及栈上一个整数的地址:

你看到的所有地址都不是真的 | 虚拟地址与物理地址

得到以下输出:

你看到的所有地址都不是真的 | 虚拟地址与物理地址

我们需要知道的是,所有这些打印出来的地址都是虚拟的,在物理内存中这些地址并不真实存在,它们最终都将由操作系统和 CPU 硬件翻译成真正的物理地址,然后才能从真实的物理位置获取该地址的值。

OK,上述就当作一个引子,让各位对物理地址和虚拟地址有个直观的理解,下面正文开始。

物理寻址 Physical Addressing

物理地址的概念很好理解,你可以把它称为真正的地址。《深入理解计算机系统 - 第 3 版》中给出的物理地址(physical address)的定义如下:

计算机系统的主存被组织成一个由 M 个连续的字节大小的单元组成的数组。每字节都有一个唯一的物理地址。

比如说,第一个字节的物理地址是 0,接下来的字节地址是 1,再下一个是 2,以此类推,给定这种简单的结构,CPU 访问内存的最自然的方式就是使用这样的物理地址。我们把这种方式称为物理寻址(physical addressing)。

举个例子,比如说当程序执行了一条加载指令,指令内容是从物理地址 4 中读取 4 字节字传送到某个寄存器中。

物理寻址过程如下:当 CPU 执行到这条指令时,会生成物理地址 4,然后通过内存主线,把它传递给内存,内存取出从物理地址 4 处开始的 4 字节字,并将它返回给 CPU,CPU 会将它存放到指定的寄存器中。看下图:

你看到的所有地址都不是真的 | 虚拟地址与物理地址

其实不难发现,物理寻址这种方式,每一个程序都直接访问物理内存,其实是存在重大缺陷的:

1)首先,用户程序可以寻址内存的任意一个字节,它们就可以很容易地破坏操作系统,从而使系统慢慢地停止运行。

2)再次,这种寻址方式使得操作系统中同时运行两个或以上的程序几乎是不可能的

举个例子,我们打开了三个相同的程序(计算器),都执行到某一步。比方说,用户在这三个程序的界面上分别输入了 10、100、1000,其对应的指令就是把用户输入的数字保存在内存中的某个地址中。如果这个位置只能保存一个数,那应该保存哪个呢?这不就冲突了吗?

再举个例子,摘自《现代操作系统 - 第 3 版》:

一个程序给物理内存地址 1000 赋值也就是存入了一些数据后,另一个程序也同样给这个地址赋值,那么第二个程序的赋值会覆盖掉第一个程序所赋的值,这会造成两个程序同时崩溃。

当然了,我们也说了是几乎不可能,不是完全不可能,还是有一些方法可以在物理寻址这种方式下实现多个程序并发运行的。

最简单的方法就是:首先,将空闲的进程存储在磁盘上,这样当它们不运行时就不会占用内存,然后,让一个程序(或者说进程)单独占用全部内存运行一小段时间,当发生上下文切换的时候,就停止这个进程,并将它所有的状态信息保存在磁盘上,再加载其他进程的状态信息,然后运行一段时间...... 只要在某一个时间内存中只有一个程序,那么就不会发生上述所说的地址冲突。这就实现了一种比较粗糙的并发。

为什么说他是粗糙的呢,因为这种方法有一个问题:将全部的内存信息保存到磁盘太慢了!特别是当内存增长的时候。

因此,我们考虑把进程对应的内存一直留在物理内存中,在发生上下文切换的时候就切换到特定的区域。

如下图所示,有 3 个进程(A、B、C),每个进程拥有从 512KB 物理内存中切出来给它们的一小部分内存,可以理解为这 3 个进程共享物理内存:

你看到的所有地址都不是真的 | 虚拟地址与物理地址

显然,这种方式是存在一定安全隐患的。毕竟如果各个进程之间可以随意读取、写入内容的话那就乱套了。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyzxjf.html