目录
1、存储器的层次结构
(1)多层结构的存储器系统
1.1 - 存储器的多层结构
1.2 - 可执行存储器
(2)主存储器与寄存器
2.1 - 主存储器
2.2 - 寄存器
(3)高速缓存和磁盘缓存
3.1 - 高速缓存
3.2 - 磁盘缓存
2、程序的装入和链接
(1)程序的装入
1.1 - 绝对装入方式(Absolute Loading Mode)
1.2 - 可重定位装入方式(Relocation Loading Mode)
1.3 - 动态运行时的装入方式(Dynamic Run-time Loading)
(2)程序的链接
2.1 - 静态链接(Static Linking)方式
2.2 - 装入时动态链接(Load-time Dynamic Linking)
1、存储器的层次结构
在计算机执行时,几乎每一条指令都涉及对存储器的访问,因此要求对存储器的访问速度能跟得上处理机的运行速度。或者说,存储器的速度必须非常快,能与处理机的速度相匹配,否则会明显地影响到处理机的运行。此外还要求存储器具有非常大的容量,而且存储器的价格还应很便宜。// 速度快、容量大、价格便宜
对于这样十分严格的三个条件,目前是无法同时满足的。于是在现代计算机系统中都无一例外地采用了多层结构的存储器系统。
(1)多层结构的存储器系统
1.1 - 存储器的多层结构
对于通用计算机而言,存储层次至少应具有三级:最高层为 CPU 寄存器,中间为主存,最底层是辅存。在较高档的计算机中,还可以根据具体的功能细分为寄存器、高速缓存、主存储器、磁盘缓存、固定磁盘、可移动存储介质等 6 层。如下图所示。// 寄存器+主存+辅存
在存储层次中,层次越高(越靠近CPU),存储介质的访问速度越快,价格也越高,相对所配置的存储容量也越小。其中,寄存器、高速缓存、主存储器和磁盘缓存均属于操作系统存储管理的管辖范畴,掉电后它们中存储的信息不再存在。而低层的固定磁盘和可移动存储介质则属于设备管理的管辖范畴,它们存储的信息将被长期保存。//主存断电信息会丢失,辅存可以长期保存信息
1.2 - 可执行存储器
在计算机系统的存储层次中,寄存器和主存储器又被称为可执行存储器。对于存放于其中的信息,与存放于辅存中的信息相比较而言,计算机所采用的访问机制是不同的,所需耗费的时间也是不同的。
进程可以在很少的时钟周期内使用一条 load 或 store 指令对可执行存储器进行访问。但对辅存的访问则需要通过 I/O 设备实现,因此,在访问中将涉及到中断、设备驱动程序以及物理设备的运行,所需耗费的时间远远高于访问可执行存储器的时间,一般相差 3 个数量级甚至更多。//对主存和辅存的访问机制不同,访问效率也不同。
对于不同层次的存储介质,由操作系统进行统一管理。操作系统的存储管理负责对可执行存储器的分配、回收,以及提供在存储层次间数据移动的管理机制,例如主存与磁盘缓存、高速缓存与主存间的数据移动等。而设备和文件管理则根据用户的需求,提供对辅存的管理机制。//存储管理指的是对可执行存储器的管理,设备和文件管理针对的是辅存的管理
(2)主存储器与寄存器
2.1 - 主存储器
主存储器简称内存或主存,是计算机系统中的主要部件,用于保存进程运行时的程序和数据,也称可执行存储器。
通常,处理机都是从主存储器中取得指令和数据的,并将其所取得的指令放入指令寄存器中,而将其所读取的数据装入到数据寄存器中;或者反之,将寄存器中的数据存入到主存储器。
由于主存储器访问速度远低于 CPU 执行指令的速度,为缓和这一矛盾,在计算机系统中引入了寄存器和高速缓存。
2.2 - 寄存器
寄存器具有与处理机相同的速度,故对寄存器的访问速度最快,完全能与 CPU 协调工作,但价格却十分昂贵,因此容量不可能做得很大。
在早期计算机中,寄存器的数目仅为几个,主要用于存放处理机运行时的数据,以加速存储器的访问速度,如使用寄存器存放操作数,或用作地址寄存器加快地址转换速度等。随着 VLSI(超大规模集成电路) 的发展,寄存器的成本也在迅速降低,在当前的微机系统和大中型机中,寄存器的数目都已增加到数十个到数百个,而寄存器的字长一般是 32 位或 64 位;而在小型的入式计算机中,寄存器的数目仍只有几个到十几个,而且寄存器的字长通常只有 8 位。
(3)高速缓存和磁盘缓存
3.1 - 高速缓存
高速缓存是现代计算机结构中的一个重要部件,它是介于寄存器和存储器之间的存储器,主要用于备份主存中较常用的数据,以减少处理机对主存储器的访问次数,这样可大幅度地提高程序执行速度。高速缓存容量远大于寄存器,而比内存约小两到三个数量级左右,从几十 KB 到几 MB,访问速度快于主存储器。在计算机系统中,为了缓和内存与处理机速度之间的矛盾,许多地方都设置了高速缓存。// 可设置两级或多级高速缓存
3.2 - 磁盘缓存
由于目前磁盘的 I/O 速度远低于对主存的访问速度,为了缓和两者之间在速度上的不匹配,而设置了磁盘缓存,主要用于暂时存放频繁使用的一部分磁盘数据和信息,以减少访问磁盘的次数。但磁盘缓存与高速缓存不同,它本身并不是一种实际存在的存储器,而是利用主存中的部分存储空间暂时存放从磁盘中读出(或写入)的信息。主存也可以看作是辅存的高速缓存,因为,辅存中的数据必须复制到主存方能使用,反之,数据也必须先存在主存中,才能输出到辅存。
2、程序的装入和链接
用户程序要在系统中运行,必须先将它装入内存,然后再将其转变为一个可以执行的程序,通常都要经过以下几个步骤:
- 编译,由编译程序(Compiler)对用户源程序进行编译,形成若干个目标模块(ObjectModule)
- 链接,由链接程序(Linker)将编译后形成的一组目标模块以及它们所需要的库函数链接在一起,形成一个完整的装入模块(Load Module)
- 装入,由装入程序(Loader)将装入模块装入内存
(1)程序的装入
将一个装入模块装入内存时,可以有如下三种装入方式:
1.1 - 绝对装入方式(Absolute Loading Mode)
当计算机系统很小,且仅能运行单道程序时,完全有可能知道程序将驻留在内存的什么位置。此时可以采用绝对装入方式。用户程序经编译后,将产生绝对地址(即物理地址)的目标代码。例如,事先已知用户程序驻留在从 R 处开始的位置,则编译程序所产生的目标模块,便可从 R 处开始向上扩展。绝对装入程序便可按照装入模块中的地址,将程序和数据装入内存。装入模块被装入内存后,由于程序中的相对地址(即逻辑地址)与实际内存地址完全相同,故不需对程序和数据的地址进行修改。// 无需进行逻辑地址和物理地址的转换
1.2 - 可重定位装入方式(Relocation Loading Mode)
采用可重定位装入程序将装入模块装入内存后,会使装入模块中的所有逻辑地址与实际装入内存后的物理地址不同。// 需要把逻辑地址转换为物理地址
例如,在用户程序的 1000 号单元处有一条指令 LOAD(1,2500),该指令的功能是将 2500 单元中的整数 365 取至寄存器 1。但若将该用户程序装入到内存的 10000~15000 号单元而不进行地址变换,则在执行 11000 号单元中的指令时,它将仍从 2500 号单元中把数据取至寄存器 1,而导致数据错误。
由此可见,正确的方法应该是,将取数指中的地址 2500 修改成 12500,即把指令中的逻辑地址 2500 与本程序在内存中的起始地址 10000 相加才得到正确的物理地址 12500。除了数据地址应修改外,指令地址也须做同样的修改,即将指令的逻辑地址 1000 与起始地址 10000 相加,得到绝对地址 11000。//静态进行地址转换,转换后不再修改
通常,把在装入时对目标程序中指令和数据地址的修改过程称为重定位。又因为地址变换通常是在进程装入时一次完成的,以后不再改变,故称为静态重定位。//该方式不允许程序运行时在内存中移动位置
1.3 - 动态运行时的装入方式(Dynamic Run-time Loading)
如果程序在内存中的移动,意味着它的物理位置发生了变化,这时必须对程序和数据的地址(绝对地址)进行修改后方能运行。实际情况下,程序在运行过程中它在内存中的位置可能经常要改变,例如,在具有对换功能的系统中,一个进程可能被多次换出,又多次被换入,每次换入后的位置通常是不同的。在这种情况下,就应采用动态运行时装入的方式。//使用背景
动态运行时的装入程序在把装入模块装入内存后,并不立即把装入模块中的逻辑地址转换为物理地址,而是把这种地址转换推迟到程序真正要执行时才进行。因此,装入内存后的所有地址都仍是逻辑地址。为使地址转换不影响指令的执行速度,这种方式需要一个重定位寄存器的支持。
(2)程序的链接
源程序经过编译后,可得到一组目标模块。链接程序的功能是将这组目标模块以及它们所需要的库函数装配成一个完整的装入模块。在对目标模块进行链接时,根据进行链按的时间不同,可把链接分成如下三种:
2.1 - 静态链接(Static Linking)方式
在程序运行之前,先将各目标模块及它们所需的库函数链接成一个完整的装配模块,以后不再拆开。我们把这种事先进行链接的方式称为静态链接方式。
例如,在下图中示出了经过编译后所得到的三个目标模块 A、B、C,它们的长度分别为 L、M 和 N。在模块 A 中有一条语句 CALL B,用于调用模块 B。在模块 B 中有一条语句 CALL C,用于调用模块 C。B 和 C 都属于外部调用符号,在将这几个目标模块装配成一个装入模块时,须解决以下两个问题:// 事先进行链接:把所有程序用到的资源一次性加载到内存
对相对地址进行修改。在由编译程序所产生的所有目标模块中,使用的都是相对地址,其起始地址都为 0,每个模块中的地址都是相对于起始地址计算的。在链接成一个装入模块后,原模块 B 和 C 在装入模块的起始地址不再是 0,而分别是 L 和 L+M,所以此时须修改模块 B 和 C 中的相对地址,即把原 B 中的所有相对地址都加上 L,把原 C 中所有相对地址都加上 L+M。
变换外部调用符号。将每个模块中所用的外部调用符号也都变换为相对地址,如把 B 的起始地址变换为 L,把 C 的起始地址变换为 L+M。这种先进行链接所形成的一个完整的装入模块,又称为可执行文件。通常都不再把它拆开,要运行时可直接将它装入内存。把这种事先进行链接而以后不再拆开的链接方式称为静态链接方式。
2.2 - 装入时动态链接(Load-time Dynamic Linking)
这是指将用户源程序编译后所得到的一组目标模块,在装入内存时,采用边装入边链接的链接方式。即在装入一个目标模块时,若发生一个外部模块调用事件,将引起装入程序去找出相应的外部目标模块,并将它装入内存。装入时动态链接方式有以下优点:// 装入时动态链接
- 便于修改和更新。对于经静态链接装配在一起的装入模块,如果要修改或更新其中的某个目标模块,则要求重新打开装入模块。这不仅是低效的,而且有时是不可能的。若采用动态链接方式,由于各目标模块是分开存放的,所以要修改或更新各目标模块是件非常容易的事。
- 便于实现对目标模块的共享。在采用静态链接方式时,每个应用模块都必须含有其目标模块的拷贝,无法实现对目标模块的共享。但采用装入时动态链接方式时,OS 就很容易将一个目标模块链接到几个应用模块上,实现多个应用程序对该模块的共享。
2.3 - 运行时动态链接(Run-time Dynamic Linking)
在许多情况下,应用程序在运行时,每次要运行的模块可能是不相同的。但由于事先无法知道本次要运行哪些模块,故只能是将所有可能要运行到的模块全部都装入内存,并在装入时全部链接在一起。显然这是低效的,因为往往会有部分目标模块根本就不运行。比较典型的例子是作为错误处理用的目标模块,如果程序在整个运行过程中都不出现错误则显然就不会用到该模块。
近几年流行起来的运行时动态链接方式,是对装入时链接方式的一种改进。这种链接方式是,将对某些模块的链接推迟到程序执行时才进行。亦即,在执行过程中,当发现一个被调用模块尚未装入内存时,立即由 OS 去找到该模块,并将之装入内存,将其链接到调用者模块上。凡在执行过程中未被用到的目标模块,都不会被调入内存和被链接到装入模块上,这样不仅能加快程序的装入过程,而且可节省大量的内存空间。// 运行时动态链接
// 程序的装入和链接,从静态发展到动态,由事先准备延迟到程序用到才准备,可节省大量内存空间,唯一的问题是,程序在运行时才去调用资源,会占用额外的操作时间