(C语言详细版)第八章 动态存储管理

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

早期,由程序员自己完成: 在执行程序之前,先需将用机器语言或汇编语言编写的程序输送到 内存的某个固定区域上,并预先给变量和数据分配好对应的内存地 址(绝对或相对地址 ) 存储管理是操作系统和编译程 有了高级语言后,程序员不需直接和内存地址打交道: 序的一个复杂且重要的问题! 变量对应的内存地址都是由编译程序在编译或执行时进行分配 单用户操作系统:内存空间被划分为系统区(供系统程序使用)和用 户区(供单一的用户程序所使用) 多道程序设计:多个用户程序共享一个内存区域,每个用户程序使 用的内存就由操作系统来进行分配。
第八章 动态存储管理
重点:内存空间的分配与回收算法,以 及可利用空间表的结构。
难点:无用单元收集算法的理解与掌握 。
1/33
第八章 动态存储管理
8.1 概述 8.2 可利用空间表及分配方法
8.3 边界标识法
8.4 伙伴系统
8.5 无用单元收集
8.6 存储紧缩
2/33
8.1 概述(1)

程序中的变量如何存储管理?
17/33


8.4 伙伴系统(1)

伙伴系统(buddy system)

与边界标识法类似 不同点是:在伙伴系统中,无论是占用块或空闲块,其大小均为 2的k次幂(k为某个正整数)

可利用空间表

若可利用内存容量为2m个字,则空闲块的大小只能是20、 21、„、 2m 所有大小相同的空闲块建于一张子表中,每个子表是一个双重循 环链表,可能有m+1个子表 将m+1个表头指针用向量结构组织成一个表

无用单元:那些用户不再使用而系统没有回收的结构和变量。 例: p = malloc(size); …. p=NULL;

悬挂访问:访问已经被释放的结点 例: p = malloc(size); …. q=p; free(p); a = *q;
因结构本身的特性,也会产生上述问题 例如广义表的结构: 共享子表(图8.9 P207)
18/33


8.4 伙伴系统(2)

可利用空间表的结构
tag kval
head llink
space 20 … 2k-1 2k

#define m 16 typedef struct WORD{ // 内存字类型 WORD *llink; // 指向前驱结点 rlink … int tag; // 0-空闲; 1-占用 int kval; // 块大小,值为2的幂次k WORD *rlink; //指向后继结点 OtherType other; //字的其它部分 }WORD, head; typedef struct HeadNode{ int nodesize; // 该链表的空闲块的大小 WORD *first; //该链表的表头指针 } FreeList[m+1]; //表头向量类型

分配



从结点大小和请求分配的量相同的链表中查找结点并分 配之 若没有,则从结点较大的链表中查找结点,将其中一部 分分配给用户,剩余的插入到相应大小的链表中 若各链表都没有合适的结点,则要执行“存储紧缩”将 小块合并
将释放的空闲块插入到相应大小的链表的表头
11/33

回收

8.2 可利用空间表及分配方法(4)


若2k-i-1<n ≤ 2k-i-1(i为小于k的整数),并且所有结点小于2k的子表均 为空,则同样需从结点大小为2k的子表中取出一块,将其中2k-i分 配给用户,剩余部分分割成若干个结点分别插入在结点大小为2k-i、 2k-i+1、…、 2k-1的子表中。
20/33
8.4 伙伴系统(4)

回收算法(P205,206)
3/33


操作系统


8.1 概述(2)

动态存储管理的基本问题

系统如何用用户提出的“请求”分配内存? 如何回收那些用户不再使用而“释放”的内存,以备新 的“请求”产生时重新进行分配? 用户提出的“请求”: 可能是进入系统的一个作业 也可能是程序执行过程中的一个动态变量 系统每次分配给用户都是一个地址连续的内存区。称已分 配给用户使用的地址连续的内存区为“占用块”,称未曾 分 配的地址连续的内存区为“ 4/33可利用空间块”或“空闲块”。
假设用户释放的内存区的起始地址为p,块的大小为2k

伙伴系统仅考虑互为“伙伴”的两个空闲块的归并
伙伴:两个由同一大块分裂出来的小块就称为“互为 伙伴”。假设p为大小为2k的空闲块的初始地址,且
p MOD 2k+1=0,则初始地址为p和p+2k的两个空
闲块互为伙伴。
21/33
8.4 伙伴系统(5)


可利用空间表的结构
tag size rlink typedef struct WORD{ // 内存字类型 union { WORD *llink; // 指向前驱结点 WORD *uplink; // 指向本结点头部 }; int tag; // 0-空闲; 1-占用 int size; // 块大小 WORD *rlink; //指向后继结点 OtherType other; //字的其它部分 }WORD, head, foot, *Space; // 指向p所指结点的底部 #define FootLoc(p) p+p->size-1

系统运行期间分配给用户的内存块的大小不固定

开始时,整个内存空间是一个空闲块 随着分配和回收的进行,可利用空间表中的结点大小和个 数也随之而变 结点的结构


链域(link):指向同一链表中下一结点的指针 标志域(tag):0-空闲块、1-占用块 大小域(size):指示该空闲块的存储量 space:地址连续的内存空间
判别其伙伴是否为空闲块

若是,则在相应子表中找到其伙伴并删除之,然后再判别合 并后的空闲块的伙伴是否是空闲块 若否,则只要将释放的空闲块简单插入在相应子表中即可


伙伴块的起始地址
p 2 k (若p MOD 2 k 1 0 ) buddy( p, k ) k k 1 k p 2 (若p MOD 2 2 )

可利用空间表:双重循环链表
分配:首次拟合或最佳拟合
特点

每个内存区的头部和底部两个边界上分别设有标识, 以标识该区域为占用块或空闲块,使得在回收时易 于判别在物理位置上与其相邻的内存区域是否为空 闲块,以便将所有地址连续的空闲存储区组合成一 个尽可能大的空闲块。
14/33
8.3 边界标识法(2)

对伙伴系统的评价

优点:算法简单、速度快
缺点:由于只归并伙伴而容易产生碎片
22/33
8.5 无用单元收集(1)

8.2~8.4节讨论的存储管理系统中,用户必须明确给出 “请求”和“释放”的信息 因为用户的疏漏或结构本身的原因致使系统在不恰当 的时候或没有进行回收而产生“无用单元”或“悬挂访问” 的问题。
23/33

8.5 无用单元收集(2)

如何解决广义表结构中的“无用单元”或“悬挂访 问”问题?


困难
使用访问计数器:在所有子表或广义表上增加一个表头 结点,并设立一个“计数域”,它的值为指向该子表或 广义表的指针数目。当该计数域的值为0时,此子表或广 义表中的结点才被释放。 收集无用单元:在程序运行中,所有的链表结点不管是 否还有用,都不被回收,直到整个可利用空间表为空。 此时中断执行程序,收集无用单元,分两步进行: 1)对所有占用结点加上标志(0-未用,1-占用) 2)对整个可利用存储空间顺序扫描一遍,将标志为0的 结点链成一个新的可利用空间表。 24/33

首次拟合法:取第一个不小于n的空闲块 最佳拟合法:取表中一个不小于n且最接近n的空闲块 表按空闲块大小自小到大有序,适于大小范围较广的系统 最差拟合法:取表中不小于n且是表中最大的空闲块 表按空闲块大小自大至小有序,适于大小范围较窄的系统
13/33

8.3 边界标识法(1)

边界标识法(Boundary tag method)
av
起始地址 内存块大小 使用情况 10,000 31,000 59,000 15,000 8,000 41,000 空闲 空闲 空闲
0 15,000
0 8,000
0 41,000 ^
(b) 目录表
7/33
(c) 链表
8.2 可利用空间表及分配方法
讨论利用可利用空间表进行动态 存储分配的方法. 目录表简单,将在操作系统课程中 介绍 这里仅就链表的情况进行讨论
tag
size space
link
——操作系统中的可变分区管理
12/33
8.2 可利用空间表及分配方法(5)

可利用空间表中的结点大小不同时的分配方法
假设用户需大小为n 的内存 若链表中仅有一块大小为m≥n的空闲块:将其中大小 为n的一部分分配给用户,剩余大小为m-n的作为一个 结点留在链表中 若链表中有若干个不小于n的空闲块:有三种分配策 略
15/33
head llink
space
foot uplink tag
8.3 边界标识法(3)

分配算法 (算法8.1 P200)
假设用首次拟合法进行分配,请求分配的存储量为n。 为使整个系统更有效地运行,在边界标识法中作如下约定

假设找到的待分配空闲块的容量为m个字(包括头部),选定一个适 当的常量e,当m-n≤e时,就将容量为m的空闲块整块分配给用户; 否则只分配其中n个字的内存块。 为避免修改指针,约定将该结点中的高地址部分分配给用户。
Hale Waihona Puke Baidu内存区连接在一起成为一个大的空闲块。

策略二:从所有空闲块中找出一个“合适”的空闲块分配之。 系统需要建立一张记录所有空闲块的“可利用空间表”,它可以是
“目录表”,也可以是“链表”。
6/33
8.1 概述(5)
0 10000 25000 39000 59000 99999
U6
31000
(a) 内存状态
8/33
8.2 可利用空间表及分配方法(1)

可利用空间表的表示——链表

一个空闲块一个结点 用户请求分配时,系统从表中删除一个结点分配之 用户释放所占内存时,系统即回收并将它插入到表中

根据系统运行的不同情况,可利用空间表有不同 的结构形式

系统运行期间所有用户请求分配的存储量大小相同
每个结点的第一个字设有

av4
0 1 av8 0 2 0 2 0 2 ^

链域(link):指向同一链表 中下一结点的指针
标志域(tag):0-空闲块、1占用块


结点类型域(type):区分大 小不同的结点
10/33
8.2 可利用空间表及分配方法(3)

系统运行期间用户请求分配的存储量有若干种 大小的规格

由于表中结点大小相同,则分配时无需查找
可以用链栈实现
9/33
——操作系统中的固定分区管理
8.2 可利用空间表及分配方法(2)

系统运行期间用户请求分 配的存储量有若干种大小 的规格

tag
type value
link
av2
0 0 0 0 0 1 0 0 ^ 0 1 ^
建立若干个可利用空间表, 同一链表中的结点大小相同
8.1 概述(3)
动态存储管理系统刚开工时 U1 U2 占用块 U1 U3 U3 U4 U5 U6 U7 U8 空闲块
系统运行初期
U4 U6 U8
系统运行若干时间之后
5/33
8.1 概述(4)
U1

U3
U4
U6
U8
当有新的用户进入系统请求分配内存,系统将如何做呢?

策略一:从高地址的空闲块中进行分配,当分配无法进行时,系统 回收所有用户不再使用的空闲块,并重新组织内存,将所有空闲的

每次分配从不同的结点(如刚进行分配的结点的后继)开始进行查找, 使分配后剩余的小块均匀地分布在链表中。——避免小块集中在 头指针所指结点附近,从而增加查询较大空闲块的时间
16/33
8.3 边界标识法(4)

回收算法(P201~203)
假设用户释放的内存区的头部地址为p

要检查刚释放的占用块M的左、右紧邻是否为空闲块
19/33
2m
8.4 伙伴系统(3)

分配算法 (算法8.2 P205)
假设用请求分配的存储量为n 。

若2k-1<n ≤ 2k-1,即第k+1个子表不空,则只删除此链表中第一个 结点并分配给用户即可 若2k-2<n ≤ 2k-1-1,结点大小为2k-1的子表为空,则从结点大小为2k 的子表中取出一块,将其中一半分配给用户,剩余的一半作为一 个新结点插入在结点大小为2k-1的子表中

与M低地址紧邻的内存区的底部地址为p-1
与M高地址紧邻的内存区的头部地址为p+p->size

M的左、右邻区均为占用块:简单插入 M的左邻区为空闲块,右邻区为占用块:合并左邻区和M,增加左 邻空闲块结点的size域的值,重新设置结点的底部 M的右邻区为空闲块,左邻区为占用块:合并M和右邻区,结点的 底部位置不变,头部要变,链表的指针也要变 M的左、右邻区均为空闲块:合并左邻区、M和右邻区,增加左邻 空闲块的size值,在链表中删除右邻空闲块结点,修改底部
相关文档
最新文档