1、要求:
用C语言分别实现采用首次适应算法和最佳适应算法的动态分区分配过程alloc()和回收过程free()。其中,空闲分区通过空闲分区链来管理:在进行内存分配时,系统优先使用空闲区低端的空间。
假设初始状态下,可用的内存空间为640KB,并有下列的请求序列:
•作业1申请130KB •作业2申请60KB。
•作业3申请100KB •作业2释放60KB。
•作业4申请200KB •作业3释放100KB。
•作业1释放130KB •作业5申请140KB。
•作业6申请60KB •作业7申请50KB •作业6释放60KB。
请分别采用首次适应算法和最佳适应算法,对内存块进行分配和回收,要求每次分配和回收后显示出空闲分区链的情况。
2、数据结构:
程序采用链式存储结构存储空闲分区链。首先定义空闲分区表的结构体,包括空闲分区表的id号,首地址、分区大小、分配标志。然后用结构体表示链表的每一个节点,包括分区表结构体、front和next指针。
3、最佳适应算法思路:
首先,定义一个p指针,让p指针遍历空闲分区链表,当找到第一个满足进程请求空间大小的空闲区时,记录此位置,并且保存请求大小与空闲分区实际大小的差值记为a,然后让p指针继续遍历空闲分区链表,每当满足请求内存大小小于空闲区大小时,就记录两者的差值并且记录为b,比较a与b的大小关系,当a>b时,将b的值赋予a,并且修改记录位置为此空闲区的位置。若,a<=b,不做操作。继续遍历链表,重复上面的操作,直到p->next指向null为止。
4、 首次适应算法思路:
首次适应算法比较简单,只要找到满足条件的空闲区,就将此区的空间分配给进程。首先,用P指针遍历链表,找到第一个空间大于或者等于请求大小的位置,将此空间分配给进程,当此空闲区大小大于请求空间大小时,将空闲区分为两部分,一部分分配给进程,另一部分为空闲区,它的大小为之前空闲区大小减去分配给进程的空间大小。
5:、内存回收算法思想:
内存回收时,回收分区与空闲分区有四种关系。第一种情况为回收分区r上临一个空闲分区,此时应该合并为一个连续的空闲区,其始址为r上相邻的分区的首地址,而大小为两者大小之和。第二种情况为回收分区r与下相邻空闲分区,合并后仍然为空闲区,该空闲区的始址为回收分区r的地址。大小为两者之和,第三种情况为回收部分r与上下空闲区相邻,此时将这三个区域合并,始址为r上相邻区域的地址,大小为三个分区大小之和。当回收部分r上下区域都为非空闲区域,此时建立一个新的空闲分区,并且加入到空闲区队列中去。
6、主要函数伪代码:
Void first_fit()//首次适应算法
{
定义一个临时节点temp储存进程ID,和请求内存大小size,标记为繁忙BUSY;
定义p指针用来遍历空闲区链表;
初始p指向链表头结点;
While(p)
{
if(p为空闲区&&p大小刚好满足要求)
{
将此区域分配给进程;
标记为繁忙区域;
Return 1;
Break;
}
If(p为空闲区&&空闲区大小比进程请求空间大小大)
{
将temp插入到链表此位置上,并且将此位置的地址赋予Temp,剩余空闲区的大小为空闲区大小与请求空间之差;
}
P=p->next;
}
Return 1;
}
Void best_fit()//最佳适应算法
{
定义surplus记录可用内存与需求内存的差值;
定义一个临时节点temp储存进程ID,和请求内存大小size,标记为繁忙BUSY;
定义p指针用来遍历空闲区链表;
初始p指向链表头结点;
定义q作为标记节点;
While(p)
{
If(p为空闲区&&p满足内存大小大于进程请求大小)
{
Q=p;
将两者差值赋予surplus;
Return 1;
Break;
}
P=p->next;
}
While(p)
{
if(p为空闲区&&p大小刚好满足要求)
{
将此区域分配给进程;
标记为繁忙区域;
Break;
}
If(p为空闲区&&空闲区大小比进程请求空间大小大)
{
If(surplus大于请求大小与内存空闲区大小之差)
更新surplus为请求大小与内存空闲区大小之差;
Q=p;
}
P=p->next;
}
If( q为空)
{
Return 0;
}
Else(找到了最佳位置)
{
将temp插入到链表此位置上,并且将此位置的地址赋予Temp,剩余空闲区的大小为空闲区大小与请求空间之差;
Return 1;
}
}
Int free(int ID)//内存回收算法
{
定义p指针用来遍历空闲区链表;
While(p)
{
If(需要回收的ID等于p的ID)
{
先释放P指向的内存;
If(p上相邻区域为空闲区&&p下相邻区域为繁忙区)
{
将p与上相邻区域合并;
此空闲区大小为两者大小之和;
首地址为p上相邻区域地址;
}
If(p下相邻区域为空闲区&&p上相邻区域为繁忙区)
{
将p与下相邻区域合并;
此空闲区大小为两者大小之和;
首地址为p下相邻区域地址;
}
If(p上相邻区域为空闲区&&p下相邻区域为空闲区)
{
将p与上、下相邻区域合并;
此空闲区大小为三者大小之和;
首地址为p上相邻区域地址;
}
If(p上相邻区域为繁忙区&&p下相邻区域为繁忙区)
{
新建空闲区;
}
Break;
}
p=p->next;
}
输出回收成功!
Return 1;
}
7、源代码:
#include
using namespace std;
#define FREE 0
#define BUSY 1
#define MAX_length 640
typedef struct freeArea//首先定义空闲区分表结构
{
int flag;
int size;
int ID;
int address;
}Elemtype;
typedef struct Free_Node
{
Elemtype date;
struct Free_Node *front;
struct Free_Node *next;
}Free_Node,*FNodeList;
FNodeList block_first;
FNodeList block_last;
int alloc(int tag);//内存分配
int free(int ID);//内存回收
int first_fit(int ID,int size);//首次适应算法
int best_fit(int ID,int size);//最佳适应算法
void show();//查看分配
void init();//初始化
void Destroy(Free_Node *p);//销毁节点
void menu();
void init()//
{
block_first=new Free_Node;
block_last = new Free_Node;
block_first->front=NULL;
block_first->next=block_last;
block_last->front=block_first;
block_last->next=NULL;
block_last->date.address=0;
block_last->date.flag=FREE;
block_last->date.ID=FREE;
block_last->date.size=MAX_length;
}
//实现内存分配
int alloc(int tag)
{
int ID,size1;
cout<<"请输入作业号:";
cin>>ID;
cout<<"请输入所需内存大小:";
cin>>size1;
if (ID<=0 || size1<=0)
{
cout<<"ERROR,请输入正确的ID和请求大小"<next;
temp->date.ID=ID;
temp->date.size=size;
temp->date.flag=BUSY;
while(p)
{
if (p->date.flag==FREE && p->date.size==size)//请求大小刚好满足
{
p->date.flag=BUSY;
p->date.ID=ID;
return 1;
break;
}
if (p->date.flag==FREE && p->date.size>size)//说明还有其他的空闲区间
{
temp->next=p;
temp->front=p->front;
temp->date.address=p->date.address;
p->front->next=temp;
p->front=temp;
p->date.address=temp->date.address+temp->date.size;
p->date.size-=size;
return 1;
break;
}
p=p->next;
}
return 0;
}
int best_fit(int ID,int size)//最佳适应算法
{
int surplus;//记录可用内存与需求内存的差值
FNodeList temp=(FNodeList)malloc(sizeof(Free_Node));
Free_Node *p=block_first->next;
temp->date.ID=ID;
temp->date.size=size;
temp->date.flag=BUSY;
Free_Node *q=NULL;//记录最佳位置
while(p)//遍历链表,找到第一个可用的空闲区间将他给q
{
if (p->date.flag==FREE&&p->date.size>=size)
{
q=p;
surplus=p->date.size-size;
break;
}
p=p->next;
}
while(p)//继续遍历,找到更加合适的位置
{
if (p->date.flag==FREE&&p->date.size==size)
{
p->date.flag=BUSY;
p->date.ID=ID;
return 1;
break;
}
if (p->date.flag==FREE&&p->date.size>size)
{
if (surplus>p->date.size-size)
{
surplus=p->date.size-size;
q=p;
}
}
p=p->next;
}
if (q==NULL)//如果没有找到位置
{
return 0;
}
else//找到了最佳位置
{
temp->next=q;
temp->front=q->front;
temp->date.address=q->date.address;
q->front->next=temp;
q->front=temp;
q->date.size=surplus;
q->date.address+=size;
return 1;
}
}
int free(int ID)//主存回收
{
Free_Node *p=block_first->next;
while(p)
{
if (p->date.ID==ID)//找到要回收的ID区域
{
p->date.flag=FREE;
p->date.ID=FREE;
//判断P与前后区域关系
if (p->front->date.flag==FREE&&p->next->date.flag!=FREE)
{
p->front->date.size+=p->date.size;
p->front->next=p->next;
p->next->front=p->front;
}
if (p->front->date.flag!=FREE&&p->next->date.flag==FREE)
{
p->date.size+=p->next->date.size;
if(p->next->next)
{
p->next->next->front=p;
p->next = p->next->next;
}
else p->next=p->next->next;
}
if(p->front->date.flag==FREE&&p->next->date.flag==FREE)
{
p->front->date.size+=p->date.size+p->next->date.size;
if(p->next->next)
{
p->next->next->front=p->front;
p->front->next=p->next->next;
}
else p->front->next=p->next->next;
}
if(p->front->date.flag!=FREE&&p->next->date.flag!=FREE)
{//
}
break;
}
p=p->next;
}
cout<<"回收成功!"<next;
while(p)
{
cout<<"分区号:";
if (p->date.ID==FREE)
cout<<"FREE"<date.ID;
cout<<"起始地址:"<date.address<date.size<date.flag==FREE)
cout<<"空闲"<next;
}
}
void menu()//菜单
{
int tag=0;
int ID;
init();
cout<<"分区模拟:"<>tag;
switch(tag)
{
case 1:
alloc(tag);
break;
case 2:
alloc(tag);
break;
case 3:
cout<<"亲输入需要回收的ID号:"<>ID;
free(ID);
break;
case 4:
show();
break;
}
}
}
void main()
{
menu();
}
8、调试分析:
首次适应算法:
最佳适应算法:
9、算法优缺点:
对于首次适应算法:
该算法倾向于优先利用内存中低址部分的空闲分区,从而保留了高址部分的大空闲区,这为以后到达的大作业分配大的内存空间创造了条件。
缺点为低址部分不断被划分,会留下许多难以利用的,很小的空闲分区,称为碎片。而每次查找又都是从低址部分开始的,这无疑又会增加查找可用空闲分区时的开销。
对于最佳适应算法:
它从全部空闲区中找出能满足作业要求的、且大小最小的空闲分区,这种方法能使碎片尽量小。为适应此算法,空闲分区表(空闲区链)中的空闲分区要按从小到大进行排序,自表头开始查找到第一个满足要求的自由分区分配。该算法保留大的空闲区,但造成许多小的空闲区。
缺点为会造成很多细小的碎片,这些小碎片不能满足大多数进程请求内存的大小,所以会造成内存浪费。