动态内存分配

操作系统与数据结构的关系

数据结构与算法
队列
存储管理表
排序
目录树
操作系统

本次基于某个操作系统作业—— 动态分区分配方式的模拟:
(1)c语言实现。
(2)假设初始状态可用内存空间为640KB,并有下面请求序列:
作业1申请130KB ,作业2申请60KB,作业3申请100KB ,作业2释放60KB,
作业4申请200KB ,作业3释放100KB,作业1释放130KB ,作业5申请140KB,
作业6申请60KB ,作业7申请50KB ,作业6释放60KB
分别采用首次适应算法和最佳适应算法进行内存块的分配和回收,要求每次分配和回收后显示出空闲内存分区链的情况。

通过阅读课本:记录

动态分区分配:

又称可变分区分配,它根据进程实际需要,动态分配内存空间给它。

数据结构
1.描述 空闲分区 的情况;
2.描述 已分配分区 的情况;

·
·

  1. 常用的数据结构有
1.空闲分区表,在系统中设置一张空闲分区表,用于记录每个空闲分区的情况;
——每个空闲分区占一个表目,表目中包括分区号、分区大小和分区地址等数据项
**图1.1**

2.空闲分区链,为了实现对空闲分区的分配和链接,在每个分区的起始部分
设置一些用于控制分区分配的信息,以及用于链接分区所用的前指向针,在
分区尾部则设置一后向指针;

通过前、后向链接指针,把所有的空闲分区链接成一个双向链;
**图1.2**

为检索方便,在分区尾部重复设置状态位和分区大小表目;
当分区被分配出去以后,把状态位由“0”改为“1”,此时,前后指针无意义。

·

分区号分区大小(KB)分区地址(K)状态
15085空闲
232155空闲
370275空闲
460532空闲
5

-------------------------------------------图-1.1 空闲分区表-----------------------------------

·

在这里插入图片描述
-----------------------------------图-1.2 空闲链结构-----------------------------------
链表采用双链表结构,data定义为空闲分区表类型,空闲分区表结构体定义表内的四个变量。

  1. 动态分区操作:
  • 1)分配内存

系统应利用某种分配算法,从空闲分区链(表)中找到所需大小的分区。设请求的分区大小为u.size,表中每个空闲分区的大小可表示为m.size。
在这里插入图片描述
---------------------图-2.1内存分配流程-------------------------------

这里的分配操作:

按某种分配算法检查内存空间,分配,分配出的内存占用当前空闲空间,
于是在链表结点剩余(m.size=m.size - u.size)
于上流程图中(m.size - u.size<=size),
size是事先规定的不在切割的剩余区的大小,说明多余部分太小,不再切割,
该空闲结点被删除
  • 2)回收内存
    当进程运行完毕释放内存时,系统根据回收区的首址,从空闲区链(表)中找到相应的插入点,此时可能出现以下四种情况之一:

(1) 回收区与插入点的前一个空闲分区F1相邻接,见图2-2(a)。此时应将回收区与插入点的前一分区合并,不必为回收分区分配新表项,而只需修改其前一分区F1的大小。
(内存分区表地址一般是从上到下地址增加,这里 插入点的地址=上一结点首地址+内存大小)
在这里插入图片描述
-----------图-2.2(a)------------

一道例题加深理解:
在这里插入图片描述
(2) 回收分区与插入点的后一空闲分区F2相邻接,见图2-2(b)。此时也可将两分区合并,形成新的空闲分区,但用回收区的首址作为新空闲区的首址,大小为两者之和。
在这里插入图片描述
-----------图-2.2(b)------------

(3) 回收区同时与插入点的前、后两个分区邻接,见图2-2(c)。此时将三个分区合并,使用F1的表项和F1的首址,取消F2的表项,大小为三者之和。
  在这里插入图片描述
---------------图-2.2(c)------------

(4) 回收区既不与F1邻接,又不与F2邻接。这时应为回收区单独建立一个新表项,填写回收区的首址和大小,并根据其首址插入到空闲链中的适当位置。

图2-3表示出了内存回收时的流程。
在这里插入图片描述
·

3. 基于顺序搜索的动态分区分配算法

  1. 首次适应(first fit,FF)算法

FF算法要求空闲分区链以地址递增的次序链接。

在分配内存时,从链首开始顺序查找,直至找到一个大小能满足要求的空闲分区为止。

然后再按照作业的大小,从该分区中划出一块内存空间,分配给请求者,余下的空闲分区仍留在空闲链中。

若从链首直至链尾都不能找到一个能满足要求的分区,则表明系统中已没有足够大的内存分配给该进程,

内存分配失败,返回。

  1. 最佳适应(best fit,BF)算法

每次为作业分配内存时,总是把能满足要求、又是最小的空闲分区分配给作业,避免“大材小用”。

为了加速寻找,该算法要求将所有的空闲分区按其容量以从小到大的顺序形成一空闲分区链。
(在上一算法每次查找前,先排序)

===========================================================

题目:假设初始状态可用内存空间为640KB,并有下面请求序列:
作业1申请130KB ,作业2申请60KB,作业3申请100KB ,作业2释放60KB,
作业4申请200KB ,作业3释放100KB,作业1释放130KB ,作业5申请140KB,
作业6申请60KB ,作业7申请50KB ,作业6释放60KB
分别采用首次适应算法和最佳适应算法进行内存块的分配和回收,要求每次分配和回收后显示出空闲内存分区链的情况。
`

空闲分区表结构描述:
在这里插入图片描述

typedef struct Free_PartList  //空闲分区表结构设计
 {
int ID;    //分区号
int size;   //分区大小
int address;//分区地址
int flag;   //状态
 }Elemtype;

·
空闲链结构描述

typedef struct Free_Dnode    //空闲双向链表
{
	Elemtype date;
	struct Free_Dnode *prior;
	struct Free_Dnode *next;
}Free_DLinkNode,*FPtr;

初始内存分配
初始化一个640KB内存空间。

/*==========================================
函数功能:初始化一个640KB内存空间
函数输入:无
函数输出:链表头指针
============================================*/
Free_DLinkNode *initialize_DLkList(void)
{
	Free_DLinkNode *head;
	Free_DLinkNode *last;
	
	//申请一个头结点和640KB空间的结点 
	head=(Free_DLinkNode *)malloc(sizeof(Free_DLinkNode));  	
	last=(Free_DLinkNode *)malloc(sizeof(Free_DLinkNode)); 

	if(head==NULL) exit(1);  //存储空间分配失败
	
	//指针域分配 
	head->next=last;         
	head->prior=NULL;
	last->prior=head;
	last->next=NULL;
	
	//数据域初始化 
	last->date.address=0;   
	last->date.flag=FREE;
	last->date.ID=FREE;
	last->date.size=MAX_Space;
 }

·

内存分配操作

//实现内存分配
int alloc(int tag)
{
	int ID,size1;
	printf("请输入作业号:"); 
	scanf("%d",ID);
	printf("请输入所需内存大小:");
	scanf("%d",size1);
	if (ID<=0 || size1<=0)
	{
		printf("ERROR,请输入正确的ID和请求大小");break;
	}
	
	if (tag==1)//采用首次适应算法
	{
		if(first_fit(ID,size1))
		{
		    printf("分配成功!");		
		}
		else printf("分配成功!");break;
	}
	
	else
	{
		if (best_fit(ID,size1))
		{
			printf("分配成功!");
		}
		else printf("分配成功!");break;
	}
	
}

(每个空闲区的大小m.size,请求分区的大小u.size)
当m.size-u.size<=SIZE(SIZE是事先规定的不再切割的剩余分区的大小),便不再切割,将整个分区分配给请求者。

思路,define一个常量SIZE,在分配内存时当surplus在(0,SIZE)范围时,将内存完全分配;而在内存回收时似乎并不需要操作,虽然在分配内存时,定义一个结点temp存放已分配内存,释放时将temp删掉,然而surplus在该该范围时,完全分配;所以temp仅在surplus大于SIZE时起作用,而此时只需修改if语句即可。

这两位博主写的很详细,超爱。

https://blog.csdn.net/weixin_39282491/article/details/81045441
https://blog.csdn.net/qq_38898129/article/details/84947687

附上今晚改的代码

#include<stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define  MAX_Size 640
#define FREE 0 //空闲状态
#define BUSY 1 //已用状态
#define SIZE 10//最小分区 

typedef struct Free_PartList  //空闲分区表结构设计
 {
int ID;    //分区号
int size;   //分区大小
int address;//分区地址
int flag;   //状态
 }Elemtype;
 
 typedef struct Free_Dnode    //空闲双向链表
{
	Elemtype date;
	struct Free_Dnode *prior;
	struct Free_Dnode *next;
}Free_DLinkNode, *FPtr;

Free_DLinkNode *head;
Free_DLinkNode *last;
Free_DLinkNode *initialize_DLkList(void);//初始化 
int first_fit(int ID,int size);//首次适应算法 
int best_fit(int ID,int size); //最佳适应算法  
int alloc(int tag);  //内存分配
int free(int ID);//主存回收
void show();//查看分配
void Destroy(Free_DLinkNode *p);//销毁节点
void menu();

Free_DLinkNode *initialize_DLkList(void)
{
	
	//申请一个头结点和640KB空间的结点 
	head=(Free_DLinkNode *)malloc(sizeof(Free_DLinkNode));  	
	last=(Free_DLinkNode *)malloc(sizeof(Free_DLinkNode)); 

	if(head==NULL) exit(1);  //存储空间分配失败
	
	//指针域分配 
	head->next=last;         
	head->prior=NULL;
	last->prior=head;
	last->next=NULL;
	
	//数据域初始化 
	last->date.address=0;   
	last->date.flag=FREE;
	last->date.ID=FREE;
	last->date.size=MAX_Size;
 }
 
 //实现内存分配
int alloc(int tag)
{
	int ID,size1;
	printf("请输入作业号:"); 
	scanf("%d", &ID);
	printf("\n请输入所需内存大小:");
	scanf("%d", &size1);
	if (ID<=0 || size1<=0)
	{
		printf("\nERROR,请输入正确的ID和请求大小");
		return 0;
	}
	
	if (tag==1)//采用首次适应算法
	{
		if(first_fit(ID,size1))
		{
		    printf("\n分配成功!");		
		}
		else printf("\n分配失败!");
		return 1;
	}
	
	else
	{
		if (best_fit(ID,size1))
		{
			printf("\n分配成功!");
		}
		else printf("\n分配失败!");
		return 1;
	}
	
}

int first_fit(int ID, int size)//首次适应算法
{
	 FPtr temp=(FPtr)malloc(sizeof(Free_DLinkNode)); 
	 Free_DLinkNode *p=head->next;  //从头结点的下一个结点开始查找 
	 temp->date.ID=ID;   //temp —进程占用的内存 
	 temp->date.size=size;
	 temp->date.flag=BUSY;
	while(p)
	{
		if (p->date.flag==FREE && p->date.size>=size && p->date.size<=size+SIZE)//请求大小刚好满足
		{
			p->date.flag=BUSY;
			p->date.ID=ID;
			return 1;
			break;
		}
		
		if (p->date.flag==FREE && p->date.size>size+SIZE)//free空间大于需求情况 
		{
			//将temp链接在p前
			temp->next=p;    
		    p->prior->next=temp;
			temp->prior=p->prior;
			p->prior=temp;
			
			temp->date.address=p->date.address; //temp的始地址修改为p的始地址 
		    p->date.address=temp->date.address+temp->date.size;//修改 p的始地址
		    p->date.size-=size;  //修改p大小 
		    return 1;
		    break;
		}
		p=p->next;
	}
	return 0;
}

int best_fit(int ID,int size)//最佳适应算法
{
	int Surplus;//记录可用内存与需求内存的差值
	FPtr temp=(FPtr)malloc(sizeof(Free_DLinkNode));
	Free_DLinkNode *p=head->next;
	temp->date.ID=ID;
	temp->date.size=size;
	temp->date.flag=BUSY;
	Free_DLinkNode *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//找到了最佳位置
	{
		if(Surplus<=SIZE&&Surplus>0)
		{
			q->date.flag=BUSY;
			q->date.ID=ID;
			return 1;
		}
		else
		{
		temp->next=q;
		temp->prior=q->prior;
		temp->date.address=q->date.address;
		q->prior->next=temp;
		q->prior=temp;
		q->date.size=Surplus;
		q->date.address+=size;
		return 1;
	    }
	}
}

int free(int ID)//主存回收
{
	Free_DLinkNode *p=head->next;
	while(p)
	{
		if (p->date.ID==ID)//找到要回收的ID区域
		{
			p->date.flag=FREE;
			p->date.ID=FREE;
			
			//判断P与前后区域关系
			if (p->prior->date.flag==FREE&&p->next->date.flag!=FREE)
			{
				p->prior->date.size+=p->date.size;
				
				//相当于把p删掉 
				p->prior->next=p->next;
				p->next->prior=p->prior;
			}
			if (p->prior->date.flag!=FREE&&p->next->date.flag==FREE)
			{
				p->date.size+=p->next->date.size;
				if(p->next->next)
				{
					p->next->next->prior=p;
				    p->next = p->next->next;
				}
				else p->next=p->next->next;
			}
			if(p->prior->date.flag==FREE&&p->next->date.flag==FREE)
			{
				p->prior->date.size+=p->date.size+p->next->date.size;
				if(p->next->next)
				{
				p->next->next->prior=p->prior;
				p->prior->next=p->next->next;
				}
				else p->prior->next=p->next->next;
			}
			if(p->prior->date.flag!=FREE&&p->next->date.flag!=FREE)
			{
				
			}
			break;
		}
		p=p->next;
	}
	printf("回收成功!\n"); 
	return 1;
}
void Destroy(Free_DLinkNode *p)
{
 
}

void show()
{
	printf("\n************************************************\n");
	printf("内存分配情况");
	printf("\n************************************************\n");
	Free_DLinkNode *p=head->next;
	while(p)
	{
		printf("分区号:");
		if (p->date.ID==FREE)
		printf("FREE\n");
		else printf("%d\n",p->date.ID);
		printf("起始地址:%d\n",p->date.address);
		printf("内存大小:%d\n",p->date.size);
		printf("分区状态:");
		if (p->date.flag==FREE)
		    printf("空闲\n");
		else
		    printf("已分配\n");
		printf("************************************************\n");
		p=p->next;
	}
}

void menu()//菜单
{
	int tag=0;
	int ID;
	initialize_DLkList();
	printf("分区模拟:");
	while(tag!=5)
	{
		printf("\n\n输入要进行的操作:");
		printf("1-首次适应算法,2-最佳适应算法,3-内存回收,4-显示内存状况,5-退出\n");
		    scanf("%d", &tag);
		switch(tag)
			{
		    case 1:
				alloc(tag);
				    break;
			case 2:
					alloc(tag);
					break;
			case 3:
				printf("亲输入需要回收的ID号:");
				scanf("%d\n", &ID);
				free(ID);
					break;
			case 4:
				show();
				break;
		    }
	}
 
}

int main()
{
	menu();
}
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验一 进程调度 编写并调试一个模拟的进程调度程序,采用“短进程优先”调度算法对五个进程进行调度。以加深对进程的概念及进程调度算法的理解. 下面是采用动态优先数的调度程序,可作参考。  例题: 设计一个有 N个进程共行的进程调度程序。   进程调度算法:采用最高优先数优先的调度算法(即把处理机分配给优先数最高的进程)和先来先服务算法。   每个进程有一个进程控制块( PCB)表示。进程控制块可以包含如下信息:进程名、优先数、到达时间、需要运行时间、已用CPU时间、进程状态等等。   进程的优先数及需要的运行时间可以事先人为地指定(也可以由随机数产生)。进程的到达时间为进程输入的时间。   进程的运行时间以时间片为单位进行计算。   每个进程的状态可以是就绪 W(Wait)、运行R(Run)、或完成F(Finish)三种状态之一。   就绪进程获得 CPU后都只能运行一个时间片。用已占用CPU时间加1来表示。   如果运行一个时间片后,进程的已占用 CPU时间已达到所需要的运行时间,则撤消该进程,如果运行一个时间片后进程的已占用CPU时间还未达所需要的运行时间,也就是进程还需要继续运行,此时应将进程的优先数减1(即降低一级),然后把它插入就绪队列等待CPU。   每进行一次调度程序都打印一次运行进程、就绪队列、以及各个进程的 PCB,以便进行检查。重复以上过程,直到所要进程都完成为止。实验二 作业调度 一、实验目的:用高级语言编写和调试一个或多个作业调度的模拟程序,以加深对作业调度算法的理解。 二、实验内容: 1.写并调试一个单道处理系统的作业等待模拟程序。 2.作业等待算法:分别采用先来先服务(FCFS)、响应比高者优先(HRN)的调度算法。 3.由于在单道批处理系统中,作业一投入运行,它就占有计算机的一切资源直到作业完成为止,因此调度作业时不必考虑它所需要的资源是否得到满足,它所占用的 CPU时限等因素。 4.每个作业由一个作业控制块JCB表示,JCB可以包含如下信息:作业名、提交时间、所需的运行时间、所需的资源、作业状态、链指针等等。作业的状态可以是等待W(Wait)、运行R(Run)和完成F(Finish)三种状态之一。每个作业的最初状态总是等待W。 5.对每种调度算法都要求打印每个作业开始运行时刻、完成时刻、周转时间、带权周转时间,以及这组作业的平均周转时间及带权平均周转时间。 三、思考:比较各种算法的优缺点。 实验三 动态分区分配方式的模拟 1、实验目的:了解动态分区分配方式中的数据结构和分配算法,并进一步加深对动态分区存储管理方式及其实现过程的理解 2、实验内容: (1)用C语言分别实现采用首次适应算法和最佳适应算法的动态分区分配过程和回收过程。其中,空闲分区通过空闲分区链(表)来管理;在进行内存分配时,系统优先使用空闲区低端的空间。 (2)假设初始状态下,可用的内存空间为640KB,并有下列请求序列: •作业1申请130KB •作业2申请60KB •作业3申请100KB作业2释放60KB •作业4申请200KB •作业3释放100KB作业1释放130KB •作业5申请140KB •作业6申请60KB •作业7申请50KB •作业8申请60KB 请分别采用首次适应算法和最佳适应算法进行内存的分配和回收,要求每次分配和回收后显示出空闲内存分区链的情况。 3、思考:讨论各种分配算法的特点。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值