动态分区分配 (Dynamic Partitioning) 概述
背景: 在可变分区存储管理中,内存空间被划分为若干个大小不等的分区。当一个新作业请求内存时,系统需要从空闲分区表或空闲分区链中,按照某种算法选择一个大小合适的空闲分区分配给它。
核心问题: 选择哪个空闲分区?不同的选择策略,会直接影响内存的利用率(即内存碎片问题)和分配效率(即查找开销)。
内存碎片:
-
内部碎片 (Internal Fragmentation): 分配给作业的分区大于作业实际所需,分区内部多余的空间无法被利用。
-
外部碎片 (External Fragmentation): 内存中存在许多不连续的、细小的空闲分区,它们的总和可能很大,但由于不连续,无法满足一个较大作业的内存请求。动态分区算法主要产生外部碎片。
1. 基于顺序搜索的分配算法 (Sequential Search Algorithms)
这类算法的核心思想是,当作业请求内存时,系统按顺序遍历一个记录了所有空闲分区的链表,直到找到一个满足条件的分区为止。
1.1 首次适应算法 (First Fit, FF)
-
算法思想: 从空闲分区链的链首开始查找,将第一个满足大小要求的空闲分区分配给作业。
-
实现: 分配后,如果该分区大于作业请求,剩下的部分仍然作为一个新的空闲分区保留在链中。
-
图示举例:
-
空闲分区链:
[100K, 地址1000] -> [20K, 地址3000] -> [300K, 地址5000] -
作业请求:
80K -
过程:
-
检查第一个分区
100K。100K >= 80K,满足条件。 -
分配:从
100K的分区中划出80K给作业。 -
更新:原分区变为
20K,空闲分区链更新为[20K, 地址1000] -> [20K, 地址3000] -> [300K, 地址5000]。
-
-
-
优点:
-
算法简单,开销小。
-
倾向于保留内存高地址部分的大空闲区,有利于满足后续对大内存的请求。
-
-
缺点:
-
随着时间推移,内存低地址部分会被不断切割,产生大量难以利用的小碎片。
-
每次查找都从头开始,增加了查找开销。
-
1.2 循环首次适应算法 (Next Fit, NF)
-
算法思想: 它是首次适应算法的变种。不再每次都从链首开始查找,而是从上一次查找结束的位置开始继续向后查找。查找到链尾时,再回到链首。
-
优点:
-
查找开销相比FF有所降低,不需要每次都从头开始。
-
空闲分区的分布更加均匀,避免了FF算法中低地址区域碎片集中的问题。
-
-
缺点:
- 由于不再优先使用低地址的小分区,导致缺乏大的空闲分区。当有大作业请求时,可能无法满足。
1.3 最佳适应算法 (Best Fit, BF)
-
算法思想: 遍历整个空闲分区链,找到一个与作业请求大小最接近且不小于它的空闲分区进行分配。
-
实现: 为了避免切割后剩下太小的、几乎无法使用的分区,可以规定当剩余部分小于某个阈值时,将整个分区全部分配(产生少量内部碎片以避免外部碎片)。
-
图示举例:
-
空闲分区链:
[100K] -> [90K] -> [300K] -
作业请求:
80K -
过程:
-
遍历整个链表,找到所有满足条件的分区:
100K,90K,300K。 -
比较大小差值:
100-80=20,90-80=10,300-80=220。 -
选择差值最小的,即
90K的分区进行分配。
-
-
-
优点:
- 能最大限度地保留大分区,提高了满足大作业请求的概率。
-
缺点:
-
每次都需要扫描整个链表,算法开销大。
-
非常容易产生大量极小的、难以利用的外部碎片。这是它的致命弱点。
-
1.4 最坏适应算法 (Worst Fit, WF)
-
算法思想: 与最佳适应相反,它遍历整个空闲分区链,总是挑选一个最大的空闲分区进行分配。
-
逻辑: 寄希望于分配后剩下的空闲区仍然足够大,可以满足其他作业的需求。
-
图示举例:
-
空闲分区链:
[100K] -> [90K] -> [300K] -
作业请求:
80K -
过程:
-
遍历整个链表,找到最大的分区
300K。 -
从
300K的分区中划分80K给作业,剩下220K。
-
-
-
优点:
- 可以减少小碎片的产生。从某种程度上说,它对中小作业更有利。
-
缺点:
-
同样需要扫描整个链表,开销大。
-
会迅速消耗掉大的空闲分区,导致后续若有大作业请求,系统将无能为力。
-
在实践中,性能通常不佳。
-
2. 基于数据结构的快速查找算法
这类算法通过特殊的数据结构来组织空闲分区,从而避免了线性扫描,提高了查找效率。
2.1 快速适应算法 (Quick Fit) / 分离适配 (Segregated Fit)
-
算法思想: 将空闲分区按其容量大小进行分类,为每一类大小都建立一个单独的空闲分区链表。
-
实现:
-
系统中有多个空闲分区链表,如一个链表专门存放大小为8K的分区,另一个存放12K的,等等。
-
当作业请求
size大小的内存时,系统直接去管理大小为size(或稍大于size) 的链表中查找。 -
如果该链表不为空,直接取下第一块分配即可,查找效率极高 (O(1))。
-
如果该链表为空,则去更大尺寸的链表中找一个分区,切割后一部分用于分配,另一部分(如果有)插入到对应大小的空闲链表中。
-
-
优点:
-
查找效率非常高,分配快速。
-
不涉及分区的合并和分割(除非在更大链表中切割),实现简单。
-
-
缺点:
-
在设计上,如何确定分区的尺寸和类别是个难题。
-
分区回收时,算法复杂(需要考虑合并问题)。
-
本质上是以空间换时间,会加剧内存碎片的产生。
-
2.2 伙伴系统 (Buddy System)
-
算法思想: 一种经典的、介于上述两者之间的方案。它规定所有分区的大小都必须是 2的幂。
-
分配过程:
-
假设作业请求
size大小的内存。系统计算出满足该请求的最小2的幂2^k(即2^(k-1) < size <= 2^k)。 -
系统检查大小为
2^k的空闲链表。 -
如果链表中有空闲分区,则直接分配。
-
如果链表为空,则去查找更大一级的链表,即
2^(k+1)。如果该链表不为空,则取出一个分区,将其对半分裂成两个大小为2^k的“伙伴”。一个用于分配,另一个挂入2^k的空闲链表。 -
如果
2^(k+1)的链表也为空,则继续向上查找,递归地进行分裂。
-
-
回收过程:
-
当一个大小为
2^k的分区被释放时,系统会检查其**“伙伴”分区**是否也空闲。 -
如果伙伴也空闲,则将两者合并成一个大小为
2^(k+1)的更大分区。 -
合并后的分区,继续尝试与它的伙伴合并,直到无法合并为止。
-
-
优点:
-
查找和分裂合并的速度很快,因为伙伴的地址可以通过简单的位运算得出。
-
能有效地减少外部碎片。
-
-
缺点:
- 由于分区大小固定为2的幂,内部碎片问题严重。例如,一个需要33K内存的作业,必须分配一个64K的分区,浪费了31K。
总结与考点分析
| 算法 | 优点 | 缺点 | 碎片情况 |
|---|---|---|---|
| 首次适应(FF) | 简单,高地址保留大块 | 低地址碎片多,查找有开销 | 外部碎片 |
| 循环首次(NF) | 分布均匀,查找开销相对小 | 缺乏大块空闲区 | 外部碎片 |
| 最佳适应(BF) | 保留大块 | 开销大,易产生大量极小的外部碎片 | 外部碎片 |
| 最坏适应(WF) | 减少小碎片 | 开销大,迅速消耗大块,性能通常不佳 | 外部碎片 |
| 快速适应(QF) | 查找极快,分配简单 | 空间浪费,回收复杂 | 外部碎片为主 |
| 伙伴系统 | 回收合并快,外部碎片少 | 内部碎片严重,算法实现相对复杂 | 内部碎片是主要问题 |