预计阅读本页时间:-
3.2 一种存储器抽象:地址空间
总之,把物理地址暴露给进程会带来下面几个严重问题。第一,如果用户程序可以寻址内存的每个字节,它们就可以很容易地(故意地或偶然地)破坏操作系统,从而使系统慢慢地停止运行(除非有特殊的硬件进行保护,如IBM 360的锁键模式)。即使在只有一个用户进程运行的情况下,这个问题也是存在的。第二,使用这种模型,想要同时(如果只有一个CPU就轮流执行)运行多个程序是很困难的。在个人计算机上,同时打开几个程序是很常见的(一个文字处理器,一个邮件程序,一个网络浏览器,其中一个当前正在工作,其余的在按下鼠标的时候才会被激活)。在系统中没有对物理内存的抽象的情况下,很难做到上述情景,因此,我们需要其他办法。
3.2.1 地址空间的概念
要保证多个应用程序同时处于内存中并且不互相影响,则需要解决两个问题:保护和重定位。我们来看一个原始的对前者的解决办法,它曾被用在IBM 360上:给内存块标记上一个保护键,并且比较执行进程的键和其访问的每个内存字的保护键。然而,这种方法本身并没有解决后一个问题,虽然这个问题可以通过在程序被装载时重定位程序来解决,但这是一个缓慢且复杂的解决方法。
一个更好的办法是创造一个新的内存抽象:地址空间。就像进程的概念创造了一类抽象的CPU以运行程序一样,地址空间为程序创造了一种抽象的内存。地址空间是一个进程可用于寻址内存的一套地址集合。每个进程都有一个自己的地址空间,并且这个地址空间独立于其他进程的地址空间(除了在一些特殊情况下进程需要共享它们的地址空间外)。
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
地址空间的概念非常通用,并且在很多场合中出现。比如电话号码,在美国和很多其他国家,一个本地电话号码通常是一个7位的数字。因此,电话号码的地址空间是从0 000 000到9 999 999,虽然一些号码并没有被使用,比如以000开头的号码。随着手机、调制解调器和传真机数量的增长,这个空间变得越来越不够用了,从而导致需要使用更多位数的号码。Pentium的I/O端口的地址空间从0到16 383。IPv4的地址是32位的数字,因此它们的地址空间从0到232 -1(也有一些保留数字)。
地址空间可以不是数字的。一套“.com”的互联网域名也是地址空间。这个地址空间是由所有包含2~63个字符并且后面跟着“.com”的字符串组成的,组成这些字符串的字符可以是字母、数字和连字符。到现在你应该已经明白地址空间的概念了。它是很简单的。
比较难的是给每个程序一个自己的地址空间,使得一个程序中的地址28所对应的物理地址与另一个程序中的地址28所对应的物理地址不同。下面我们将讨论一个简单的方法,这个方法曾经很常见,但是在有能力把更复杂(而且更好)的机制运用在现代CPU芯片上之后,这个方法就不再使用了。
基址寄存器与界限寄存器
这个简单的解决办法使用一种简单的动态重定位。它所做的是简单地把每个进程的地址空间映射到物理内存的不同部分。从CDC 6600(世界上最早的超级计算机)到Intel 8088(原始IBM PC的心脏),所使用的经典办法是给每个CPU配置两个特殊硬件寄存器,通常叫做基址寄存器和界限寄存器。当使用基址寄存器和界限寄存器时,程序装载到内存中连续的空闲位置且装载期间无须重定位,如图3-2c所示。当一个进程运行时,程序的起始物理地址装载到基址寄存器中,程序的长度装载到界限寄存器中。在图3-2c中,当第一个程序运行时,装载到这些硬件寄存器中的基址和界限值分别是0和16 384。当第二个程序运行时,这些值分别是16 384和32 768。如果第三个16KB的程序被直接装载在第二个程序的地址之上并且运行,这时基址寄存器和界限寄存器里的值会是32 768和16 384。
每次一个进程访问内存,取一条指令,读或写一个数据字,CPU硬件会在把地址发送到内存总线前,自动把基址值加到进程发出的地址值上。同时,它检查程序提供的地址是否等于或大于界限寄存器里的值。如果访问的地址超过了界限,会产生错误并中止访问。这样,对图3-2c中第二个程序的第一条指令,程序执行
JMP 28
指令,但是硬件把这条指令解释成为
JMP 16412
所以程序如我们所愿地跳转到了CMP指令。在图3-2c中第二个程序的执行过程中,基址寄存器和界限寄存器的设置如图3-3所示。

使用基址寄存器和界限寄存器是给每个进程提供私有地址空间的非常容易的方法,因为每个内存地址在送到内存之前,都会自动先加上基址寄存器的内容。在很多实际系统中,对基址寄存器和界限寄存器会以一定的方式加以保护,使得只有操作系统可以修改它们。在CDC 6600中就提供了对这些寄存器的保护,但在Intel 8088中则没有,甚至没有界限寄存器。但是,Intel 8088提供了多个基址寄存器,使程序的代码和数据可以被独立地重定位,但是没有提供引用地址越界的预防机制。
使用基址寄存器和界限寄存器重定位的缺点是,每次访问内存都需要进行加法和比较运算。比较可以做得很快,但是加法由于进位传递时间的问题,在没有使用特殊电路的情况下会显得很慢。