1 设计内容
通过编写和调试存储管理的模拟程序以加深对存储管理方案的理解,熟悉可变分区存储管理的内存分配和回收。
设计一个可变式分区分配的存储管理方案。并模拟实现分区的分配和回收过程。对分区的管理法采用下面两种算法之一:首次适应算法、最佳适应算法,必须建立空闲分区表和已分配分区表,回收算法考虑以下四种情况:
回收区与插入点的前一个空闲分区相邻接;
回收区与插入点的后一个空闲分区相邻接;
回收区同时与插入点的前、后两个分区相邻接;
回收区不与任何空闲区邻接;
2 分析设计
可变分区存储管理方式在程序装入内存之前并不预先建立分区,而是在程序运行时根据程序对内存空间的需要,动态的建立内存分区。
对分区的管理法采用最佳适应算法。
2.1算法原理
最佳适应算法:在空闲分区链上,找到大于待载入程序大小的所有空闲区,再在里面把最小的空闲区分配给带载入程序;如果所有空闲区都不能满足带载入程序所需大小,则分配失败。
回收算法:内存的回收即是在程序运行完之后退出内存,把该内存空间及时回收,以便重新分配给需要的程序。主要考虑四种情况:
回收区只与上空闲分区相邻接:将这两个区域合并成一个新空闲分区,新空闲分区的起始地址为上空闲分区起始地址,大小为回收区和上空闲分区之和,同时修改相应的数据结构。
回收区只与下空闲分区相邻接:将这两个区域合并成一个新空闲分区,新空闲分区的起始地址为回收区起始地址,大小为回收区和下空闲分区之和,同时修改相应的数据结构。
回收区同时与插入点的前、后两个分区相邻接:将这三个区域合并成一个新空闲分区,新空闲分区的起始地址为上空闲分区起始地址,大小为回收区,上空闲分区和下空闲分区之和,同时修改相应的数据结构。
回收区不与任何空闲区邻接:将回收区单独作为一个空闲分区,同时修改相应的数据结构。
2.2程序结构
该算法主要以分区系统函数subarea()统筹插入函数insert(),输入程序函数scandP(),最佳适应算法函数optimun(),时间流失模拟函数timego(),输出函数printL()和回收算法recycle()来实现,总内存在主函数main()中创建。
插入函数insert():待载入程序插入空闲分区的低地址端;
输出函数printL():输出总分区表,已分配分区表以及空闲分区表,按照地址递增输出;
输入程序函数scandP():选择该时段想输入的程序个数,按照最佳适应算法载入程序;
最佳适应算法函数optimun():检索所有空闲分区,如果有空闲分区大小和待载入程序的一样,直接把该分区分配给待载入程序;如果只有大于待载入程序大小的空闲分区,则在其中找到最小的那个空闲分区,把它分配给待载入程序;如果没有满足大小的空闲分区,则反馈为分配失败。
回收算法recycle():按照地址递增检索所有已载入内存的程序,如果程序达到了所需的时间,则把它回收,回收分四种情况。
时间流失模拟函数timego():已载入内存的所有程序所需时间减一;
分区系统函数subarea():进入循环:输入该时段的程序,模拟时间流逝,回收,检查载入失败的程序是否可以被满足,输出分区表。
主函数main():创建总内存,启动分区系统。
2.3数据结构
设定总内存MAXS为500KB
定义分区单元结构体:
typedef struct node{ //定义分区单元
int ID; //程序号
int START; //起始地址
int SIZE; //大小
char STATE; //状态(已分配为‘P’或空闲为‘L’)
int NEEDTIME; //如果是已分配分区 则有 还需占用时间
struct node * next,* prior; //prior是前驱指针,next是后置指针
}SUBAREA;
定义内存分区双链表的头指针:
SUBAREA *ALL=NULL; //内存分区链的头指针
定义收集分配失败程序的数组和标识:
int get[100][2]; //程序集 用来记录未载入的程序
int g=0; //分配失败程序标识
2.4程序流程图
最佳适应算法流程图:
回收算法流程图:
2.5关键代码
插入函数insert():
void insert(SUBAREA *p,int size,int time){ //插入内存分区链的函数
SUBAREA p1;
p1=(SUBAREA)malloc(sizeof(SUBAREA));
p1->SIZE=size; //程序插入低地址端
p1->STATE='P';
p1->ID=id;
id++;
p1->START=p->START;
p1->NEEDTIME=time;
p->SIZE=p->SIZE-size;
p->START=p1->START+size;
if(p->prior==NULL){ //如果p结点为头结点
p1->next=p;
p->prior=p1;
p1->prior=NULL;
ALL=p1;
}
else{
p1->prior=p->prior;
p->prior->next=p1;
p1->next=p;
p->prior=p1;
}
}
输出函数printL():
void printL(char f){ //输出分区表 'P’为已分配分区表,'L’为空闲分区表,'S’为所有分区表
SUBAREA *p;
p=ALL;
if(f=='S'){
int i=1;
cout<<" 总分区表:"<<endl;
cout<<"_________________________________________________________________"<<endl;
cout<<"\t分区号 |"<<"\t起始地址(KB) |"<<"\t大小(KB) |"<<"\t状态 |"<<endl;
while(p!=NULL){
if(p->STATE=='P'){
cout<<"\t"<<i<<"\t |"<<"\t"<<p->START<<"\t |"<<"\t"<<p->SIZE<<"\t |"<<"\t"<<p->STATE<<p->ID<<"\t |"<<endl;
}
else if(p->STATE=='L'){
cout<<"\t"<<i<<"\t |"<<"\t"<<p->START<<"\t |"<<"\t"<<p->SIZE<<"\t |"<<"\t"<<p->STATE<<"\t |"<<endl;
}
i++;
p=p->next;
}
cout<<"__________________|___________________|___________|______________|"<<endl<<endl;
}
else if(f=='P'){
int j=1;
cout<<" 已分配分区表:"<<endl;
cout<<"________________________________________________________________________________"<<endl;
cout<<"\t分区号 |"<<"\t起始地址(KB) |"<<"\t大小(KB) |"<<"\t状态 |"<<"\t时间\t|"<<endl;
while(p!=NULL){
if(p->STATE=='P'){
cout<<"\t"<<j<<"\t |"<<"\t"<<p->START<<"\t |"<<"\t"<<p->SIZE<<"\t |"<<"\t"<<p->STATE<<p->ID<<"\t |";
cout<<"\t"<<p->NEEDTIME<<"\t|"<<endl;
j++;
}
p=p->next;
}
cout<<"__________________|___________________|___________|______________|______________|"<<endl<<endl;
}
else if(f=='L'){
int k=1;
cout<<" 空闲分区表:"<<endl;
cout<<"_________________________________________________________________"<<endl;
cout<<"\t分区号 |"<<"\t起始地址(KB) |"<<"\t大小(KB) |"<<"\t状态 |"<<endl;
while(p!=NULL){
if(p->STATE=='L'){
cout<<"\t"<<k<<"\t |"<<"\t"<<p->START<<"\t |"<<"\t"<<p->SIZE<<"\t |"<<"\t"<<p->STATE<<"\t |"<<endl;
k++;
}
p=p->next;
}
cout<<"__________________|___________________|___________|______________|"<<endl<<endl;
}
}
输入程序函数scandP():
void scandP(){ //载入一批程序 大小和时间
int s,t,i,n;
cout<<"请选择在这个时长载入程序的个数: ";
cin>>n;
if(n!=0){
for(i=1;i<=n;i++){
cout<<"请输入第"<<i<<"个程序所需内存的大小SIZE和需要时间NEEDTIME:"<<endl;
cin>>s>>t;
optimun(s,t);
}
cout<<endl<<"-----------------------------载入完毕-----------------------------"<<endl<<endl;
printL('S');
printL('P');
printL('L');
}
else
cout<<"在这个时长不载入程序!"<<endl;
}
最佳适应算法函数optimun():
void optimun(int size,int time){ //最佳适应算法
SUBAREA *p1,*p2;
int a=1,min=MAXS+1,area=0;
p1=ALL;
p2=ALL;
while(p1!=NULL){
if(p1->STATE=='L'){
if(p1->SIZE==size){ //该空闲区等于程序需求
p1->NEEDTIME=time;
p1->ID=id;
p1->STATE='P';
id++;
a=0;
break;
}
else if(p1->SIZE>size){ //找到大于要求的各空闲区的最小值
if(p1->SIZE<min){
area=p1->START;
min=p1->SIZE;
}
p1=p1->next;
}
else if(p1->SIZE<size) //小于
p1=p1->next;
}
else
p1=p1->next;
}
while(min!=MAXS+1&&a==1){ //在链表中找到刚才的空闲区
if(p2->START==area){
insert(p2,size,time);
break;
}
p2=p2->next;
}
if(a==1&&min==MAXS+1){
cout<<"!!!所有空闲分区的大小都不能满足要求,此次分配失败!!!"<<endl<<endl;
g++; //失败数加1
fail(g,size,time); //记录
}
else
cout<<"~已分配程序~ 程序名为:"<<"P"<<id-1<<endl<<endl;
}
回收算法recycle():
void recycle(){ //回收算法
SUBAREA *p,*p1;
p=ALL;
cout<<endl<<"--------------------正在对已完成程序进行回收处理-----------------"<<endl<<endl;
while(p!=NULL){
p1=(SUBAREA*)malloc(sizeof(SUBAREA));
if(p->STATE=='P'&&p->NEEDTIME==0&&p->prior!=NULL&&p->next!=NULL){ //此结点不是首尾位置
if(p->prior->STATE=='L'&&p->next->STATE=='L'){ //上下分区都是空闲分区
p1->START=p->prior->START;
p1->SIZE=p->SIZE+p->prior->SIZE+p->next->SIZE;
p1->NEEDTIME=0;
p1->STATE='L';
if(p->next->next!=NULL){ //它的下分区是否 尾结点
p1->next=p->next->next;
p->next->next->prior=p1;
}
else
p1->next=NULL;
if(p->prior->prior!=NULL){ //它的上分区是否 首结点
p1->prior=p->prior->prior;
p->prior->prior->next=p1;
}
else{
p1->prior=NULL;
ALL=p1;
}
}
else if(p->prior->STATE=='L'&&p->next->STATE=='P'){ //上分区是空闲分区,下分区是已分配分区
p1->START=p->prior->START;
p1->SIZE=p->prior->SIZE+p->SIZE;
p1->NEEDTIME=0;
p1->STATE='L';
if(p->prior->prior!=NULL){
p1->prior=p->prior->prior;
p->prior->prior->next=p1;
}
else{
p1->prior=NULL;
ALL=p1;
}
p1->next=p->next;
p->next->prior=p1;
}
else if(p->prior->STATE=='P'&&p->next->STATE=='L'){ //下分区是空闲分区,上分区是已分配分区
p1->START=p->START;
p1->SIZE=p->SIZE+p->next->SIZE;
p1->NEEDTIME=0;
p1->STATE='L';
p1->prior=p->prior;
p->prior->next=p1;
if(p->next->next!=NULL){
p1->next=p->next->next;
p->next->next->prior=p1;
}
else
p1->next=NULL;
}
else if(p->prior->STATE=='P'&&p->next->STATE=='P'){ //上下都不是
p->STATE='L';
p->ID=NULL;
}
}
else if(p->STATE=='P'&&p->NEEDTIME==0&&p->prior==NULL&&p->next!=NULL){ //此结点是 首结点
if(p->next->STATE=='L'){
p1->START=p->START;
p1->SIZE=p->SIZE+p->next->SIZE;
p1->NEEDTIME=0;
p1->STATE='L';
if(p->next->next!=NULL){
p1->next=p->next->next;
p->next->next->prior=p1;
}
else
p1->next=NULL;
p1->prior=NULL;
ALL=p1;
}
else{
p->STATE='L';
p->ID=NULL;
}
}
else if(p->STATE=='P'&&p->NEEDTIME==0&&p->next==NULL&&p->prior!=NULL){ //此结点是 尾结点
if(p->prior->STATE=='L'){
p1->START=p->prior->START;
p1->SIZE=p->prior->SIZE+p->SIZE;
p1->NEEDTIME=0;
p1->STATE='L';
if(p->prior->prior!=NULL){
p1->prior=p->prior->prior;
p->prior->prior->next=p1;
}
else{
p1->prior=NULL;
ALL=p1;
p1->next=NULL;
}
}
else {
p->STATE='L';
p->ID=NULL;
}
}
else if(p->STATE=='P'&&p->NEEDTIME==0&&p->next==NULL&&p->prior==NULL) //同时为首尾结点 即只有一个分区
p->STATE='L';
p=p->next;
}
cout<<endl<<"-----------------------------完成回收----------------------------"<<endl<<endl;
printL('S');
printL('P');
printL('L');
}
时间流失模拟函数timego():
void timego(){ //模拟时间流逝函数
SUBAREA *p;
p=ALL;
while(p!=NULL){
if(p->STATE=='P')
p->NEEDTIME-=1;
p=p->next;
}
cout<<endl<<"--------------------------一个时长过去了-------------------------"<<endl<<endl;
}
分区系统函数subarea():
void subarea(){ //分区系统
while(1){
int gett[100][2];
scandP(); //选择载入的程序
timego(); //时间流逝
recycle(); //回收
if(g!=0){
for(int o=1;o<=g;o++){
gett[o][0]=get[o][0];
gett[o][1]=get[o][1];
get[o][0]=0;
get[o][1]=0;
}
g=0;
cout<<"检查载入失败的程序是否能被满足条件:"<<endl;
for(int m=1;m<o;m++){
cout<<"分配失败程序队列第"<<m<<"个程序时间和大小:"<<gett[m][0]<<" "<<gett[m][1]<<endl;
optimun(gett[m][0],gett[m][1]);
}
if(g!=o-1){
printL('S');
printL('P');
printL('L');
}
}
}
}
主函数main():
int main(){
SUBAREA FI;
FI=(SUBAREA)malloc(sizeof(SUBAREA)); //创建第一个分区 即总内存
FI->START=0;
FI->SIZE=MAXS;
FI->NEEDTIME=0;
FI->STATE=‘L’;
FI->next=ALL;
FI->prior=ALL;
ALL=FI;
subarea(); //启动分区系统
return 0;
}
3 开发环境
Windows 7 + VC++6.0
4 运行结果及分析
先输入三个程序大小好时间分别为:
200 1
250 3
45 1
见图6.4.1
图6.4.1
再输入一个程序大小和时间分别为:
45 1
见图6.4.2
图6.4.2
可以看到,分区分配按照最佳适应算法实现。
5 心得体会
这次为期三周的实习,真真正正让我体会到了代码的无限乐趣!
原本打算用这学期新学的java来实现进程调度,但是不够熟悉,只好还是用更熟悉的c语言来打。经过不断地磨合修改代码,从课本学习对照,上网查资料,第一周已经过了两天,而我得出了我的进程调度的简化版本,真的很满足当时。但是陆陆续续出了很多的问题却找不到解决办法,和小伙伴一起探讨了一段时间,修改了很多bug,最终把界面优化美化后看起来很舒服,后面又依据实习要求完成了可变分区存储管的实现,心中莫名有些成就感。
在实习期间,通过和小伙伴的讨论和上网学习,真正的学到了很多东西。从刚开始的为了完成任务而学习变成了因为兴趣而学习,养成了很好的学习习惯。这次实习令人很满足,也很令人回味。
参考文献
1.胡元义,黑新宏等.《操作系统原理》.北京:电子工业出版社,2018.08:177-180
2.耿国华等.《数据结构(第二版)》.北京:高等教育出版社,2015.07(2018.02重印):60-70