【操作系统】存储器管理

存储器简介

既然是介绍存储器管理,肯定要介绍存储器

顾名思义,存储器,肯定是用来存储数据的,如果把计算机中的CPU比喻成人的宝贝脑袋瓜子,存储器就是你的海马体(不是海绵体)专门用来存储数据。

计算机执行时,几乎每一条指令都涉及对存储器的访问,就像你平时无论做什么,都会用到大脑中的记忆功能。

计算机存储器根据控制器指定的位置存入和取出信息。有了存储器,计算机才有记忆功能,才能保证正常工作。

理想的存储器要满足下面三个条件:

  • 存储器的速度非常快
  • 存储器的容量非常大
  • 存储器的价格很便宜

现在的技术无法同时满足这三个条件,所以现代计算机系统中无一例外地使用了多层结构地存储器系统

多层结构的存储器系统

存储器的多层结构

上图中,寄存器,高速缓存,主存储器,磁盘缓存均属于操作系统存储管理的管辖范畴,掉电后其中存储的信息不再存在。

而低层的固定磁盘和可移动存储介质则属于设备管理的管理范畴,其中存储的信息将被长期保存。

可执行存储器

在计算机系统的存储层次中,寄存器和主存储器又被称为可执行存储器,存放其中的信息与存放于辅存中的信息而言,计算机采用的访问机制是不同的,所需耗费的时间也不同。

主存储器,辅助存储器,寄存器

有一个很形象的比喻:
在这里插入图片描述

如果生物不太行,下面一个解释也很形象:
在这里插入图片描述

存储器分为 内存储器(内存)外存储器(外存)。内存储器又常称为主存储器(简称主存),属于主机的组成部分;外存储器又常称为辅助存储器(简称辅存),属于外部设备。
存储器管理的主要对象是内存,由于对外存的管理与对内存的管理类似,只是它们的用途不同,外存主要用于存放文件,对外存的管理放在文件管理中介绍

主存储器

主存储器简称内存或主存,用于保存进程运行时的程序和数据,也称可执行存储器。

  • 内储存器直接与CPU相连接,储存容量较小,但速度快,用来存放当前运行程序的指令和数据,并直接与CPU交换信息。

辅助存储器

  • 外储存器是内储存器的扩充。它储存容量大,价格低,但储存速度慢,一般用来存放大量暂时不用的程序,数据和中间结果,需要时,可成批的与内存进行信息交换。外存只能与内存交换信息,不能被计算机系统的其他部件直接访问。

寄存器

  • 寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果。CPU中大部分都是寄存器,可以把CPU理解为是寄存器的集合体,寄存器是有限存储容量的高速存储部件,它们可用来暂存指令、数据和位址。
    寄存器可分为
    • 通用寄存器、
    • 专用寄存器
    • 控制寄存器。

寄存器拥有非常高的读写速度,所以在寄存器之间的数据传送非常快。

高速缓存和磁盘缓存

在这里插入图片描述

高速缓存

高速缓存是现代计算机结构中一个重要部件,从上图可以看出,高速缓存是介于寄存器和主存储器之间的存储器,可以备份主存中较常用的数据,减少处理机对主存储器的访问次数,可以大幅度地提高执行速度。

通常进程的程序和数据存放在主存储器中,每当要访问时,才被临时复制到一个速度较快的高速缓存中。这样当CPU访问一组特定信息时,须首先检查它是否在高速缓存中,

  • 若存在,直接从高速缓存中取出
  • 不存在,从主存中读取信息

大部分计算机都有指令高速缓存,用来缓存下一条将执行的指令。

高速缓存的速度越高价格越贵,所以有的计算机系统中设计了两级或多级高速缓存。

  • 紧靠内存的一级高速缓存速度最高,容量最小,
  • 二级高速缓存的容量稍大,速度也稍低,
  • 离内存越远,速度越慢,容量越大,价格越低。

磁盘缓存

从上图中可以看到,磁盘缓存介于主存储器和固定磁盘之间。
我们知道,磁盘的I/O 速度远低于对主存的访问速度,为了缓和两个之间速度上的不匹配,我们设置了磁盘缓存.

磁盘缓存主要用于暂时存放频繁使用的一部分磁盘数据和信息,以减少访问磁盘的次数。

注意,磁盘缓存和高速缓存不同,磁盘缓存不是一种实际存在的存储器,而是利用主存中的部分存储空间暂时存放从磁盘中读出(或写出)的信息

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

一个文件通常被存储在辅存中,当期需要运行或被访问时,就必须调入主存,也可以暂时存放在主存的磁盘高速缓存中。

程序的装入与链接

用户程序在系统中运行,必须先将其装入内存,然后再将其变为一个可以执行的程序,通常需要如下步骤:

  1. 编译
    有编译程序对用户源程序进行编译 ,形成若干个目标模块
    在这里插入图片描述

  2. 链接
    由链接程序将编译后形成的一组目标模块以及它们所需要的库函数链接在一起,形成一个完整的装入模块
    在这里插入图片描述

  3. 装入
    由装入程序将装入模块装入内存
    在这里插入图片描述

程序装入

如果只有单个目标模块,则无需进行链接可直接装入,这个目标模块也就是装入模块。所以我们先来介绍这种最简单的情况。

在将一个装入模块装入内存时,有三种装入方式

绝对装入方式

(Absolute)
计算机系统很小,且仅能运行单道程序时,完全有可能知道程序将驻留在什么位置,此时可采用绝对装入方式
用户程序经过编译后,将产生绝对地址(即物理地址)的目标代码。

优点:装入过程简单
缺点:依赖于硬件结构,不适于多道程序系统

可重定位装入方式

(Relocation Loading Mode)
绝对装入方式只能将目标模块装入到内存中事先指定的位置,这只适用于弹道程序环境。

多道程序环境下,编译程序不可能预知编译后所得到的目标模块应在何处。
因此对于用户程序编译所形成的若干个目标模块,它们的起始地址都是从0 开始的,程序中的其他地址也都是相对于起始地址计算的。
此时可采用可重定位装入方式,它可以根据内存的具体情况将装入模块装入到内存的合适位置。

在采用可重定位装入程序将装入模块装入内存后,装入模块的所有逻辑地址与实际装入内存后的物理地址不同

案例

有这样一个用户程序编译而成的单个目标模块,起始地址从0开始。
在程序的1000号单元中,有一条指令LOAD 1, 2500,功能是将2500处的整数365取至寄存器1。

这样看没有问题。
在这里插入图片描述
若将其装入内存的10000~15000号单元
在这里插入图片描述
若没有进行地址变换,从图中可以看到,原来在2500处位置的数据现在位置变为了12500,但是在执行程序LOAD 1, 2500时,仍然会从地址2500处进行取值。导致数据错误。
所以要将目标程序的数据地址和指令地址进行修改。

把在装入时对目标程序中指令和数据地址的修改过程称为重定位

因为地址变换通常是在进程装入时一次完成的,以后不再改变,故称为静态重定位

动态运行时装入方式

(Dynamic Run-time Loading)
也称为 动态重定位

可重定位装入方式可将装入模块装入到内存中任何允许的位置。
但是该方式并不允许程序运行时在内存中移动位置
因为程序在内存中的移动意味着其物理地址发生改变,此时必须对程序和数据的地址(绝对地址)修改。

实际中一个程序可能在内存中的位置经常发生改变。
此时应该使用动态运行时装入方式

动态运行时的装入程序把装入模块装入内存后,并不立即将装入模块的逻辑地址转换为物理地址,而是将地址转换推迟到程序真正执行时才进行。
因此装入内存后的所有地址仍是逻辑地址。

但是每次程序运行时才进行地址转换,会不会影响程序的指令速度呢?
这就需要用到重定位寄存器的支持。

程序链接

前面在介绍程序的装入时,是以一种特殊情况,即只有一个目标模块,此时这个目标模块就是装入模块。
在这里插入图片描述

但是一般源程序经过编译后,得到的是一组目标模块.
在这里插入图片描述

链接程序的功能是将这组目标模块以及它们所需要的库函数装配成一个完整的装入模块

根据进行链接的时间不同,可以将链接 分成如下三种。

静态链接

(Static Linking)
程序运行前,先将各模块及它们所需的库函数链接成一个完整的可执行程序,以后不再拆开。

装入时动态链接

(Load-time Dynamic Linking)
将用户的源程序编译后所得到的一组目标模块,在装入内存时,采用边装入,边链接的方式
优点:

  • 便于修改和更新
    采用动态链接方式,各目标模块时分开存放的,修改或更新各目标模块是很简单的事。
  • 便于实现对目标模块的共享

运行时动态链接

(Run-time Dynamic Linking)
对某些目标模块的链接,是在程序执行中所需要该目标模块时,才对它进行的链接。

连续分配存储管理方式

程序在装入内存时,必须要现有一定大小的内存空间才能成功装入内存。
连续分配方式为一个用户分配一个连续的内存空间,即程序中代码或数据的逻辑地址相邻体现在内存空间分配时物理地址的相邻。

连续分配方式可分为四类:

  • 单一连续分配
  • 固定分区分配
  • 动态分区分配
  • 动态可重定位分区分配

单一连续分配

内存在此种方式下分为系统区用户区,系统区仅提供给操作系统使用,用户区为用户提供。

这种方式无需进行内存保护,内存中永远只有一道程序,不会因访问越界而干扰其它程序

固定分区分配

划分分区

固定分区分配是最简单的一种多道程序存储管理方式,
将用户内存空间划分为若干个固定大小的区域,每个分区只装入一道作业
划分分区方法有两种:
在这里插入图片描述

  • 分区大小相等
    用于利用一台计算机去控制多个相同对象的场合,但是缺乏灵活性

  • 分区大小不等
    划分为含有多个较小的分区,适量的中等分区,少量的大分区

内存分配

为便于内存分配,常将分区按其大小进行排队,并为之建立一张分区使用表,其中各表项包括每个分区的起始地址,大小及状态(是否已分配)
在这里插入图片描述
当有一个用户程序要装入时,有内存分配程序依据用户程序的大小检索该表,从中找出一个能满足要求的,尚未分配的分区,将之分配给程序,然后将该表项中的状态置为"已分配"。

若未找到大小足够的分区,则拒绝为该用户分配内存

动态分区分配

动态分区分配又称为可变分区分配

根据进程的实际需要,动态地为进程分配内存空间。

在实现动态分区分配时,会涉及到三方面的问题:

  • 分区分配中所用的数据结构
  • 分区分配算法
  • 分区的分配与回收

动态分区分配中的数据结构

实现动态分区分配需要配置相应的数据结构描述空闲分区和已分配分区的情况,为分配提供依据。

常用的数据结构有:

  • ⓵ 空闲分区表
    用于记录每个空闲分区的情况,每个空闲分区占一个表目。
    表目中包括分区号,分区大小,分区始址,分区状态
    在这里插入图片描述

  • ⓶ 空闲分区链
    为实现对空闲分区的分配和链接,在每个分区的起始部分设置一些用于控制分区分配的信息,以及用于链接各分区所用的前向指针,在分区尾部设置一后向指针。
    在这里插入图片描述

    通过前,后向链接指针,可将所有的空闲分区链接成一个双向链。
    为检索方便,在分区尾部重复设置状态位和分区大小表目。
    当分区被分配出去以后,把状态位由"0"改为"1",此时前后向指针已无意义。

动态分区分配算法

将新作业装入内存时,需要按照一定的算法,从空闲分区表或者空闲分区链空选出一个分区分配给改作业。

这一节比较多,放在后面介绍

分区分配操作

动态分区存储管理方式中,主要的操作时分配内存和回收内存

  1. 分配内存
    在这里插入图片描述

  2. 回收内存
    进程运行完毕释放内存时,系统根据回收区的首址,从空闲区链(表)中找到相应的插入点,此时可能出现下面四种情况之一:

  • 回收区与插入点的前一个空闲分区F1 相邻接,
    此时将回收区与插入点的前一分区合并,不必为回收区分配新表项,只需修改前一分区F1的大小。 在这里插入图片描述

  • 回收分区与插入点的后一空闲分区相邻接
    此时可将两分区合并,形成新的空间分区,但用回收区的首址作为新空闲区的首址,大小为两者之和。
    在这里插入图片描述

  • 回收区同时与插入点的前后两个分区邻接
    此时将三个分区合并,使用F1的表项和F1的首址,取消F2的表项,大小位三者之和。
    在这里插入图片描述

  • 回收区既不与F1邻接,也不与F2邻接。
    这时为回收区单独建立一个新表项,填写回收区的首址和大小,并根据其首址插入到空闲链中的适当位置。
    在这里插入图片描述

动态可重定位分区分配

在这里插入图片描述
地址变换过程是在程序执行期间,随着对每条指令或数据的访问自动进行的。称为动态重定位

在这里插入图片描述

动态分区分配算法

基于顺序搜索的动态分区分配算法

为了实现动态分区分配,通常将系统中的空闲分区链接成一个链。
所谓顺序搜索,就是依次搜索空闲分区链上的空闲分区,去寻找一个其大小能满足要求的分区。
基于顺序搜索的动态分区分配算法有如下四种

  • 首次适应(First Fit, FF)算法
    空闲分区以地址递增的次序链接,
    分配内存时顺序查找,找到大小能满足要求的第一个空闲分区

  • 循环首次适应(Next Fit, NF)算法
    又称邻近适应算法,由首次适应算法演变而成。
    不同之处在于分配内存时从上次查找结束的位置开始继续查找

  • 最佳适应(Best Fit, BF)算法
    空闲分区按容量递增形成分区链,找到第一个能满足要求的空闲分区。

  • 最坏适应(Worst Fit)算法
    又称最大适应算法
    空闲分区以容量递减的次序链接,找到第一个能满足要求的空闲分区,即挑出最大的分区

在这几种算法中首次适应算法是最简单,通常也是最快和最好的。
不过首次适应算法会使得内存中的低地址部分出现很多小的空闲分区,每次分配查找时,都要经过这些分区,增加了查找的开销。

基于索引搜索的动态分区分配算法

当系统很大时,内存分区可能会很多,相应的空闲分区链就很长,这时采用顺序搜索分区方法可能会很慢,为提高搜索空闲分区的速度,可采用基于索引搜索的动态分区分配算法,目前常用的有:

  • 快速适应算法
  • 伙伴系统
  • 哈希算法

有兴趣的小伙伴可以上网查找资料,这里不再仔细介绍。

常见混淆概念

物理地址 和逻辑地址的区别

  • 物理地址:加载到内存地址寄存器中的地址,内存单元的真正地址。在前端总线上传输的内存地址都是物理内存地址,编号从0开始一直到可用物理内存的最高端。这些数字被北桥(Nortbridge chip)映射到实际的内存条上。物理地址是明确的、最终用在总线上的编号,不必转换,不必分页,也没有特权级检查(no translation, no paging, no privilege checks)。

  • 逻辑地址:CPU所生成的地址。逻辑地址是内部和编程使用的、并不唯一。例如,你在进行C语言指针编程中,可以读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址(偏移地址),不和绝对物理地址相干。

我的简单理解:一次考试,全校1000个学生,我考了第999名,班级倒数第二。
这里的全校排名999名就是物理地址。
而班级排名倒数第二就是逻辑地址,是相对于这个班级而言的。如果你的成绩放在另外一个班里,可能就是倒数第一。但是无论你在哪个班,全校排名即物理地址都不会改变。

外部碎片和内部碎片

外部碎片:分区外的空间浪费
内部碎片:分区内的空间浪费

猜你喜欢

转载自blog.csdn.net/weixin_45468845/article/details/107711284