操作系统实验——主存分配与释放(C语言)

目录

实验要求

代码实现

运行结果

代码解析


实验要求

1、模拟操作系统的主存分配,运用可变分区的存储管理算法设计主存分配和回收程序,并不实际启动装入作业。

2、采用最先适应法。

3、当一个新作业要求装入主存时,必须查空闲区表,从中找出一个足够大的空闲区。若找到的空闲区大于作业需要量,这是应把它分成二部分,一部分为占用区,加一部分又成为一个空闲区。

4、当一个作业撤离时,归还的区域如果与其他空闲区相邻,则应合并成一个较大的空闲区,登在空闲区表中。

5、运行所设计的程序,输出有关数据结构表项的变化和内存的当前状态。 

代码实现

#include <stdio.h>
#define CAP 1024    //初始化内存容量 
#define SYSCAP 100  //系统所占地址,从低位开始 
#define N 1000		//进程最大个数 

//作业分区 
struct USED{
	int id,sp,ep,longsize;
}a[N];

//空闲分区 
struct FREE{
	int id,sp,ep,longsize;
}b[N];

//anum为作业分区块数,bnum为空闲分区块数。初始时作业分区0块,空闲分区有1块
int anum=0,bnum=1;        
void Init()              //初始化函数,程序开始时只有一个空闲分区 
{
	b[0].id=0;b[0].sp=SYSCAP;b[0].ep=CAP;b[0].longsize=b[0].ep-b[0].sp;
	printf("\n\n--------------最先适应算法--------------\n"); 
	printf("\n内存总量为:%dk\n",CAP); 
	printf("系统占用容量为:%dk\n",SYSCAP);
	printf("---------------------------------------------\n"); 
	printf("空闲分区    起始地址    结束地址    长度\n");
	printf("   %d          %d         %d       %d\n",b[0].id,b[0].sp,b[0].ep,b[0].longsize);
	printf("---------------------------------------------\n"); 
}
void Print(int anum,int bnum)      //格式化输出作业分区和空闲分区表 
{
	int i,j;
	printf("-----------------作业分区表---------------------\n"); 
	printf("作业分区    起始地址    结束地址    长度\n");
	for(i=0;i<anum;i++)
	{
		printf("   %d          %d         %d         %d\n",a[i].id,a[i].sp,a[i].ep,a[i].longsize);
	}
	printf("------------------空闲分区表--------------------\n"); 
	printf("空闲分区    起始地址    结束地址    长度\n");
	for(i=0;i<bnum;i++)
	{
		printf("   %d          %d         %d        %d\n",i,b[i].sp,b[i].ep,b[i].longsize);
	} 
	printf("---------------------------------------------\n\n\n"); 
}
void Put()                   //进程装入作业 
{
	int size;
	printf("请输入作业%d的大小\n",anum);
	scanf("%d",&size);
	int i;
	for(i=0;i<bnum;i++)
	{
		b[i].longsize=b[i].ep-b[i].sp;       //更新各空闲分区长度 
		if(b[i].longsize>=size)     //找到第一个能放下进程的空闲分区 
		{
			a[anum].id=anum;			//空闲区id 
			a[anum].sp=b[i].sp;			//作业内存起始地址等于空闲分区起始地址 
			a[anum].ep=a[anum].sp+size; //作业的结束地址等于起始地址+作业长度 
			b[i].sp=b[i].sp+size;		//空闲分区起始地址应向后移动size 
			a[anum].longsize=size;		//更新作业地址长度到结构体数组中 
			b[i].longsize=b[i].ep-b[i].sp;//更新空闲分区地址长度 
			anum++;						//作业数+1 
			break;						//一次只放入一个作业,结束查找 
		}
	}
	Print(anum,bnum);                   //打印结果 
}

void Merge()	//合并空闲分区,查找结束地址和开始地址重合的地址进行合并 
{
	int i,j;
	if(bnum>=1)   //当空闲分区大于等于两个时,才需要检查是否存在可合并的区间 
	{
		for(i=0;i<bnum-1;i++)//从第一个开始检查,到倒数第二个停止 
		{
			if(b[i].ep==b[i+1].sp)
			{
				bnum--;     //存在可合并区间,合并后空闲区间-1 
				b[i].ep=b[i+1].ep;  //结束地址变为合并后的结束地址 
				b[i].longsize=b[i].ep-b[i].sp;//更新空闲区间长度 
				for(j=i+1;i<bnum;i++)
				{
					b[j]=b[j+1];  //删除第i+1个空闲区间 
				}
			}
		}
	}
	
}

void Sort(int anum,int bnum)//简单排序函数,将空闲区间按起始地址递增的顺序排序 
{
	int i,j,min;
	for(i=0;i<bnum;i++)
	{
		min=b[i].sp;
		for(j=i;j<bnum;j++)
		{
			if(b[j].sp<min)
			{
				min=b[j].sp;
				b[N-1]=b[j];
				b[j]=b[i];
				b[i]=b[N-1];
			}
		}
	}
}

void Remove()//回收作业 
{
	int i,j,flag=1;//flag是标志位 
	printf("请输入需要回收的作业:\n");
	scanf("%d",&i);
	//回收作业存在下面两种情况 
	
	for(j=0;j<bnum;j++)//情况1:回收作业后,被回收作业地址刚好能和已存在的空闲分区合并,不需要增加空闲分区块数 
	{
		if(a[i].ep==b[j].sp)
		{
			b[j].sp=a[i].sp;   
			b[j].longsize=b[j].ep-b[j].sp;
			flag=0;  //标志位置0,只能执行一种情况 
			break;
		}
	}
	if(flag)		//情况2:回收作业后,被回收地址不能与已存在的空闲区间合并,需要增加空闲分区块数 
	{				//创建新的空闲分区 
		
		b[bnum].sp=a[i].sp;
		b[bnum].ep=a[i].ep;
		b[bnum].longsize=a[i].longsize;
		b[bnum].id=bnum;
		bnum++;		//空闲分区个数+1 
	}
	anum--;         //移除作业后,作业数-1 
	for(;i<anum;i++)
	{
		a[i]=a[i+1];//删除第i个作业分区 
		a[i].id--; //作业id对应-1 
	}
	Sort(anum,bnum);//排序 
	Merge();//再次检查合并空闲分区,防止出现增加空闲分区后出现的可合并分区 
	Print(anum,bnum);//打印结果 
}
int main()
{
	int input;//input 代表程序运行状态
	Init();//初始化,输出 
	while(1)//写一个死循环 
	{
		printf("装入作业:1  回收作业:0  其他输入结束程序\n");
		scanf("%d",&input);
		if(input==1)
		{
			Put();
		}else if(input==0)
		{
			Remove();
		}else break;//任意数字退出循环,程序结束 
	}
	return 0;
}

运行结果

 代码解析

将内存总量,系统内存进行宏定义(有需要的直接修改宏定义的数据即可)

定义两个结构体数组,分别用于存放和空闲分区的状态数据

anum和bnum为全局变量,分别代表作业分区和空闲分区的数量 

#include <stdio.h>
#define CAP 1024    //初始化内存容量 
#define SYSCAP 100  //系统所占地址,从低位开始 
#define N 1000		//进程最大个数 
//作业分区 
struct USED{
	int id,sp,ep,longsize;
}a[N];
//空闲分区 
struct FREE{
	int id,sp,ep,longsize;
}b[N];
int anum=0,bnum=1;

  

 初始化函数,在算法程序未开始处理前打印初始数据。这里数组下标从0开始

void Init()              //初始化函数,程序开始时只有一个空闲分区 
{
	b[0].id=0;b[0].sp=SYSCAP;b[0].ep=CAP;b[0].longsize=b[0].ep-b[0].sp;
	printf("\n\n--------------最先适应算法--------------\n"); 
	printf("\n内存总量为:%dk\n",CAP); 
	printf("系统占用容量为:%dk\n",SYSCAP);
	printf("---------------------------------------------\n"); 
	printf("空闲分区    起始地址    结束地址    长度\n");
	printf("   %d          %d         %d       %d\n",b[0].id,b[0].sp,b[0].ep,b[0].longsize);
	printf("---------------------------------------------\n"); 
}

打印结果,按顺序打印对应的结构体数据

void Print(int anum,int bnum)      //格式化输出作业分区和空闲分区表 
{
	int i,j;
	printf("-----------------作业分区表---------------------\n"); 
	printf("作业分区    起始地址    结束地址    长度\n");
	for(i=0;i<anum;i++)
	{
		printf("   %d          %d         %d         %d\n",a[i].id,a[i].sp,a[i].ep,a[i].longsize);
	}
	printf("------------------空闲分区表--------------------\n"); 
	printf("空闲分区    起始地址    结束地址    长度\n");
	for(i=0;i<bnum;i++)
	{
		printf("   %d          %d         %d        %d\n",i,b[i].sp,b[i].ep,b[i].longsize);
	} 
	printf("---------------------------------------------\n\n\n"); 
}

 核心算法:将作业装入内存(具体实现方法全都写入注释里了,如果还有有不懂的可以私信我)

void Put()                   //进程装入作业 
{
	int size;
	printf("请输入作业%d的大小\n",anum);
	scanf("%d",&size);
	int i;
	for(i=0;i<bnum;i++)
	{
		b[i].longsize=b[i].ep-b[i].sp;       //更新各空闲分区长度 
		if(b[i].longsize>=size)     //找到第一个能放下进程的空闲分区 
		{
			a[anum].id=anum;			//空闲区id 
			a[anum].sp=b[i].sp;			//作业内存起始地址等于空闲分区起始地址 
			a[anum].ep=a[anum].sp+size; //作业的结束地址等于起始地址+作业长度 
			b[i].sp=b[i].sp+size;		//空闲分区起始地址应向后移动size 
			a[anum].longsize=size;		//更新作业地址长度到结构体数组中 
			b[i].longsize=b[i].ep-b[i].sp;//更新空闲分区地址长度 
			anum++;						//作业数+1 
			break;						//一次只放入一个作业,结束查找 
		}
	}
	Print(anum,bnum);                   //打印结果 
}

 核心算法:将作业回收,作业所占据的内存重新收回变为空闲区

 这里说一下代码中写的回收作业时为什么要分两种情况

情况1:

在作业回收时,进入空闲分区能直接和空闲分区直接合并,不需要增加新的空闲分区块(如下图)

 回收合并后:

情况2:

在作业回收时,进入空闲分区时不能直接和空闲分区直接合并,需要增加新的空闲分区块(如下图,黄色块为待回收的作业分区)

回收合并后:

void Remove()//回收作业 
{
	int i,j,flag=1;//flag是标志位 
	printf("请输入需要回收的作业:\n");
	scanf("%d",&i);
	//回收作业存在下面两种情况 
	
	for(j=0;j<bnum;j++)//情况1:回收作业后,被回收作业地址刚好能和已存在的空闲分区合并,不需要增加空闲分区块数 
	{
		if(a[i].ep==b[j].sp)
		{
			b[j].sp=a[i].sp;   
			b[j].longsize=b[j].ep-b[j].sp;
			flag=0;  //标志位置0,只能执行一种情况 
			break;
		}
	}
	if(flag)		//情况2:回收作业后,被回收地址不能与已存在的空闲区间合并,需要增加空闲分区块数 
	{				//创建新的空闲分区 
		
		b[bnum].sp=a[i].sp;
		b[bnum].ep=a[i].ep;
		b[bnum].longsize=a[i].longsize;
		b[bnum].id=bnum;
		bnum++;		//空闲分区个数+1 
	}
	anum--;         //移除作业后,作业数-1 
	for(;i<anum;i++)
	{
		a[i]=a[i+1];//删除第i个作业分区 
		a[i].id--; //作业id对应-1 
	}
	Sort(anum,bnum);//排序 
	Merge();		//再次检查合并空闲分区,防止出现增加空闲分区后出现的可合并分区 
	Print(anum,bnum);//打印结果 
}

排序算法,每次进行回收后按地址从大到小进行重新排序(这一步处理必须有)

作业回收时是任意的,如果先回收了高地址块不排序,在下一次检索空闲分区时会先把高地址分出去,显然这是不符合最佳适应算法的定义的

void Sort(int anum,int bnum)//简单排序函数,将空闲区间按起始地址递增的顺序排序 
{
	int i,j,min;
	for(i=0;i<bnum;i++)
	{
		min=b[i].sp;
		for(j=i;j<bnum;j++)
		{
			if(b[j].sp<min)
			{
				min=b[j].sp;
				b[N-1]=b[j];
				b[j]=b[i];
				b[i]=b[N-1];
			}
		}
	}
}

 合并函数,进行检查再合并处理,这里其实是在处理前面两种情况之后出现的第三种情况

 

回收合并后: 

void Merge()	//合并空闲分区,查找结束地址和开始地址重合的地址进行合并 
{
	int i,j;
	if(bnum>=1)   //当空闲分区大于等于两个时,才需要检查是否存在可合并的区间 
	{
		for(i=0;i<bnum-1;i++)//从第一个开始检查,到倒数第二个停止 
		{
			if(b[i].ep==b[i+1].sp)
			{
				bnum--;     //存在可合并区间,合并后空闲区间-1 
				b[i].ep=b[i+1].ep;  //结束地址变为合并后的结束地址 
				b[i].longsize=b[i].ep-b[i].sp;//更新空闲区间长度 
				for(j=i+1;i<bnum;i++)
				{
					b[j]=b[j+1];  //删除第i+1个空闲区间 
				}
			}
		}
	}
	
}

主函数。通过用户输入来调用对应处理函数

int main()
{
	int input;//input 代表程序运行状态
	Init();//初始化,输出 
	while(1)//写一个死循环 
	{
		printf("装入作业:1  回收作业:0  其他输入结束程序\n");
		scanf("%d",&input);
		if(input==1)
		{
			Put();
		}else if(input==0)
		{
			Remove();
		}else break;//任意数字退出循环,程序结束 
	}
	return 0;
}

感谢阅读~各位有什么好的建议评论或者私信我都可以~有没看懂的地方也可以私信我~

觉得有用的话点个赞再走吧~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值