引子
连续动态内存管理,该实验其实就是对双向链表进行操作来模拟内存分配,比起普通链表更细心一点就行。
一、实验目的
1) 理解内存管理相关理论;
2) 掌握连续内存管理理论;
3) 掌握动态连续内存管理理论。
二、实验内容
本实验主要针对操作系统中内存管理相关理论进行实验,要求实验者编写一个程序,该程序管理一块虚拟内存,实现内存分配和回收功能。
1) 模拟管理 64M 的内存块;
2) 设计内存分配函数;
3) 设计内存回收函数;
4) 实现动态分配和回收操作;
5) 可动态显示每个内存块信息。
三、实验原理
连续内存分配:为一个用户程序分配一个连续的内存空间,它分为单一连续分配,固定分区分配和动态分区分配,在本实验中,我们主要讨论动态分区分配。
动态连续分配:根据进程的实际需要,动态地为之分配内存空间。在实现可变分区分配时,将涉及到分区分配中的所用的数据结构、分区分配算法和分区的分配与回收操作这几个问题。
- 分区分配中的数据结构
(1) 空闲分区表:一张数据表,用于记录每个空闲块的情况,如起始地址、大小,使用情况等。
(2) 空闲分区链:为了实现对空闲分区的分配,把所有的空闲内存块连成一个双向链,便于分配和回收。 - 分区分配算法
(1) 首次适应算法:从链首出发,寻找满足申请要求的内存块。
(2) 循环首次适应算法:从上次查找的下一个空闲块开始查找,直到找到满足要求的内存块。
(3) 最佳适应算法:在每次查找时,总是要找到既能满足要求又最小的内存块给分配给用户进程。为了方便查找,所有的空闲内存块按从小到大的顺序存放在空闲链表中。 - 内存分配操作
利用分配算法查找到满足要求的内存块,设请求内存大小为 u.size,而分配的内存块大小为 m.size,如果 m.size-u.size≤size (size 为设定的不可再分割的内存大小),则不再切割;反之,按 u.size 分配给申请者,剩余的部分仍留在内存链中。 - 回收内存
根据回收区地址,从空闲链表中找到相应的插入点。
(1) 回收区与插入点的前一个空闲分区相邻,此时将回收区与前一分区合并,不为回收区分配新表项。
(2) 回收区与插入点的后一个空闲分区相邻,将回收区与后一分区合并成一个新区,回收区的首址最为新分区的首址。
(3) 回收区与前(F1)后(F2)分区相邻,则把三个分区合并成一个大的分区,使 F1 的首址作为新分区的首址,修改 F1 大小,撤销 F2 表项。
(4) 回收区不与任何分区相邻,为回收区建立一个新表项。
C++代码如下:
#include <cstdio>
#include <iostream>
using namespace std;
struct Elem{
int num; //分区序号
int begin; //起始地址
int size; //分区大小
int status; //使用状态
};
//空闲分区链的每一个结点,空闲分区链是一个双向链表
struct Node
{
Elem data;
Node* prior; //前趋指针
Node* next; //后趋指针
};
//空闲分区链
class LinkedList
{
private:
Node* first; //头结点
Node* end; //尾结点
Node* record; //记录上次查找的空闲块
public:
void InitLinkedList();
void show();
int first_fit(int request);
int cycle_first_fit(int request); //写出来有bug,还是不贴出来了
int best_fit(int request); //最佳适应算法
int recovery(int mnum); //回收分区
};
void LinkedList::InitLinkedList()
{
first=new Node();
end=new Node();
first->prior=NULL;
first->next=end;
end->prior=first;
end->next=NULL;
end->data.num=1;
end->data.begin=40;
end->data.size=600;
end->data.status=0;
first->data.num=0;
first->data.begin=0;
first->data.size=40;
first->data.status=1;
}
//首次适应算法
int LinkedList::first_fit(int request)
{
Node *p=first->next;
while(p)
{
//有大小正好合适的内存块
if(p->data.status==0&&p->data.size==request)
{
p->data.status=1;
return 1;
}
//有空闲块且能满足需求,分块后有剩余
else if(p->data.status==0&&p->data.size>request)
{
Node* temp=new Node();
temp->data.size=request;
temp->data.status=1;
temp->prior=p->prior;
temp->next=p;
temp->data.begin=p->data.begin;
p->prior->next=temp;
p->prior=temp;
p->data.begin=temp->data.begin+request;
p->data.size-=request;
record=p; //记录上次查找的下一个空闲块
//重新分配分区序号
Node* p2=first->next;
int nums=1;
while(p2)
{
p2->data.num=nums++;
if(p2->next==NULL)
{
end=p2; //重置尾结点
}
p2=p2->next;
}
return 1;
}
//记录上次查找的下一个空闲块
else if(p->data.status==0)
{
record=p;
}
p=p->next;
}
return 0;
}
int LinkedList::best_fit(int request)
{
Node* p=first->next;
Node* re=NULL;
int rsize=0;
//找到最小的满足需求的内存块
while(p)
{
//有大小正好合适的内存块
if(p->data.status==0&&p->data.size==request)
{
p->data.status=1;
return 1;
}
//有空闲块且能满足需求,则记录最小的满足需求的空闲内存块
else if(p->data.status==0&&p->data.size>request)
{
if(re==NULL)
{
re=p;
rsize=p->data.size;
}
else if(rsize>p->data.size)
{
re=p;
rsize=p->data.size;
}
}
p=p->next;
}
//能找到最小的空闲块
if(re!=NULL)
{
Node* temp=new Node();
temp->data.size=request;
temp->data.status=1;
temp->prior=re->prior;
temp->next=re;
temp->data.begin=re->data.begin;
re->prior->next=temp;
re->prior=temp;
re->data.begin=temp->data.begin+request;
re->data.size-=request;
//重新分配分区序号
Node* p2=first->next;
int nums=1;
while(p2)
{
p2->data.num=nums++;
if(p2->next==NULL)
{
end=p2; //重置尾结点
}
p2=p2->next;
}
return 1;
}
return 0;
}
int LinkedList::recovery(int mnum) //回收被占用的分区
{
//最好别删除头结点,因为链表结构没有设置得很好,本来应该在头部加一个头结点的
if(mnum==0)
{
return 0;
}
Node* p=first;
while(p!=NULL&&p->data.num!=mnum)
{
p=p->next; //找到该序号对应的分区
}
//没有找到该分区
if(p==NULL)
{
return 0;
}
//找到该分区相邻的空闲分区
Node* pre=p;Node* rear=p;
int size=p->data.size; //记录总空闲区间的内存大小
while(pre->prior!=NULL&&pre->prior->data.status==0)
{
pre=pre->prior;
size+=pre->data.size;
}
while(rear->next!=NULL&&rear->next->data.status==0)
{
rear=rear->next;
size+=rear->data.size;
}
//回收之后形成的新的空闲分区
Node *temp=new Node();
temp->data.size=size;
temp->data.begin=pre->prior->data.begin+pre->prior->data.size;
temp->data.status=0;
//连接上总表
temp->next=rear->next;
temp->prior=pre->prior;
pre->prior->next=temp;
if(rear->next!=NULL) rear->next->prior=temp;
//删除旧结点
p=pre;
while(p!=rear)
{
p=p->next;
delete p->prior;
}
delete p;
//重置分区号
Node* p2=first->next;
int nums=1;
while(p2)
{
p2->data.num=nums++;
if(p2->next==NULL)
{
end=p2; //重置尾结点
}
p2=p2->next;
}
return 1;
}
void LinkedList::show()
{
Node* p=first;
printf("\n\t\t》主存空间分配情况《\n");
printf("**********************************************************\n\n");
printf("分区序号\t起始地址\t分区大小\t分区状态\n\n");
while(p)
{
printf("%d\t\t%d\t\t%d",p->data.num,p->data.begin,p->data.size);
if(p->data.status==0) printf("\t\t空闲\n\n");
else printf("\t\t已分配\n\n");
p=p->next;
}
printf("**********************************************************\n\n");
}
int main()
{
LinkedList mylist;
mylist.InitLinkedList();
mylist.show();
while(1)
{
printf("请输入想要进行的操作(1:分配内存,2:回收内存 3:显示内存信息 4:退出系统)\n");
int oper=0;
scanf("%d",&oper);
if(oper==1)
{
while(1)
{
printf("请输入想要进行的分配算法(1:首次适应算法,2:最佳适应算法,3:取消分配)\n");
int num=0;
scanf("%d",&num);
if(num==3)
{
break;
}
int request=0;
printf("请输入要分配的内存大小\n");
scanf("%d",&request);
if(request<0)
{
printf("内存分配不能为负!\n");
continue;
}
if(num==1)
{
if(mylist.first_fit(request))
{
printf("内存分配成功!\n");
}
else
{
printf("内存申请过多,分配失败!\n");
}
break;
}
else if(num==2)
{
if(mylist.best_fit(request))
{
printf("内存分配成功!\n");
}
else
{
printf("内存申请过多,分配失败!\n");
}
break;
}
else
{
printf("您输入的分配算法操作符号有误,请重新输入!\n");
continue;
}
}
}
else if(oper==2)
{
int num=0;
printf("请输入回收区分区序号\n");
scanf("%d",&num);
if(num==0)
{
printf("不能删除头结点\n");
continue;
}
if(mylist.recovery(num))
{
printf("回收成功!\n");
}
else
{
printf("没有查到该分区序号\n");
}
}
else if(oper==3)
{
mylist.show();
}
else if(oper==4)
{
break;
}
else
{
printf("您输入的操作有误,请重新输入!\n");
continue;
}
}
return 0;
}
运行截图如下: