1、数据结构进阶一动态存储管理概念

来源:互联网 发布:淘宝怎样追加二次评论 编辑:程序博客网 时间:2024/06/06 20:48

1、数据结构进阶一动态存储管理概念

           在之前的笔记中,我们学习了数据结构的一些重要概念以及简单实现。很多代码都是摘自网络的,不过都亲测可用的并附以解释了。

           这篇开始还是学习数据结构,不过类似数据结构笔记的下篇。


           “一个精神生活很充实的人,一定是颐和园很有理想的人,一定是一个很高尚的人,一定是一个只做物质的主人而不做物质的奴隶的人。--陶铸”

 

1.  动态存储管理

存储管理是操作系统的重要组成部分,它负责管理计算机系统的存储器。

动态存储管理的基本问题是系统如何应用户提出的“请求”分配内存?又如何收回那些用户不再使用而释放的内存以备新的“请求”产生时重新进行分配。

So,基本问题:系统如何按用户的要求分配内存;当用户使用完毕,系统如何回收内存。

一些概念如下:

“占用块”:分配给用户使用的地址连续的内存区。

“空闲块”:未曾分配的地址连续的内存区,也称“可利用空间块”

可利用空间表:把可利用空间表看做是一个“存储池”,它有以下三种不同的结构形式:

第一种情况是系统运行期间所有用户请求分配的存储量大小相同 ;

第二种情况是系统运行期间用户请求分配的存储量有几种大小的规格 ;

第三种情况,系统在运行期间分配给用户的内存块大小不固定 。

  动态存储分配过程中的内存状态如下图1


a)   系统运行初期

b)   系统运行一段时间以后

           OK,此时又有新的用户作业进入系统请求分配内存,系统将如何处理?

           通常有两种做法:一种策略是系统继续从高地址的空闲块中进行分配,而不理会已分配给用户的内存是否已空闲,直到分配无法进行

           另一种策略是用户程序一旦运行结束,便将它所占内存区释放成为空闲块,同时,每当新的用户请求分配内存时,系统需要巡视整个内存区中所有空闲块,并从中找出一个“合适”的空闲块分配之。

           第二种策略需要实现,系统需建立一张记录所有空闲块的可利用空间表。此表的结构可以是目录表也可以是链表。

如下图2

 

2.  可利用空间表及分配方法

2.1      可利用空间表

将所有内存空闲块用链表连接而成;

空闲块的大小可以是全相同的,也可以是分成若干固定大小的,还可以是随机大小的。

 

2.2      分配方法

2.2.1          首次拟合法

分配找到的第一个不小于n的空闲块的一部分。

操作方便,查找快捷;

2.2.2          最佳拟合法

分配不小于n且最接近n的空闲块的一部分。

尽可能将大的空闲块留给大程序使用;

2.2.3          最坏拟合法

分配不小于n且是最大的空闲块的一部分。

尽可能减少分配后无用碎片;

2.3      边界标识法

边界标识法是操作系统中用以进行动态分区分配的一种存储管理方法,介绍的第三种情况,即用户请求的内存块大小不固定,随不同的请求而变化。系统将所有的空闲块链接在一个双重循环链表结构的可利用空间表中;分配可按最先适应分配算法进行,也可按最优适应分配算法进行。系统的特点在于:在每个内存区的头部和底部两个边界上分别设有标识,以识别该区域为占有块或空闲块,使得在回收用户释放的空闲块时容易判别在物理位置上与其相邻的内存区域是否为空闲块,以便将所有地址连续的空闲存储区组合成一个尽可能大的空闲块。

如下所示它表示一个空闲块。整个结点由三部分组成。其中space为一段地址连续的存储单元,是可以分配给用户的内存区域,它的大小保存在head中的size域中。它以头部head和底部foot作为它的两个边界;在head和foot中分别设有标志域tag,且设定空闲块中tag的值为“0”,占用块中tag的值为“1”;foot位于结点底部,因此它的地址是随结点中space空间的大小而变的。

 

 

2.3.1          分配算法

最先适应分配算法来说明边界标识法的应用。实现时,可以从表头指针pav所指的第一个结点开始进行查询,找到第一个容量不小于请求分配的存储量的空闲块即可进行分配。为了使整个系统更有效地运行,在边界标识法中可做如下两条约定:

(1)假设找到的某个待分配的空闲块的容量为m个字,若每次只从中分配n(n<m)个字给用户作业,则剩余m-n个字大小的空闲块结点仍留在链表中。如此进行多次分配之后,链表中会出现一些容量极小称之为“碎片”的空闲块,这样将大大减慢分配(查找)的速度。为克服这一弊端,可以选定一个适当的容量e,当m-n<e时,就将容量为m的空闲块整块分配给用户作业;反之,只分配其中n个字的内存块。同时,为了避免修改指针,在分配部分空间时约定将结点中的高地址部分分配给用户。

(2)按照最先适应分配策略,每次在分配存储块时总是从表头指针pav所指的结点开始进行查找,找到第一不小于n的空闲块即进行分配。但是,由于每次总是从同一个结点开始查找,必然造成存储容量小的结点集中在链表的前端,这同样会增加查找较大空闲块的时间。因此,在每次分配完成之后,令指针pav指向刚进行分配的结点的后继结点,这就是为何将可利用空间表组织成循环链表的原因。

2.3.2          收回算法

若释放块的左、右邻区均为占用块,则处理最为简单,只要将此新的空闲块作为一个结点插入到可利用空间表中即可;若只有左邻区是空闲块,则应将回收块与其左邻区合并成一个结点;若只有右邻区是空闲块,则应将回收块与其右邻区合并成一个结点;若左、右邻区都是空闲块,则应将三块合起来成为一个结点留在可利用空间表中。

2.3.3          问题

查找适合需要的块,需要较多的时间

查找适合需要的块的策略(最先/最佳/最坏),每种都有缺陷

碎片问题

 

 

 

 

2.4      伙伴系统

起始地址为p大小为2k的内存块,其伙伴块的起始地址:

 

例:空闲区的大小为210=1024,地址从0到1023。

如果一个块的大小为28,起始地址为512,则它的伙伴块的起始地址为768。

如果大小为27,起始地址为384,则伙伴块的起始地址为256。

 

 

2.4.1          分配算法

若2k-1<n<=2k-1,且第k+1个子表不空,则删除此链表中一个结点,分配给用户。

若2k-2<n<=2k-1-1,由于大小为2k的子表为空,则从取出一个结点,将一半分给用户,另一半插到大小为2k-1的子表中。

 

 

 

 

2.4.2          回收算法

只有伙伴才合并,并将合并后的新空闲块加入上一级大小的空闲块链表中。

回收两块首地址分别为768及128,大小为27的存储块,请画出回收后该伙伴系统的状态图。


因为768 % 27+1=0,所以768和768+27=896互为伙伴, 伙伴合并后,首址为768,块大小为28。因为768 % 28+1=28,所以,所以首址768大小为28的块和首址512大小为28的块合并,成为首址512大小为29的空闲块。因为128 %27+1=27,其伙伴地址为128-27=0, 将其插入可利用空间表中。

2.5      无用单元的收集

 

可利用空间表虽然方便地实现了存储空间的动态管理,但它的主要特点是应用户的请求而分配内存,在用户释放存储空间时进行回收。因此,在这类存储管理系统中,用户必须明确给出“请求”和“释放”的信息。但用户难免在某些时候会因为疏漏或其它原因致使系统没有进行回收而产生“无用单元”的问题。此处,“无用单元”指的是那些用户不再使用而系统又没有回收的结构或变量。

另外,由于数据结构本身的原因也有可能造成无用单元的产生,如广义表中存在着共享和递归成份,对共享结点来说,当该结点从某一条链上删除时它可能还链接在别的关系中,并不能立即释放该结点的空间,所以只有在全部链接关系中都被删除时,该结点才是无用结点,该结点所占用的空间才成为无用单元可以回收。

因此,要及时释放共享结点所占的空间,必须给每个结点增设一个共享计数器,记录本结点被几个链共享,当结点从某一链中被删除时,就将此计数器减1,反之在插入时计数器加1,一旦该计数器被减到0时,说明该结点在结构中不再有用,便可以回收。

这种处理方法的缺点是增加了额外的存储开销,同时也使程序的处理变得更加复杂。有时少量无用单元的存在并不会影响系统的正常运行,但是当无用单元积累到一定阶段,就要求系统去找出这些无用单元,并把它们送回到可利用空间表中去。在这里关键的问题是如何从整个存储空间中找出那些无用单元。

“标志”算法通过周游广义表给每个有用结点记上标志,相应地所有无用结点由于不带标志,因此只要扫描一遍内存就可通过标志判断哪些是有用结点,哪些是无用结点,这样便可将所有的无用单元收回至可利用空间表。

 

2.6      存储压缩

在动态存储管理中,由于每个用户作业所需的存储空间大小不尽相同,所以当系统运行一段时间后必然导致可利用空间表中存在一些容量很小的内存块,这些内存块分布在内存中的不同区域,它们很难再次被作业利用(在操作系统中这种小块内存被称为“碎片”),这大大降低了存储空间的利用率。“存储压缩”是将所有的空闲块移到一个连续的存储区,从而使可利用空间形成一个连续的存储块,这样可以满足大作业的需求,提高系统的利用率。

实施“存储压缩“通常有两种做法,一种是一旦有用户释放存储块即进行回收压缩;另一种是在程序执行过程中不回收用户随时释放的存储块,而是在可利用空间不够分配或在进行无用单元的收集时进行“存储压缩”。

如下图 所示:


存储压缩的过程比较复杂,不仅要改变结点的物理地址,同时还要修改结点中的全部指针。实际上,存储压缩是有条件的,当某个有用块中的程序正在执行与外设相关的输入输出操作时不能进行程序的移动(具体的原因在操作系统相关课程中学习)。此处,可以通过以下三个步骤来实现存储压缩:

(1)给有用结点分配新地址;

(2)修改有用结点的指针值;

(3)将有用结点移动到新分配的位置。

 

原创粉丝点击