操作系统课程设计:基于空闲表的磁盘首次适应算法模拟实现

#include <iostream>
#include <stdio.h>
#define N 5    //设有5个空闲区
using namespace std;
int start;    //存放首址,即第一空闲盘块号 
struct freearea	  //定义一个空闲区说明表结构,并初始化变量		
{ 
	 int ID;    //分区号
	 int startaddress;    //第一空闲盘块号 
	 int size;     //空闲盘块数 
	 int state;     //空闲区状态:1为可用空闲块*/
}freeblock[N]={{1,2,6,1},{2,9,2,1},{3,12,8,1},{4,25,12,1},{5,40,4,1}};

//为文件分配空闲盘块的函数,首次适应 
int alloc(int applyarea)		
{
	int i,tag=0;			//applyarea为申请盘块量,tag为检查是否有满足文件需要的空闲区的标志
	for(i=0;i<N;i++)		//检查空闲表是否有满足作业要求的空闲区                                                                                              
	if(freeblock[i].state==1 && freeblock[i].size>applyarea)
	{
		freeblock[i].startaddress=freeblock[i].startaddress+applyarea;
		freeblock[i].size=freeblock[i].size-applyarea;
		tag=1;		//有满足条件的空闲区时,tag置1
		return freeblock[i].startaddress-applyarea;  //返回为文件分配的第一空闲盘块号 
	}
	else
		if(freeblock[i].state==1 && freeblock[i].size==applyarea)
		{
			 freeblock[i].state=0;
			 tag=1;	
			 return freeblock[i].startaddress;	//返回为文件分配的第一空闲盘块号 
		}
		if(tag==0)
		return -1;		//没有满足条件的空闲区,分配不成功,返回-1
}
//盘块回收 
void setfree()
{
	 int s,l,tag1=0,tag2=0,tag3=0,i,j;   //tag1代表释放区的高地址是否邻接一个空闲区,tag2代表释放区的高低地址是否都邻接一个空闲区,tag3代表释放区的低地址是否邻接一个空闲区
	 cout<<"请输入释放区的起始地址,即第一空闲盘块号 :  ";
	 cin>>s;
	 cout<<"请输入释放盘块的数目:  ";
	 cin>>l;
	 cout<<"* **  ***   ****回收盘块后空闲区表的状态如上****   ***  ** *\n";	 
	 for(i=0;i<N;i++)
	 {
		 if(freeblock[i].startaddress==s+l && freeblock[i].state==1)
		 {
			   l=l+freeblock[i].size;//释放区与后一空闲区合并 
			   tag1=1;		//有与释放区高地址邻接的空闲区,tag1置1 
			   for(j=0;j<N;j++)
			   if(freeblock[j].startaddress+freeblock[j].size==s && freeblock[j].state==1)
			   {
					 freeblock[i].state=0;//空闲区i需要合并到j中,所以置为不可用 
					 freeblock[j].size=freeblock[j].size+l;//将j空闲区,释放区,i空闲区合并 
					 tag2=1;    //有与释放区上下都邻接的空闲区,tag2置1
					 break;
			   }
			   if(tag2==0)	  //仅释放区与后一空闲区相连 
			   {
			       freeblock[i].startaddress=s;
				   freeblock[i].size=l;
				   break;
			   }
		 }
	 }	 
	 if(tag1==0)	//无与释放区高地址邻接的空闲区,并检查是否低地址有邻接空闲区
	 {
	    for(i=0;i<N;i++)
		if(freeblock[i].startaddress+freeblock[i].size==s && freeblock[i].state==1)
		{
			freeblock[i].size=freeblock[i].size+l;//修改前一空闲分区的大小 
			tag3=1;	    //有与释放区低地址邻接的空闲区
			break;
		}
		if(tag3==0)		//无与释放区低地址邻接的空闲区
		for(j=0;j<N;j++)
		 if(freeblock[j].state==0)     //找一个不用的区,将释放区放入其中
		 {
			freeblock[j].startaddress=s;
		    freeblock[j].size=l;
			freeblock[j].state=1;
			break;
		 }
	 }
}
//对空闲区表中的空闲区调整的函数,使空闲区按始地址从小到大排列,空表目放在最后面
void adjust()
{
	 int i,j;
	 struct freearea middata;
	 for(i=0;i<N;i++)		//将空闲区按始地址顺序在表中排列,冒泡即可 
		for(j=0;j<N;j++)
		if(freeblock[j].startaddress>freeblock[j+1].startaddress)
		{
			middata.startaddress=freeblock[j].startaddress;
			middata.size=freeblock[j].size;
			middata.state=freeblock[j].state;
			freeblock[j].startaddress=freeblock[j+1].startaddress;
			freeblock[j].size=freeblock[j+1].size;
			freeblock[j].state=freeblock[j+1].state;
			freeblock[j+1].startaddress=middata.startaddress;
			freeblock[j+1].size=middata.size;
			freeblock[j+1].state=middata.state;
		}
		for(i=0;i<N;i++)	//将空表目放在表后面
			for(j=0;j<N;j++)
			if(freeblock[j].state==0&&freeblock[j+1].state==1)
			{
				middata.startaddress=freeblock[j].startaddress;
				middata.size=freeblock[j].size;
				middata.state=freeblock[j].state;
				freeblock[j].startaddress=freeblock[j+1].startaddress;
			    freeblock[j].size=freeblock[j+1].size;
				freeblock[j].state=freeblock[j+1].state;
				freeblock[j+1].startaddress=middata.startaddress;
				freeblock[j+1].size=middata.size;
				freeblock[j+1].state=middata.state;
			}
}
//打印空闲区
void print()
{
	 int i;
	 printf("|---------------------------------------------------------------|\n");
	 printf("|         ID       start        size        state        |\n");
	 printf("|---------------------------------------------------------------|\n");
	 for(i=0;i<N;i++)
		{
		printf("|        %3d        %3d         %3d         %3d          |\n",
			 freeblock[i].ID,freeblock[i].startaddress,freeblock[i].size,freeblock[i].state);
		printf("|---------------------------------------------------------------|\n");
		}
}
 //首次适应算法 
void First_fit()
{
    int applyarea,start;
    {
		 cout<<"请输入作业的申请量:";
		 cin>>applyarea;
		 start=alloc(applyarea);     //调用alloc()函数为作业分配空间,start为返回的始地址
		 if(start==-1)		//alloc()分配不成功时,返回-1
         cout<<"作业申请量过大,空闲区表中的存储空间无法满足,分配失败\n";
		 else
		 {
			 adjust();
			 cout<<"申请作业的起始地址为:"<<start<<endl;
			 cout<<"作业的申请量为:"<<applyarea<<endl;
			 cout<<"内存分配成功"<<endl;
		 }
    }
}
/*主函数*/
int main()	
{
loop: int ch;//算法选择标记
    cout<<"                       磁盘首次适应算法模拟实现                 "<<endl; 	 
	int choice;  //操作选择标记
	while(1)		
    {
        cout<<"      *******************************************"<<endl;
        cout<<"      1: 分配盘块             2: 回收盘块        "<<endl;
		cout<<endl;
        cout<<"      3: 查看空闲表      	 0: 退出       "<<endl;
        cout<<"      *******************************************"<<endl;
        cout<<"请输入您的操作 :";		
        cin>>choice;
        if(choice==1) // 分配盘块 
		{
			int ch;
		      First_fit();
	    
		}
        else  
			if(choice==2)  // 回收盘块
			{
			    setfree();
                adjust();
		 	    print();
			}	
            else 
				if(choice==3)    
					print();  //显示空闲分区表
                else 
					if(choice==0)
					   goto loop; //退出
                    else //输入操作有误
					{			
                         cout<<"输入有误,请重试!"<<endl;
                         continue;		
					}		
	}
	return 0;
}

 

  • 首次适应算法要求空闲分区链以地址递增的次序链接。在分配时从链首开始顺序查找,直至找到一个大小能满足要求的空闲分区为止。然后按照作业大小,从该分区中划出一块空间,分配给请求者,余下的空闲分区仍留在空闲链中。

对首次适应算法与其他三种基于顺序搜索的动态分区分配算法的分析如下:

首先,首次适应算法(first fit)属于基于顺序搜索的动态分区分配算法。为了实现动态分区分配,通常是将系统中的空闲分区链接成一个链。顺序搜索指的是依次搜索空闲分区链上的空闲分区,去寻找一个其大小能满足要求的分区。

在外存的管理中,由于连续分配具有较高的分配速度,可减少访问磁盘的I/O频率,所以它在诸多分配方式中仍占有一席之地。例如,对换空间一般采用连续分配方式。对于文件系统,当文件较小时(1-4个盘块),仍采用连续分配方式,为文件分配邻接的几个盘块;当文件较大时,便采用离散分配方式。另外对于多媒体文件,为了能减少磁头的寻道时间,也采用连续分配方式。

基于顺序搜索的动态分区分配算法有以下四种:首次适应算法、循环首次适应算法、最佳适应算法和最坏适应算法。

实例分析:

假设有五个内存分区为100 KB、500 KB、200 KB、300 KB和600 KB(按顺序排列),那么首次适应、循环首次适应、最佳适应和最坏适应算法将如何替换212 KB、417 KB、112 KB和426 KB(按顺序排列)?

首次适应算法:

为212k分配空间:依次找寻,找到第一个大于212k的空闲区,找到第二个空闲区500k>212k,分配给212k,剩余288k空闲区;

为417k分配空间:依次找寻,找到第一个大于417k的空闲区,找到第五个空闲区600k>417k,分配给417k,剩余183k空闲区;

为112k分配空间:依次找寻,找到第一个大于112k的空闲区,找到第二个空闲区288k>112k,分配给112k,剩余176k空闲区;

为426k分配空间:依次找寻,找到第一个大于426k的空闲区,未找到,此作业将等待释放空间。

循环首次适应算法:

为212k分配空间:从开头找寻,找到第一个大于212k的空闲区,找到第二个空闲区500k>212k,分配给212k,剩余288k空闲区;

为417k分配空间:在刚刚分配空间的下一个开始找,找到第五个空闲区600k>417k,分配给417k,剩余183k空闲区;

为112k分配空间:继续找寻,找到第一个大于112k的空闲区,找到第二个空闲区288k>112k,分配给112k,剩余176k空闲区;

为426k分配空间:继续找寻,找第一个大于426k的空闲区,未找到,此作业将等待释放空间。

最佳适应算法:

为212k分配空间:找到第一个跟212k大小最接近的空闲区,找到第四个空闲区300>212k,剩余88k空闲区;

为417k分配空间:找到第一个跟417k大小最接近的空闲区,找到第二个空闲区500>417,剩余83k空闲区;

为112k分配空间:找到第一个跟112k大小最接近的空闲区,找到第三个空闲区200>112k,剩余88k空闲区;

为426k分配空间: 找到第一个跟426大小最接近的空闲区,找到第五个空闲区600k>426,剩余74k空闲区。

最坏适应算法:

为212k分配空间:找到第一个大小最大的空闲区,找到第五个空闲区600>212k,剩余388k空闲区;

为417k分配空间:找到第一个大小最大的空闲区,找到第二个空闲区500>417,剩余83k空闲区;

为112k分配空间:找到第一个大小最大的空闲区,找到第三个空闲区388>112k,剩余276k空闲区;

为426k分配空间:找到第一个大小最大的空闲区,达到大小最大的空闲区300k<426k,所以不分配。

  1. 对四种顺序搜索的动态分区算法的思想分析如下:

1.首次适应算法(FF):所谓的首次的意思以地址递增链接。因此在分配内存的时候,从链首开始查找,直到找到一个大小能满足要求的空闲分区为之;

2.循环首次适应算法(NF):和首次适应算法的区别就是,在为进程分配内存空间的时候,不再是都从链首开始查找, 而是从上次找到的空闲的分区的下一个空闲分区开始查找,直到找到一个能满足要求的空闲分区;

3.最佳适应算法(BF):将所有的空闲空间按其容量从小到大的顺序形成一空闲分区链,然后进行分配 ,找符合要求的同时最小的空闲分区;

4.最坏适应算法(WF):将所有的空闲空间按其容量从大到小的顺序形成一空闲分区链,然后进行分配。

  1. 对四种算法的目的分析如下:

1.首次适应算法(first fit):从空闲分区表的第一个表目起查找该表,把最先能够满足要求的空闲区分配给作业,这种方法的目的在于减少查找文件的时间;

2.循环首次适应算法(next fit):从上次找到的空闲的分区的下一个空闲分区开始查找,直到找到一个能满足要求的空闲分区,这种方法的目的是避免低址部分留下许多很小的空闲分区,以及减小查找可用空闲分区的开销;

3.最佳适应算法(best fit):从全部空闲区中找出能满足作业要求的,且大小最小的空闲分区,这种方法能使碎片尽量小;

4.最差适应算法(worst fit):它从全部空闲区中找出能满足作业要求的、且大小最大的空闲分区,从而使链表中的节点大小趋于均匀。

  1. 对四种算法的优缺点的分析如下:

1.首次适应算法能保留高地址的大空闲区,可以为以后到达的大作业分配大的内存创造条件,同时因为大多分布在低地址有利于减少查找时间。缺点是低址部分被不断划分,会留下许多难以利用的很小的空闲分区,称为碎片。而每次查找又都是从低地址开始的,会增加查找可利用空闲分区的时间。

2.循环首次适应算法是对首次适应算法的改进,不再从最低地址分配而是从上次空闲区开始查找有利于使内存中的空闲分区分布得更均匀从而减少查找空闲分区的开销,但缺点是会缺乏大的空闲分区。

3.最佳适应算法为了加速查找,将所有空闲区从小到大形成一个空闲分区链,第一次找到的满足要求的空闲区一定最佳,避免了两种首次适应算法的“大材小用”,但缺点是会形成十分小的难以利用的碎片区。

4.最坏适应算法的策略恰好与最佳适应算法相反,是对最佳适应算法的改进:从大到小形成空闲分区链,从最大分区开始分配给作业可以使剩下的空闲区不至于太小,与前三种算法相比产生碎片的可能最小,对中小作业有利,同时查找空闲区效率很高——看第一个即可,但是缺点是缺少大的分区,对大作业不利。

  • 结论:空闲分区的分配与内存的分区(动态)相似,采用首次适应算法或最佳适应算法,它们对存储空间的利用率大致相当,都优于最坏适应算法。在系统为某新创建的文件分配空闲盘块时,先顺序地检索空闲表的各表项,直到找到第一个其大小能满足要求的空闲区,再将该盘区分配给用户(进程),同时修改空闲表。系统在对用户所释放的存储空间进行回收时,也采取类似于内存回收的方法,即要考虑回收区是否与空闲表中插入点的前区和后区相邻接,对相邻接者应予以合并。
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值