连续分配方式,是指为一个用户程序分配一个连续的内存空间。
------- 动态分区分配:又称为可变分区分配,是根据进程的实际需要动态地为之分配内存空间,使分区的大小刚好与作业的大小相等。
动态分区分配并不预先将内存划分成一块块的分区,而是在作业进入内存时,根据作业的大小动态地建立分区,并使分区的大小正好适应作业
的需要,因此系统中分区的大小是可变的,分区的数目也是可变的。
在实现可变分区分配时,将涉及到分区分配中所用的数据结构、分区分配算法和分区的分配和回收操作三个问题。
1、分区分配中的数据结构
---- 与固定分区分配类似,动态分区分配也使用分区说明表来记录内存中各分区的使用情况,除此之外,为了便于分区的分配,系统中必须配置
相应的数据结构,用来描述空闲分区和已分配分区的情况,为分配提供依据。
常用的数据结构有以下两种形式:
---- 空闲分区表
在系统中设置一张空闲分区表,用于记录每个空闲分区的情况。
每个空闲分区占一个表目,表目中包括分区序号、分区始址及分区的大小等数据项。
---- 空闲分区链
为了实现对空闲分区的分配和链接,在每个分区的起始部分,设置一些用于控制分区分配的信息,以及用于链接各分区的前向指针;
在分区尾部则设置一后向指针,通过前、后向链接指针,可将所有的空闲分区链接成一个双向链。
为了检索方便,在分区尾部重复设置状态位和分区大小表目。当分区被分配出去以后,把状态位由“0”变为“1”,此时,前、后向指针已无意义。
2、分区分配算法
---- 为把一个新作业装入内存,需按照一定的分配算法,从空闲分区表或空闲分区链中选出一分区分配给该作业。下面是5种分配算法。
---- 首次适应算法(first fit)
以空闲分区链为例,FF算法要求空闲分区链以地址递增的次序链接。在分配内存时,从链首开始顺序查找,直至找到一个大小能满足要求的空闲
分区为止;(空闲分区大小>作业大小)然后再根据作业的大小,从该分区中划出一块内存空间分配给请求者,余下的空闲分区仍留在空闲链中。
若从链首至链尾都不能找到一个能满足要求的分区,则此次内存分配失败,返回。
-- 优点:优先利用内存中低址部分的空闲分区,保留了高址部分的大空闲区,无内部碎片。为以后到达的大作业分配大的内存空间创造了条件。
-- 缺点:低址部分不断被划分,会留下许多难以利用的、很小的空闲分区,存在外部碎片。
每次查找都是从低址部分开始,这会增加查找可用空闲分区的开销。
-- 要求:空闲分区表(空闲分区链)按地址从低到高排列。
---- 循环首次适应算法(next fit)
由FF算法演变而成的。在为进程分配内存空间时,不再是每次都从链首开始查找,而是从上次找到的空闲分区的下一个空闲分区开始查找,直至找
到一个能满足要求的空闲分区,从中划出一块与请求大小相等的内存空间分配给作业。
为实现该算法,应设置一起始查询指针,用于指示下一次起始查询的空闲分区,并采用循环查找方式,即如果最后一个(链尾)空闲分区的大小仍
不能满足要求,则应返回到第一个空闲分区,继续比较。找到后,应调整起始查询指针。
-- 优点:使内存中的空闲分区分布的更均匀,从而减少了查找空闲分区的开销,无内部碎片。
-- 缺点:会缺乏大的空闲分区,有外部碎片。
-- 要求:空闲分区表(空闲分区链)按地址从低到高排列。
---- 最佳适应算法(best fit)
所谓“最佳”是指每次为作业分配内存时,总是把能满足要求、又是最小的空闲分区分配给作业,避免“大材小用”。
为了加速查找,该算法要求将所有的空闲分区按其容量以从小到大的顺序形成一空闲分区链。
这样,第一次找到的能满足要求的空闲区,必然是最佳的。(似乎是最佳的,宏观上却不一定)
因为每次分配后所切割下来的剩余部分总是最小的,在存储器中会留下许多难以利用的小空闲区。
-- 优点:保留了大空闲区,无内部碎片。
-- 缺点:产生许多难以利用的小空闲区,仍有外部碎片。
-- 要求:按容量从小到大排列。
---- 最坏适应算法(worst fit)
该算法要扫描整个空闲分区表或链表,总是挑选一个最大的空闲区分割给作业使用,其优点是可使剩下的空闲区不至于太小,产生碎片的
几率最小,对中、小作业有利,同时最坏适应分配算法查找效率很高。
-- 要求:将所有的空闲分区按其容量以从大到小的顺序形成一空闲分区链,查找时只要看第一个分区能否满足作业要求。
-- 优点:剩下的空闲区不会太小,便于下次使用,无内部碎片。
-- 缺点:使存储器中缺乏大的空闲分区,有外部碎片。
-- 最坏适应算法和首次适应算法、循环首次适应算法、最佳适应算法一起,称为“顺序搜索法”。
---- 快速适应算法(quick fit)
该算法又称为分类搜索法,是将空闲分区根据其容量大小进行分类,对于每一类具有相同容量的所有空闲分区,单独设立一个空闲分区链表。
因此,系统中存在多个空闲分区链表,同时在内存中设立一张管理索引表,该表的每一个表项对应了一种空闲分区类型,并记录了该类型
空闲分区链表表头的指针。空闲分区的分类是根据进程常用的空间大小进行划分,如2KB、4KB、8KB等,对于其他大小的分区,如7KB这样的
空闲分区,即可以放在8KB的链表中,也可以放在一个特殊的空闲区链表中。
-- 优点:查找效率高,仅需要根据进程的长度,寻找到能容纳它的最小空闲区链表,并取下第一块进行分配即可。
该算法在进行空闲分区分配时,不会对任何分区产生分割,能保留大的分区,不会产生内存碎片。
-- 缺点:分区归还主存时算法复杂,系统开销较大。此外,该算法在分配空闲分区时是以进程为单位,一个分区只属于一个进程,因此在为进程
所分配的一个分区中,存在一定的浪费。空闲分区划分越细,浪费越严重,典型的以空间换时间的做法。
3、分区分配操作
-- 在动态分区存储管理方式中,主要的操作是分配内存和回收内存。
---- 分配内存
系统应利用某种分配算法,从空闲分区链(表)中找到所需大小的分区。设请求的分区大小为u.size,表中每个空闲分区的大小可表示为m.size。
若m.size-u.size<=size(size是事先规定的不可再切割的剩余分区的大小,m.size>u.size),说明多余部分太小,可不再切割,将整个分区分配给请求者;
否则(多余部分大于size),从该分区中按请求的大小划分出一块内存空间分配出去,余下的部分仍留在空闲分区链(表)中,然后,将分配区的
首址返回给调用者。
---- 回收内存
当进程运行完毕释放内存时,系统根据回收区的地址,从空闲区链(表)中找到相应的插入点,此时可能出现以下4种情况之一:
1)回收区与插入点的前一个空闲分区F1相邻接。此时应将回收区与插入点的前一分区合并,不必为回收分区分配新表项。
只需修改其前一分区F1的大小。F1的大小=F1的大小+回收区的大小(F1的首址是新空闲区的首址)
2)回收分区与插入点的后一空闲分区F2相邻接。此时也可将两分区合并,形成新的空闲分区,但用回收区的首址作为新空闲区的
首址,大小为两者之和。
3)回收区同时与插入点的前、后两个空闲分区相邻接。此时将三个分区合并,使用F1的表项和F1的首址,取消F2的表项,大小为三者之和。
F1的空间大小=F1+回收区+F2 (F1的首址)
4)回收区既不与F1邻接,也不与F2邻接。这时应为回收区单独建立一新表项,填写回收区的首址和大小,并根据其首址插入到空闲链中的适当
位置。