浅析操作系统中的虚拟地址与物理地址
⽬录
⼀、前⾔
⼆、你看到的所有地址都不是真的
三、物理寻址 Physical Addressing
四、虚拟寻址 Virtual Addressing
⼀、前⾔
先解释下⼀个困扰了我很久的问题:虚拟地址(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 会将它存放到指定的寄存器中。看下图:
2020年斋月封斋时间表其实不难发现,物理寻址这种⽅式,每⼀个程序都直接访问物理内存,其实是存在重⼤缺陷的:
1)⾸先,⽤户程序可以寻址内存的任意⼀个字节,它们就可以很容易地破坏操作系统,从⽽使系统慢慢地停⽌运⾏。
2)再次,这种寻址⽅式使得操作系统中同时运⾏两个或以上的程序⼏乎是不可能的。
举个例⼦,我们打开了三个相同的程序(计算器),都执⾏到某⼀步。⽐⽅说,⽤户在这三个程序的界⾯上分别输⼊了 10、100、1000,其对应的指令就是把⽤户输⼊的数字保存在内存中的某个地址中。如果这个位置只能保存⼀个数,那应该保存哪个呢?这不就冲突了吗?
再举个例⼦,摘⾃《现代操作系统 - 第 3 版》:
⼀个程序给物理内存地址 1000 赋值也就是存⼊了⼀些数据后,另⼀个程序也同样给这个地址赋值,那么第⼆个程序的赋值会覆盖掉第⼀个程序所赋的值,这会造成两个程序同时崩溃。天生我才必有用下一句
当然了,我们也说了是⼏乎不可能,不是完全不可能,还是有⼀些⽅法可以在物理寻址这种⽅式下实现多个程序并发运⾏的。
最简单的⽅法就是:⾸先,将空闲的进程存储在磁盘上,这样当它们不运⾏时就不会占⽤内存,然后,让⼀个程序(或者说进程)单独占⽤全部内存运⾏⼀⼩段时间,当发⽣上下⽂切换的时候,就停⽌这个进程,并将它所有的状态信息保存在磁盘上,再加载其他进程的状态信息,然后运⾏⼀段时间...... 只要在某⼀个时间内存中只有⼀个程序,那么就不会发⽣上述所说的地址冲突。这就实现了⼀种⽐较粗糙的并发。
为什么说他是粗糙的呢,因为这种⽅法有⼀个问题:将全部的内存信息保存到磁盘太慢了!特别是当内存增长的时候。
因此,我们考虑把进程对应的内存⼀直留在物理内存中,在发⽣上下⽂切换的时候就切换到特定的区域。
如下图所⽰,有 3 个进程(A、B、C),每个进程拥有从 512KB 物理内存中切出来给它们的⼀⼩部分内存,可以理解为这 3 个进程共享物理内存:娱乐男明星排行榜
显然,这种⽅式是存在⼀定安全隐患的。毕竟如果各个进程之间可以随意读取、写⼊内容的话那就乱套了。
那么如何对每个进程使⽤的地址进⾏保护(protection)呢?继续使⽤物理内存模型肯定是不⾏了,因此操作系统创造了⼀个新的内存抽象,引⼊了⼀个新的内存模型,那就是虚拟地址空间,很多书中都会直接称呼为 “地址空间(Address Space)”。
四、虚拟寻址 Virtual Addressing
我先通俗地解释下虚拟地址空间和虚拟地址的概念,直接上书中的定义读起来有点⽣涩。
就是说每个进程的栈啊、堆啊、代码段啊等等它们的实际物理内存地址对于这个进程来说是不可见的,谁也不能直接访问这个物理地址。
那我们怎么去访问这个进程呢?
操作系统会给每个进程分配⼀个虚拟地址空间(vitural address),每个进程包含的栈、堆、代码段这些都会从这个地址空间中被分配⼀个地址,这个地址就被称为虚拟地址。底层指令写⼊的地址也是虚拟地址。
每个进程都拥有⼀个⾃⼰的地址空间,并且独⽴于其他进程的地址空间。也就是说⼀个进程中的虚拟地址 28 所对应的物理地址与另⼀个进程中的虚拟地址 28 所对应的物理地址是不同的,这样就不会发⽣冲突了。
可以这么理解,物理地址就是⼀个仓库,虚拟地址就是⼀个门牌,⽐⽅说⼀共有三⼗个门牌,那么所有的进程都能看见这三⼗个门牌,但是他们看见的某个相同门牌,指向的并不是同⼀个仓库。
OK,下⾯再来看《现代操作系统 - 第 3 版》书中对于地址空间的解释,应该很容易理解了:
地址空间是⼀个进程可⽤于寻址内存的⼀套地址集合。每个进程都有⼀个⾃⼰的地址空间,并且这个地址空间独⽴于其他进程的地址空间(除了在⼀些特殊情况下进程需要共享它们的地址空间外)。电信积分兑换
地址空间的概念⾮常通⽤,并且在很多场合中出现。⽐如电话号码,在美国和很多其他国家,⼀个本地电话号码通常是⼀个 7 位的数字。因此,电话号码的地址空间是从 0 000 000 到 9 999 999。
地址空间也可以是⾮数字的,以 “” 结尾的⽹络域名的集合也是地址空间。这个地址空间是由所有包含 2~63 个字符
并且后⾯跟着 “” 的字符串组成的,组成这些字符串的字符可以是字母、数字和连字符。
到现在你应该已经明⽩地址空间的概念了,它是很简单的。
有了虚拟地址空间后,CPU 就可以通过⽣成⼀个虚拟地址来访问主存,这个虚拟地址在被送到内存之前会先被转换成合适的物理地址,这个虚拟地址到物理地址的转换过程称为地址翻译/地址转换(address translation)。
地址翻译需要 CPU 硬件和操作系统的密切合作:CPU 上的内存管理单元(Memory Management Unit,MMU)就是专门⽤来进⾏虚拟地址到物理地址的转换的,不过 MMU 需要借助存放在内存中的查询表,⽽这张表的内容正是由操作系统进⾏管理的。
那么,上述这⼀套 CPU ⽣成虚拟地址并进⾏地址翻译的流程就是虚拟寻址(virtual addressing)。举个例⼦,看下图:
以上就是浅析虚拟地址与物理地址的详细内容,更多关于虚拟地址物理地址的资料请关注其它相关⽂章!