动态分区分配是操作系统内存管理中可变分区存储管理方式的核心机制,属于连续分配方式的一种。与固定分区分配预先将内存划分为大小固定的若干个区域不同,动态分区分配在作业(进程)装入内存时,根据作业实际需要的空间大小动态地建立分区,并使分区的大小正好适应作业的需要。这种按需划分、按需建立的特性,是动态分区分配区别于其他连续分配方式的根本标志,也是它的名称中"动态"二字的由来。
在软考考纲中,动态分区分配通常被归入"存储管理"这一章,与分页存储管理、分段存储管理、段页式存储管理并列。教材的标准表述是:动态分区分配又称可变分区分配,它是根据进程的实际需要,动态地为之分配内存空间。这一表述包含三个关键约束。其一,分配发生在进程装入阶段,由操作系统的内存分配程序负责执行,而非在系统启动时预先完成;其二,分区大小是可变的,不再预先固定,每个分区的边界都随着装入进程的大小而变化;其三,分区之间必须保持连续,因此动态分区分配仍然是一种连续分配方式,进程的整个逻辑地址空间必须映射到一段连续的物理内存区间,这一点与分页存储管理的离散分配形成了根本区别。
理解动态分区分配,必须首先厘清它与分页存储管理的本质差异。分页存储管理把进程的地址空间和物理内存都划分为固定大小的页框(帧),进程可以离散地占用多个不连续的页框,通过页表建立逻辑页到物理页框的映射,从而彻底避免了外部碎片问题,代价是引入了页表的额外存储开销和地址变换的时间损耗。而动态分区分配坚持连续分配,进程占用的是整块连续内存,因此无需页表这类映射结构,地址变换相对简单,但代价是会产生外部碎片——那些因为太小而无法被任何进程利用的空闲分区。这一对"连续性与碎片化"的矛盾,是贯穿动态分区分配全章的主线,也是命题人最常考察的分界点。
从发展脉络看,动态分区分配是最早出现的多道程序内存管理技术之一。早期单道批处理系统采用单一连续分配,整个内存被分为操作系统区和用户区,任何时刻只允许一个用户进程驻留内存,内存利用率极低。随着多道程序设计的引入,人们首先想到固定分区,但它存在"内碎片"严重和分区数量限制两个缺陷。动态分区分配正是为了克服固定分区的僵硬性而提出的改进方案,它不再预先划定分区边界,而是让内存中的空闲区域随着进程的装入和退出动态地分裂与合并。这一思想后来虽然被分页和分段技术所取代,但作为理解内存管理演进逻辑的起点,它在软考中始终保持着稳定的出题频率。
需要特别强调的是,"动态分区"中的"动态"二字有两层含义,考生务必区分清楚。第一层含义是分区的划分时机是动态的,即在进程装入时才确定分区大小,而非系统启动时预先划分;第二层含义是分区的边界是动态变化的,进程退出后释放的分区可能与相邻空闲分区合并,也可能被后续进程重新分割。这两层含义分别对应了"分配"和"回收"两个环节,而动态分区分配算法的研究重点,恰恰集中在分配环节——即当有多个空闲分区可供选择时,究竟应该把进程装入哪一个分区。
动态分区分配算法要解决的核心问题是:当系统收到一个大小为 size 的内存请求时,如何在若干空闲分区中挑选一个进行分配。要完成这个挑选动作,系统首先必须掌握内存中所有空闲分区的信息,这就涉及到空闲分区的数据组织问题。软考考纲要求掌握两种组织方式:空闲分区表和空闲分区链。
空闲分区表是一种静态的数组结构。系统在内存中设置一张表,每一行记录一个空闲分区的起始地址、分区大小和状态。表项的数目决定了系统最多能跟踪多少个空闲分区,通常是一个固定的上限。当进程装入时,系统按某种算法在表中查找一个足够大的空闲分区;当进程退出时,系统将释放的分区登记到表中,并检查是否有相邻空闲分区需要合并。空闲分区表的优点是结构简单、易于实现,缺点是表的大小固定,当空闲分区数量超过表项上限时,无法再登记新的空闲分区,系统会失去对这些分区的跟踪能力。
空闲分区链则是一种动态的链表结构。系统在每个空闲分区的起始处设置一个链表节点,节点中包含该分区的长度、指向下一个空闲分区的指针等信息,所有空闲分区通过指针串成一条链。与空闲分区表相比,空闲分区链的节点数量可以动态增减,不受固定上限的约束,但代价是链表节点本身占用了空闲分区的一部分空间。在软考的表述中,空闲分区链又常被称为"空闲链",它是现代操作系统中更常用的组织方式,因为它天然支持分区的动态分裂与合并。
无论采用表还是链,动态分区分配算法要处理的对象本质上都是一致的——一组已知起始地址和大小的空闲分区集合。四种分配算法(首次适应、循环首次适应、最佳适应、最坏适应)的区别,不在于数据组织的不同,而在于"从这组空闲分区中挑哪一个"的决策策略不同。理解了这一点,就能把算法的记忆从"死记四种名称"升级为"掌握四种挑选规则的差异",从而在题目中灵活应变。
首次适应算法(First Fit,简称 FF)是最简单、最直观的分配策略。其规则是:空闲分区按照地址递增的次序排列,分配程序从链首(即低地址端)开始顺序查找,找到第一个大小能够满足请求的空闲分区,就把它分配给进程,并把剩余部分(如果有)留在空闲链中。
首次适应算法的查找逻辑可以用一句话概括:从低地址往高地址找,碰到第一个装得下的就用。这种"见好就收"的策略带来的一个显著后果是,低地址端的空闲分区被反复分割,产生许多零散的小碎片;而高地址端的空闲分区则相对完整,保留着大块的空闲空间。因此首次适应算法有一个典型特征:空闲分区的分布呈现出"低地址端碎片化、高地址端大块化"的格局。命题人常常利用这一特征设置陷阱,例如要求考生判断"首次适应算法分配后,空闲分区的排列具有什么特征"。
从查找效率看,首次适应算法通常能够较快地完成分配,因为它往往无需遍历整条空闲链就能在低地址端找到合适的空闲分区。但它的缺点是查找的起点固定在链首,每次分配都从低地址端开始扫描,导致低地址端的碎片被越切越细。这一缺陷正是循环首次适应算法要解决的对象。
最佳适应算法(Best Fit,简称 BF)的规则是:把作业装入与它所需大小最接近的空闲分区中。实现方式上,系统把所有空闲分区按照容量从小到大排序,分配时从链首开始查找,找到的第一个能满足要求的空闲分区,就是容量最小的那个能满足要求的空闲分区。最佳适应算法的设计意图是"物尽其用",尽量让每一个分区都恰好满足请求,从而把大块的空闲分区保留下来,供后续较大的请求使用。
然而最佳适应算法有一个著名的副作用:它每次分配后留下的剩余空间往往极小,小到无法再被任何进程利用,从而产生大量无法利用的外部碎片。这些"鸡肋"般的微小碎片堆积起来,反而使内存的可利用率下降。软考真题中关于最佳适应算法的题目,几乎都围绕这个"看似最优、实则碎化"的反直觉结论展开,考生必须牢记:最佳适应算法追求的是"当前分配的最小剩余",但代价是"整体碎片的加剧"。
最坏适应算法(Worst Fit,简称 WF)则反其道而行之,它的规则是:把作业装入到最大的空闲分区中。实现方式上,空闲分区按照容量从大到小排序,分配时从链首(即最大的空闲分区)开始,若它能满足要求就直接分配。最坏适应算法的设计意图是,从最大的分区中切一块给进程,剩下的部分仍然足够大,还能继续满足后续的请求,从而尽量避免产生小碎片。但它的副作用是,大块空闲分区会被迅速分割,当系统中出现较大内存请求时,往往已经没有足够大的空闲分区可以满足,最终导致大作业无法装入。
最佳适应与最坏适应构成了两种对立的取舍:最佳适应"省着用",把大块留给未来,代价是当下产生大量小碎片;最坏适应"放开用",优先动用大块,代价是大块被切碎、后续大请求无区可装。软考常把这两种算法放在同一个题目里对比考查,要求考生根据给定的空闲分区序列和请求序列,分别计算出两种算法下的分配结果,进而辨析它们各自的碎片化后果。
循环首次适应算法(Next Fit,简称 NF)是首次适应算法的一个改进变体。首次适应算法每次分配都从链首开始查找,导致低地址端被反复切割;循环首次适应算法则引入一个"查找指针"(又称起始查寻指针),记录上一次分配结束的位置,下一次分配时从该指针处继续向后查找
本篇完!