操作系统学习-16 存储器与程序的装入与链接

本文介绍了存储器的多级结构,包括寄存器、主存储器、高速缓存和磁盘缓存。讨论了程序装入的三种方式:绝对装入、可重定位装入和动态运行时装入,以及链接的静态链接、装入时动态链接和运行时动态链接。重点在于理解存储层次和程序装入链接在操作系统中的作用。
摘要由CSDN通过智能技术生成

写在前面

目前为止,我开始进入新的一章的学习——存储器管理的学习了。不知不觉已经学了进程管理与处理机管理这些知识了,回过头看看感觉自己的博客总结的还是不错的,但是呢,总结不错的也要重新温习一遍才能掌握这些最基础的知识。目前还是保持学习节奏,尽量在年前完成操作系统基本知识的学习,并将博客复习一遍。说回来,这篇博客将首先整理一下存储器的基础知识,然后学习程序的装入与链接等知识。

多级存储器结构

对于通用计算机而言,存储层次至少应具有三级:最高层为 CPU 寄存器,中间为主存,最底层是辅存。对于较高档的计算机中,还可以根据具体的功能分工细划为6层:寄存器、高速缓存、主存储器、磁盘缓存、固定磁盘、可移动存储介质。

图1. 计算机系统存储层次示意
这里写图片描述

如上图所示,在存储层次中越往上,存储介质的访问速度越快,价格也越高,相对存储容量也越小。寄存器、高速缓存、主存储器和磁盘缓存均属于操作系统存储管理的管辖范畴,掉电后它们存储的信息不再存在。固定磁盘和可移动存储介质属于设备管理的管辖范畴,它们存储的信息将被长期保存。

寄存器和主存储器又被称为可执行存储器,存放于其中的信息与存放于辅存中的信息相比较而言,计算机所采用的访问机制是不同的,所需耗费的时间也是不同的。进程可以在很少的时钟周期内使用一条 load 或 store 指令对可执行存储器进行访问。但对辅存的访问则需要通过 I/O 设备来实现,因此,访问中将涉及到中断、设备驱动程序以及物理设备的运行,所需耗费的时间远远高于对可执行存储器访问的时间,一般相差 3 个数量级甚至更多。

对于不同层次的存储介质,由操作系统进行统一管理。操作系统的存储管理,负责对可执行存储器的分配、回收以及提供在存储层次间数据移动的管理机制。在设备和文件管理中,根据用户的需求提供对辅存的管理机制。

主存储器与寄存器

主存储器
主存储器(简称内存或主存)是计算机系统中一个主要部件,用于保存进程运行时的程序和数据,也称可执行存储器。CPU
的控制部件只能从主存储器中取得指令和数据,数据能够从主存储器读取并将它们装入到寄存器中,或者从寄存器存入到主存储器。CPU 与外围设备交换的信息一般也依托于主存储器地址空间。由于主存储器的访问速度远低于 CPU 执行指令的速度,为缓和这一矛盾,在计算机系统中引入了寄存器和高速缓存。

主存容量对于当前的微机系统和大中型机,可能一般为数十 MB到数 GB,而且容量还在不断增加,而嵌入式计算机系统一般仅有几十 KB 到几 MB。

寄存器
寄存器访问速度最快,完全能与 CPU 协调工作,但价格却十分昂贵,因此容量不可能做得很大。寄存器的长度一般以字(word)为单位。寄存器用于加速存储器的访问速度,如用寄存器存放操作数,或用作地址寄存器加快地址转换速度等。

寄存器的数目,对于当前的微机系统和大中型机,可能有几十个甚至上百个;而嵌入式计算机系统一般仅有几个到几十个。

高速缓存和磁盘缓存

高速缓存
高速缓存是现代计算机结构中的一个重要部件,其容量大于或远大于寄存器,而比内存约小两到三个数量级左右,从几十 KB 到几 MB,访问速度快于主存储器。

根据程序执行的局部性原理(即程序在执行时将呈现出局部性规律,在一较短的时间内,程序的执行仅局限于某个部分),将主存中一些经常访问的信息存放在高速缓存中,减少访问主存储器的次数,可大幅度提高程序执行速度。通常,进程的程序和数据是存放在主存储器中,每当使用时,被临时复制到一个速度较快的高速缓存中。

磁盘缓存
由于目前磁盘的 I/O 速度远低于对主存的访问速度,因此将频繁使用的一部分磁盘数据和信息,暂时存放在磁盘缓存中,可减少访问磁盘的次数。磁盘缓存本身并不是一种实际存在的存储介质,它依托于固定磁盘,提供对主存储器存储空间的扩充,即利用主存中的存储空间,来暂存从磁盘中读出(或写入)的信息。

主存也可以看做是辅存的高速缓存,因为,辅存中的数据必须复制到主存方能使用;反之,数据也必须先存在主存中,才能输出到辅存。

总结一下:一个文件的数据可能出现在存储器层次的不同级别中,一个文件数据通常被存储在辅存中(如硬盘),当其需要运行或被访问时,就必须调入主存,也可以暂时存放在主存的磁盘高速缓存中。大容量的辅存常常使用磁盘,磁盘数据经常备份到磁带或可移动磁盘组上,以防止硬盘故障时丢失数据。有些系统自动地把老文件数据从辅存转储到海量存储器中,如磁带上,这样做还能降低存储价格。

程序的装入和链接

在多道程序环境下,要使程序运行,必须先为之创建进程。而创建进程的第一件事,便是将程序和数据装入内存。将一个用户源程序变为一个可在内存中执行的程序,通常都要经过以下几个步骤:

  1. 编译:由编译程序(Compiler)将用户源代码编译成若干个目标模块(Object Module);
  2. 链接:由链接程序(Linker)将编译后形成的一组目标模块,以及它们所需要的库函数链接在一起,形成一个完整的装入模块(Load Module);
  3. 装入:由装入程序(Loader)将装入模块装入内存;

图2. 对用户程序的处理步骤
这里写图片描述

程序的装入

在将一个装入模块装入内存时,可以有绝对装入方式、可重定位装入方式和动态运行时装入方式:

绝对装入方式(Absolute Loading Mode)
在编译时,如果知道程序将驻留在内存的什么位置,那么,编译程序将产生绝对地址的目标代码。例如,事先已知用户程序(进程)驻留在从 R 处开始的位置,则编译程序所产生的目标模块(即装入模块)便从 R 处开始向上扩展。

绝对装入程序按照装入模块中的地址,将程序和数据装入内存。装入模块被装入内存后,由于程序中的逻辑地址与实际内存地址完全相同,故不须对程序和数据的地址进行修改。说白了,这种方式就是直接跟内存打交道,程序员直接给出绝对地址时,不仅要求程序员熟悉内存的使用情况,而且一旦程序或数据被修改后,可能要改变程序中的所有地址。

绝对装入方式只能将目标模块装入到内存中事先指定的位置。在多道程序环境下,编译程序不可能预知所编译的目标模块应放在内存的何处,因此,绝对装入方式只适用于单道程序环境。

可重定位装入方式(Relocation Loading Mode)
在多道程序环境下,所得到的目标模块的起始地址通常是从 0 开始的,程序中的其它地址也都是相对于起始地址计算的。此时应采用可重定位装入方式,根据内存的当前情况,将装入模块装入到内存的适当位置。

需要注意:在采用可重定位装入程序将装入模块装入内存后,会使装入模块中的所有逻辑地址与实际装入内存的物理地址不同。通常是把在装入时对目标程序中指令和数据的修改过程称为重定位。又因为地址变换通常是在装入时一次完成的,以后不再改变,故称为静态重定位。

可重定位装入方式可将装入模块装入到内存中任何允许的位置,故可用于多道程序环境;但这种方式并不允许程序运行时在内存中移动位置。因为,程序在内存中的移动,意味着它的物理位置发生了变化,这时必须对程序和数据的地址(是绝对地址)进行修改后方能运行。

动态运行时装入方式(Dynamic Run-time Loading)
在运行过程中程序在内存中的位置可能经常要改变,此时就应采用动态运行时装入的方式。动态运行时的装入程序在把装入模块装入内存后,并不立即把装入模块中的相对地址转换为绝对地址,而是把这种地址转换推迟到程序真正要执行时才进行。因此,装入内存后的所有地址都仍是相对地址。为使地址转换不影响指令的执行速度,这种方式需要一个重定位寄存器的支持。

程序的链接

源程序经过编译后,可得到一组目标模块,再利用链接程序将这组目标模块链接,形成装入模块。根据链接时间的不同,可把链接分成如下三种:

  1. 静态链接。在程序运行之前,先将各目标模块及它们所需的库函数,链接成一个完整的装配模块,以后不再拆开。这种方式称为静态链接方式。
  2. 装入时动态链接。这是指将用户源程序编译后所得到的一组目标模块,在装入内存时,采用边装入边链接的链接方式。
  3. 运行时动态链接。这是指对某些目标模块的链接,是在程序执行中需要该(目标)模块时,才对它进行的链接。

静态链接方式(Static Linking)
三个目标模块 A、B、C,它们的长度分别为 L、M 和 N。在模块 A 中有一条语句 CALL B,用于调用模块 B。在模块 B 中有一条语句 CALL C,用于调用模块 C。B 和C 都属于外部调用符号,在将这几个目标模块装配成一个装入模块时,须解决以下两个问题:

  1. 对相对地址进行修改。在由编译程序所产生的所有目标模块中,使用的都是相对地址,其起始地址都为 0,每个模块中的地址都是相对于起始地址计算的。在链接成一个装入模块后,原模块 B 和 C 在装入模块的起始地址不再是 0,而分别是 L 和 L+M,所以此时须修改模块 B 和 C 中的相对地址,即把原 B 中的所有相对地址都加上 L,把原 C 中的所有相对地址都加上 L+M。
  2. 变换外部调用符号。将每个模块中所用的外部调用符号也都变换为相对地址,如把B 的起始地址变换为 L,把 C 的起始地址变换为 L+M,如图 4-4(b)所示。这种先进行链接所形成的一个完整的装入模块,又称为可执行文件。通常都不再拆开它,要运行时可直接将它装入内存。这种事先进行链接,以后不再拆开的链接方式,称为静态链接方式。

图3. 程序静态链接示意图
这里写图片描述

装入时动态链接(Load-time Dynamic Linking)
用户源程序经编译后所得的目标模块,是在装入内存时边装入边链接的,即在装入一个目标模块时,若发生一个外部模块调用事件,将引起装入程序去找出相应的外部目标模块,并将它装入内存。还要按照上图所示的方式来修改目标模块中的相对地址。装入时动态链接方式有以下优点:

  1. 便于修改和更新。对于经静态链接装配在一起的装入模块,如果要修改或更新其中的某个目标模块,则要求重新打开装入模块。这不仅是低效的,而且有时是不可能的。若采用动态链接方式,由于各目标模块是分开存放的,所以要修改或更新各目标模块是件非常容易的事。
  2. 便于实现对目标模块的共享。在采用静态链接方式时,每个应用模块都必须含有其目标模块的拷贝,无法实现对目标模块的共享。但采用装入时动态链接方式,OS 则很容易将一个目标模块链接到几个应用模块上,实现多个应用程序对该模块的共享。

运行时动态链接(Run-time Dynamic Linking)
将对某些模块的链接推迟到程序执行时才进行链接,亦即,在执行过程中,当发现一个被调用模块尚未装入内存时,立即由 OS 去找到该模块并将之装入内存,把它链接到调用者模块上。凡在执行过程中未被用到的目标模块,都不会被调入内存和被链接到装入模块上,这样不仅可加快程序的装入过程,而且可节省大量的内存空间。

这种链接方式改进了在许多情况下,应用程序在运行时,每次要运行的模块可能是不相同的。但由于事先无法知道本次要运行哪些模块,故只能是将所有可能要运行到的模块都全部装入内存,并在装入时全部链接在一起,这一低效的情况。

写在后面

这一部分概念居多,看了三遍也没有完全理清,后面复习这篇博客的时候还是多理解,关键句子多读几遍。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值