操作系统——分区存储管理
分区存储管理是把主存储器中的用户区作为一个连续区或分成若干个连续区进行管理,每个连续区中可装入一个作业。
多道程序系统一般都采用多个分区的存储管理,具体可分为固定分区和可变分区两种方式。
一、固定分区存储管理
把主存中可分配的用户区域预先划分成若干个连续的分区,每个连续区的大小可以相同,也可以不同。但是,一旦划分好分区之后,主存中分区的个数就固定了,且每个分区的大小也固定不变。这是一种静态分区法。
在固定分区方式管理下,每个分区用来装入一个作业。由于主存中有多个分区,就可同时在每个分区中装入一个作业。所以,这种存储管理方式适用于多道程序系统。
1、主存空间的分配与释放
为了管理主存空间的使用,必须设置一张“主存分配表”(分区说明表),以说明各分区的分配情况。主存分配表中应指出各分区的起始地址和长度,并为每个分区设一个标志位。当标志位为“0”时,表示对应的分区是空闲分区,当标志位为非“0”时,表示对应的分区已被某作业占用。空闲分区可以用来装作业。
当作业队列中有作业要装入主存时,存储管理可采用“顺序分配算法”进行主存空间的分配。
顺序查看主存分配表,找到一个标志为“0”的并且长度大于或等于欲装入作业的地址空间长度的分区,则把此分区分配给该作业,相应表目的标志位改成作业名的标识;若找不到一个这样的空闲分区,则该作业暂时不能装入主存。
主存空间的释放很简单。某作业执行结束后必须归还所占的分区,这时存储管理根据作业名查看主存分配表,找到相应的表目后,把其中的标志位重新置成“0”即可。
2、地址转换
固定分区管理方式下作业的地址转换常采用静态重定位技术。
3、存储保护
固定分区管理方式下只考虑判断其物理地址即可。常采用“界限寄存器对”法。
If 下限地址<=物理地址<=上限地址
Then 继续
Else 产生“越界中断” ,转越界中断的处理子程序
4.内存扩充
采用覆盖技术
5.固定分区的优缺点
优点:实现简单,无外部碎片
缺点:
a.当用户程序太大时,可能所有的分区都不能满足需求,此时不得不采用覆盖技术解决,但这又会降低性能
b.会产生内部碎片,碎片大,存在小分区占用大作业的情况,内存利用率低。
解决办法:采用可变分区存储管理
二、可变分区存储管理
内存管理的可变分区模式,又称变长分区模式、动态分区分配模式。这种分配方式不会预先划分内存分区,而是在进程装入内存时,根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。因此系统分区的大小和数目是可变的。
与固定分区的区别就是:动态的划分分区。
克服固定分区管理的“内碎片”问题。
1.可变分区模式下,刚开始,OS就绪,但任何用户程序未进入内存前整个用户内存区是一大空间。已占用区和空闲分区并不是绝对的。
2.必须有表来记录分区的情况。
3.程序进入内存时的例行工作就是分配空闲区和装入程序,并修改相应的空闲表和已分配区表。
4.一旦一个内存分区被分配给一个进程,该进程可以被装入该块中执行,装入时需重定位。
可变分区分配的数据结构
系统要使用什么样的数据结构来记录内存的使用情况?
可变分区分配算法
把一个新作业装入内存时,需要按照一定的可变分区分配算法,从空闲分区表(或空闲分区链)中选出一个分区分配给该作业。
在可变分区分配方式中,当有很多空闲分区都满足需求时,应该使用哪个分区进行分配?
这里介绍三种可变分区分配算法
最先适应分配算法
算法思想:每次都从低地址开始查找,找到第一个能满足大小的空闲分区。
实现步骤:
空闲区地址由低到高排序
=>1.顺序查找各个空闲区,把第一个找到能容纳申请要求的内存区分配给申请者.(若空闲区比作业长度大,则分割该空闲区。一部分分配给作业一部分空闲。)
=>2.调整相应的空闲分区表和已分配分区表。
评价:性能一般但实现比较简单直接,易于释放时合并相邻空间分区。比较容易的满足大作业的需要。完成一次分配平均需要的搜索次数较大,影响了工作效率。
尽可能地利用存储器中低地址的空闲区,而尽量保存高地址的空闲区。
最佳适应算法
算法思想:由于可变分区分配是一种连续分配方式,为各进程分配的空间必须是连续的一整片区域。因此为了保证当“大进程”到来时能有连续的大片空间,优先使用更小的空闲区。
实现步骤:
空闲分区按容量递增次序链接。每次分配内存时顺序查找空闲分区表,找到大小能够满足要求的第一个空闲分区。
评价:尽可能地保留了较大的空间。 产生大量的不能被使用的很小的空闲区。因此这种方法会产生很多的外部碎片。所以该算法分配效果不一定是最佳的。
尽可能地利用存储器中小的空闲区,而尽量保存大的空闲区。
最坏适应算法
算法思想:为了解决最佳适应算法的问题——即留下太多难以利用的小碎片,可以在每次分配时,优先使用最大的连续空闲区,这样分配后的空闲区就不会太小,更方便使用。
实现步骤:
空闲分区按照容量递减次序链接。每次分配内存时顺序查找空闲分区表,找到大小能满足要求的第一个空闲分区。
评价:分割后产生的空闲区一般仍可以供以后分配使用。工作一段时间后,不能满足大作业对空闲区的请求。
尽可能地利用存储器中大的空闲区。
三种算法的比较:
可变分区内存回收
只比固定分区管理增加了合并相邻空闲区的操作。
主要是为了及时减少“外碎片”,利于今后大作业的到来。
实现回收内存空间,关键是修改空闲分区表和已分配分区表。
回收内存分区时可能会遇到的四种情况:
(a)若释放区R既有上邻空闲区,又有下邻空闲区。将三个空闲区合并成一个大空闲区。
先将R与F2合并记为F2,
再将F2与F1合并记为F1,并将F2从链中删除。
IF (B+H1=C) AND (C+L2=D)
THEN 修改空闲表,分配表。
(b)若释放区R只有上邻空闲区F1。
则只修改空闲区F1大小即可。
IF (D+H2=E)
THEN 修改空闲表,分配表。
(c)只有下邻空闲区
修改空闲区F2的首地址。
F2的大小=F2的大小+R的大小
(d)既无上邻又无下邻空闲区
Else 修改释放区的首地址为空闲区的起始地址
地址转换
动态重定位
分区的存储保护
If 下限地址<=物理地址<=上限地址
Then 继续
Else 产生“越界中断” ,转越界中断的处理子程序
内存扩充
消除了固定分区管理造成的“内碎片”,但是不可避免的在内存空间造成“外碎片”。
采用移动(紧缩)技术。定时的或在内存紧张时,将内存中所有作业移到内存的一端,使其相邻。
经过紧缩后的进程在内存中的位置发生了变化,若不对程序和数据的地址进行修改,在进程就无法运行。
要使其运行,必须进行“动态重定位”
注意:
=》紧缩的时机:
(1)一旦有归还的分区便进行紧缩,系统开销大。
(2)分配算法发现各空闲区不够用,但其和够用时。此法紧缩开销小,更实用。因此,实际的可变分区分配算法比固定分区分配算法主要增加了“紧缩”操作
三、 伙伴系统(buddy system)
伙伴系统可看作固定分区分配和可变分区分配的一种折中方案。
采用伙伴系统时,内存是以2的幂次个字节大小的空闲块为分配单位的。系统初启时,只有1个最大的2的幂次的空闲块,它就是整个可用的内存空间。当1个进程申请内存时,系统就分给它1个大于或等于进程所申请尺寸的最小的2的幂次的空闲块。
例如,某进程提出的50KB的内存请求,将首先被系统向上取整,转化为对1个64KB的空闲块的请求。如果此时不存在如此大小的空闲块,则此时可获得的1个最小的比该空闲块大的空闲块将被对分成2个“伙伴”单位。对其中1个伙伴单位的对分过程可能还要继续下去,直到获得1个64KB的空闲块为止。
伙伴系统的内存释放算法考虑将两个伙伴单位合并成1个大1倍的空闲单位。且这个合并过程会递归下去,直到不能继续合并为止。
注意观察下图,合并后的空闲区块也必须是2的幂次个字节
为了实现伙伴系统,系统要为每一种可能的空闲块维护1个空闲块链表。设系统管理的可用内存空间共为2N个字节,则1个伙伴系统最多需要维护N个空闲块链表。由于每种尺寸的空闲块都有一个空闲块队列,因此内存的分配与释放可以有效地进行。
伙伴系统的最大缺陷是不能有效地利用内存,特别是内碎片严重。例如,1个257KB的进程需要占用1个512KB的分配单位,其中将产生255KB的内碎片。另外,每次释放内存时都尽可能地合并伙伴单位的做法也会降低系统性能,因为刚合并好的块可能马上又要对分。一种改进的做法是延迟合并的时机。如今Linux用它。