1.项目描述:
主存是中央处理器能够直接存取指令和数据的存储器。能否合理而有效地使用它,在很大程度上将影响整个计算机系统的性能。
设计模拟可变分区管理方式中使用首次和最佳适应以及最差适应算法实现主存的分配与回收。帮助理解在不同的存储管理方式下如何实现主存空间的分配和回收。
2.系统结构设计:
3.系统程序实现:
#include<stdio.h>
#include<stdlib.h>
#define Max 1024
int memoryLen = Max; //内存空间大小
typedef struct Aatom //结构体Alist节点的属性,以下注释称作属性节点
{
int id; //分区编号,-1表示空闲分区
int stratAdd; //分区的首地址
int length; //分区长度
int status; //分区状态,0表示空闲,1表示作业
} Natom;
typedef Natom oneAtom;
typedef struct Alist //以下注释称作分区节点
{
oneAtom* node; //节点
struct Alist* prior;//头指针
struct Alist* next; //尾指针
} Nlist;
/*********************************************************
* 功能描述:初始化一个链表
* 输入参数:无
* 输出参数:无
* 返回值: 返回一个链表指针
* 其它说明:消息字段之间用分号(;)分隔
************************************************************/
Nlist init() //初始化一个链表
{
Nlist oneList;
oneList.node = (oneAtom *)malloc(sizeof(oneAtom));
oneList.node->id = -1;
oneList.node->stratAdd = 0;
oneList.node->length = memoryLen;
oneList.node->status = 0;
oneList.prior = oneList.next = NULL;
return oneList;
}
/*********************************************************
* 功能描述:输入属性节点的id,length信息
* 输入参数:item->id 属性节点的序号
item->length 属性节点的长度
* 输出参数:输入非法字符会输出错误信息
* 返回值: 返回一个属性节点指针
* 其它说明:消息字段之间用分号(;)分隔
************************************************************/
oneAtom* input() //初始化打算申请的内存属性节点
{
int a;
char ch;
oneAtom* item = (oneAtom *)malloc(sizeof(oneAtom));
printf("Enter the id of the work (0-10000): \n");
item->id=-1;
while(1)
{
a=scanf("%d",&item->id); //获取输入语句返回值
if(!a) //如果输入的是数字以外的字符
{
//fflush(stdin);///*************//清除输入流中的字母,windows下
while(((ch=getchar())!='\n')&&ch!=EOF);
printf("Error: illegal id\nEnter the id again\n");
}
else if(item->id>-1&&item->id<10001)//如果是合法的数字,跳出输入循环
{
break;
}
else
{
printf("Error: illegal id\nEnter the id again\n");
}
}
printf("Enter the length of the work (1-%d): ",memoryLen);
while(1)
{
a=scanf("%d",&item->length);
if(!a)
{
//fflush(stdin);//清除输入流中的字母
while(((ch=getchar())!='\n')&&ch!=EOF);
printf("Error: illegal length\nEnter the length again\n");
}
else if(item->length>0&&item->id<memoryLen+1)//如果是合法的数字,跳出输入循环
{
break;
}
else
{
printf("Error: illegal length\nEnter the length again\n");
}
}
item->status = 0;
return item;
}
/*********************************************************
* 功能描述:显示内存分区
* 输入参数:无
* 输出参数:打印内存分区
* 返回值: 无
* 其它说明:消息字段之间用分号(;)分隔
************************************************************/
void Momery_state(Nlist *lList)//显示内存
{
Nlist* temp = lList;
char s1[]="id";
char s2[]="stratAdd";
char s3[]="length";
char s4[]="status";
char s5[]="free";
char s6[]="work";
printf("\n============================================================================\n");
printf("%10s\t%10s\t%10s\t%10s\n",s1,s2,s3,s4);
printf("----------------------------------------------------------------------------\n");
while (temp)
{
if(temp->node->status == 0 && temp->node->id == -1)
{
printf("%10d\t%10d\t%10d\t%10s\n",temp->node->id,temp->node->stratAdd,temp->node->length,s5);
}
else
{
printf("%10d\t%10d\t%10d\t%10s\n",temp->node->id,temp->node->stratAdd,temp->node->length,s6);
}
temp = temp->next;
}
printf("============================================================================\n");
}
/*********************************************************
* 功能描述:首次适应算法
* 输入参数:Nlist *lList 链表头结点
* 输出参数:输出成功失败信息
* 返回值: 0:成功 1;失败
* 其它说明:消息字段之间用分号(;)分隔
************************************************************/
int First_fit(Nlist *lList)
{
oneAtom* item = input(); //调用输入函数,写入节点属性
Nlist* temp = lList; //定义一个临时变量指向lList
while (temp)//指针不为空
{
if(temp->node->status == 0 && temp->node->length > item->length)//分区空闲且长度大于作业长度
{
//会建立一个新空闲分区,先给作业分区和新分区节点赋值,然后将节点链接到链表中
//Nlist *prior = temp->prior; //此分区上一个分区的地址
Nlist *next = temp->next; //此分区下一个分区的地址
int base = temp->node->stratAdd; //当前分区的首地址
oneAtom* new_node = (oneAtom*)malloc(sizeof(oneAtom));//新分区节点的属性节点
new_node->id = -1;
new_node->status = 0;
new_node->stratAdd = base + item->length; //新分区的首地址是原分区的首地址+作业大小
new_node->length = temp->node->length - item->length;//新分区的大小等于原分区大小-作业大小
temp->node = item; //给作业进行分配,只需修改链表节点的状态为作业态
temp->node->status = 1;
temp->node->stratAdd=base;
Nlist* temp_next = (Nlist*)malloc(sizeof(Nlist)); //新分区节点
temp_next->node = new_node; //保存新空闲分区的信息
temp_next->prior = NULL;
temp_next->next = NULL;
//这个函数以下代码temp链表节点是作业节点,temp_next是空闲节点
//注意这个prior和next是原分区的上一节点和下一节点
if(next == NULL) //之前链表中只有一个分区
{
//temp->node->stratAdd = 0; //将作业节点和新分区节点链接到表中
temp->next = temp_next;
temp_next->prior = temp;
}
else
{
temp_next->next = next; //保证新插入的节点会记录原先节点的下一个节点的首地址
temp_next->prior = temp; // 首尾都需要保证
temp->next = temp_next; //最后让所申请的分区节点的下一个节点指向 我们刚刚建立的临时节点
next->prior=temp_next;
}
printf("Assignment successful!\n");
return 1;
}
else if(temp->node->status == 0 && temp->node->length == item->length)//分区空闲且长度等于作业长度
{
item->stratAdd = temp->node->stratAdd;
item->status = 1; //只需要修改空闲状态为作业状态
temp->node = item;
printf("Work Assignment successful!\n");
return 1;
}
else //分区长度小于作业长度
{
temp = temp->next; //顺着链表依次向后查找
}
}
//能够跳出循环,说明没有足够大的分区
printf("Out of memory space\nWork assignment failed!!!\n");
return 0;
}
/*********************************************************
* 功能描述:回收作业分区
* 输入参数:Nlist *lList 链表头结点
* 输出参数:输出回收成功或失败信息
* 返回值: 0:成功 1;失败
* 其它说明:消息字段之间用分号(;)分隔,空闲分区不可回收
************************************************************/
int Momory_recycle(Nlist *lList)//回收
{
Nlist* temp = lList; //申请一个链表节点 指向list 的头节点
int iid; //用于存放要释放的节点的分区号
char ch;
printf("enter the id of the work to free:");
while(1)
{
int a=scanf("%d",&iid); //获取输入语句返回值
if(!a) //如果输入的是数字以外的字符
{
while(((ch=getchar())!='\n')&&ch!=EOF);
printf("Error: illegal id\nEnter the id again\n");
}
else if(iid>-1&&iid<10001) //如果是合法的数字,跳出输入循环
{
break;
}
else
{
printf("Error: illegal id\nEnter the id again\n");
}
}
while (temp)
{
if(temp->node->id == iid) //首先找到节点id=iid的节点,然后分四种情况讨论
{
// 一、特殊: 要回收的是第一个结点
if(temp->prior == NULL)
{
temp->node->status = 0;
temp->node->id = -1; //如果区号只能用正整数的话那么可以恢复这句话
if(temp->next == NULL) //表示整个表只有这一个节点
{
printf("Recycling successful!\n");
return 1;
}
else if(temp->next->node->status == 0)//后面的节点是空闲分区节点
{
Nlist* next = temp->next;
temp->node->length = temp->node->length + next->node->length;
temp->next = next->next;
if(next->next == NULL) //该节点后面的空闲分区是最后一个节点
{
free(next);
printf("Recycling successful!\n");
return 1;
}
else
{
next->next->prior = temp;
free(next);
printf("Recycling successful!\n");
return 1;
}
}
else
{
printf("Recycling successful!\n");
return 1;
}
}
//以下4种if是一般条件下的分情况讨论
if(temp->prior->node->status != 0 && temp->next->node->status != 0)//二、前后都不是空闲的分区
{
temp->node->status = 0;
temp->node->id = -1;
printf("Recycling successful!\n");
return 1;
}
if(temp->prior->node->status == 0 && temp->next->node->status == 0)//三、前面和后面都是空闲的
{
Nlist* prior = temp->prior;
Nlist* next = temp->next;
prior->node->length = prior->node->length + temp->node->length + next->node->length;
prior->next = next->next;
if(next->next == NULL)
{
free(temp);
free(next);
printf("Recycling successful!\n");
return 1;
}
else
{
next->next->prior = prior;
free(temp);
free(next);
printf("Recycling successful!\n");
return 1;
}
}
if(temp->prior->node->status == 0)// 四、前面是空闲,后面是作业
{
Nlist* prior = temp->prior;
prior->next = temp->next;
temp->next->prior = prior;
prior->node->length += temp->node->length;
free(temp);
printf("Recycling successful!\n");
return 1;
}
if(temp->next->node->status == 0)//五、前面是作业,后面是空闲
{
Nlist* next = temp->next;
temp->node->length += next->node->length;
temp->node->status = 0;
temp->node->id = -1;
temp->next = next->next;
if(next->next == NULL)// 此时来判断 temp->next 是否是系统的最后一个结点
{
free(next);
printf("Recycling successful!\n");
return 1;
}
else
{
next->next->prior = temp;
free(next);
printf("Recycling successful!\n");
return 1;
}
}
}
temp = temp->next;
}
printf("No job with this ID was found\nRecycle failed!!!\n");
return 0;
}
/*********************************************************
* 功能描述:最佳适应算法
* 输入参数:Nlist *lList 链表头结点
* 输出参数:输出分配成功或失败信息
* 返回值: 0:成功 1;失败
* 其它说明:消息字段之间用分号(;)分隔
************************************************************/
int Best_fit(Nlist *lList)
{
int minn = memoryLen+10; //记录最小分区的结点的大小
int base_min = 0; //记录最小分区的结点的起始地址
Nlist* temp = lList;
oneAtom* item = input(); //获得属性节点的id,length
while (temp) //这个循环只是找能满足作业的最小分区地址,但如果相等可直接结束
{
//如果分区未分配就要进行比较操作,并且记录差值和分区的id号
if(temp->node->status == 0 && temp->node->length > item->length)
{
if(temp->node->length < minn)//遍历找能满足作业的最小分区
{
minn = temp->node->length;
base_min = temp->node->stratAdd;
}
temp = temp->next;
}
else if(temp->node->status == 0 && temp->node->length == item->length)
//如果想等,一定是最小分区,无需再比较
{
int base = temp->node->stratAdd;
temp->node = item;
temp->node->status = 1;
temp->node->stratAdd = base;
printf("Assignment successful!\n");
return 1;
}
else
temp = temp->next;
}
if(minn==memoryLen+10)//如果minn的值未发生变化,说明没有满足条件的空闲分区
{
printf("Out of memory space\nWork assignment failed!!!\n");
return 0;
}
//因为可能没有任何一个空间可以满足要求需要做一个判断处理
temp = lList;
while (temp)//这个循环是通过上个循环找到的最小分区地址进行分配
{
if(temp->node->stratAdd == base_min)
{
Nlist *next = temp->next; //此分区下一个分区的地址
int base = temp->node->stratAdd; //当前分区的首地址
oneAtom* new_node = (oneAtom*)malloc(sizeof(oneAtom));//新分区节点的属性节点
new_node->id = -1;
new_node->status = 0;
new_node->stratAdd = base + item->length; //新分区的首地址是原分区的首地址+作业大小
new_node->length = temp->node->length - item->length;//新分区的大小等于原分区大小-作业大小
temp->node = item; //给作业进行分配,只需修改链表节点的状态为作业态
temp->node->status = 1;
temp->node->stratAdd=base;
Nlist* temp_next = (Nlist*)malloc(sizeof(Nlist)); //新分区节点
temp_next->node = new_node; //保存新空闲分区的信息
temp_next->prior = NULL;
temp_next->next = NULL;
//这个函数以下代码temp链表节点是作业节点,temp_next是空闲节点
//注意这个prior和next是原分区的上一节点和下一节点
if(next == NULL) //之前链表中只有一个分区
{
temp->next = temp_next;
temp_next->prior = temp;
}
else
{
temp_next->next = next; //保证新插入的节点会记录原先节点的下一个节点的首地址
temp_next->prior = temp;// 首尾都需要保证
temp->next = temp_next; //最后让所申请的分区节点的下一个节点指向 我们刚刚建立的临时节点
next->prior=temp_next;
}
printf("Assignment successful!\n");
return 1;
}
temp=temp->next;
}
return -1;
}
/*********************************************************
* 功能描述:最差适应算法
* 输入参数:Nlist *lList 链表头结点
* 输出参数:输出分配成功或失败信息
* 返回值: 0:成功 1;失败
* 其它说明:消息字段之间用分号(;)分隔
************************************************************/
int Worst_fit(Nlist *lList)
{
int maxx = -1; //记录最小分区的结点的大小
int base_max = 0; //记录最小分区的结点的起始地址
Nlist* temp = lList;
oneAtom* item = input();//获得属性节点的id,length
while (temp) //这个循环只是找能满足作业的最小分区地址,但如果相等可直接结束
{
//如果分区未分配就要进行比较操作,并且记录差值和分区的id号
if(temp->node->status == 0 && ((temp->node->length > item->length)||(temp->node->length = item->length)))
{
if(temp->node->length > maxx)//遍历找能满足作业的最小分区
{
maxx = temp->node->length;
base_max = temp->node->stratAdd;
}
temp = temp->next;
}
else if(temp->node->status == 0 && temp->node->length == memoryLen)
//如果想等,一定是最小分区,无需再比较
{
int base = temp->node->stratAdd;
temp->node = item;
temp->node->status = 1;
temp->node->stratAdd = base;
printf("Assignment successful!\n");
return 1;
}
else
temp = temp->next;
}
if(maxx==-1)//如果minn的值未发生变化,说明没有满足条件的空闲分区
{
printf("Out of memory space\nWork assignment failed!!!\n");
return 0;
}
temp = lList;
while (temp)//这个循环是通过上个循环找到的最小分区地址进行分配
{
if(temp->node->stratAdd == base_max)
{
Nlist *next = temp->next; //此分区下一个分区的地址
int base = temp->node->stratAdd; //当前分区的首地址
oneAtom* new_node = (oneAtom*)malloc(sizeof(oneAtom));//新分区节点的属性节点
new_node->id = -1;
new_node->status = 0;
new_node->stratAdd = base + item->length; //新分区的首地址是原分区的首地址+作业大小
new_node->length = temp->node->length - item->length;//新分区的大小等于原分区大小-作业大小
temp->node = item; //给作业进行分配,只需修改链表节点的状态为作业态
temp->node->status = 1;
temp->node->stratAdd=base;
Nlist* temp_next = (Nlist*)malloc(sizeof(Nlist)); //新分区节点
temp_next->node = new_node; //保存新空闲分区的信息
temp_next->prior = NULL;
temp_next->next = NULL;
//这个函数以下代码temp链表节点是作业节点,temp_next是空闲节点
//注意这个prior和next是原分区的上一节点和下一节点
if(next == NULL) //之前链表中只有一个分区
{
//temp->node->stratAdd = 0;//将作业节点和新分区节点链接到表中
temp->next = temp_next;
temp_next->prior = temp;
}
else
{
temp_next->next = next; //保证新插入的节点会记录原先节点的下一个节点的首地址
temp_next->prior = temp; //首尾都需要保证
temp->next = temp_next; //最后让所申请的分区节点的下一个节点指向 我们刚刚建立的临时节点
next->prior=temp_next;
}
printf("Assignment successful!\n");
return 1;
}
temp=temp->next;
}
return -1;
}
/*********************************************************
* 功能描述:主函数
* 输入参数:无
* 输出参数:提示菜单信息
* 返回值: 0
* 其它说明:消息字段之间用分号(;)分隔
************************************************************/
int main()
{
Nlist lList = init();//调用初始化函数,返回值是链表头结点
int choice;
int key=1; //钥匙作为结束程序的参数
char ch;
printf("You can choose one of the following choices!\n");
while(key)
{
printf("1: First Fit\n2: Best Fit\n3: Worst Fit\n4: Memory Release\n5: Memory Status\n6: exit\nYour choices(1-6): ");
while(1)
{
int a=scanf("%d",&choice);//获取输入语句返回值
if(!a) //如果输入的是数字以外的字符
{
while(((ch=getchar())!='\n')&&ch!=EOF);
printf("Error: illegal choice\nEnter the choice again\n");
}
else if(choice>0&&choice<7)//如果是合法的数字,跳出输入循环
{
break;
}
else
{
printf("Error: illegal choice\nEnter the choice again\n");
}
}
switch (choice)
{
case 1: //1.首次适应算法
First_fit(&lList);
break;
case 2: //2.最佳适应算法
Best_fit(&lList);
break;
case 3: //3.最差适应算法
Worst_fit(&lList);
break;
case 4: //4.内存回收
Momory_recycle(&lList);
break;
case 5: //5.显示内存状况
Momery_state(&lList);
break;
default: //6.退出
key=0;
break;
}
if(key==1)
{
system("pause");//windows下
//sleep(2);//linux下
printf("Enter your next choice from the following choices!\n");
}
}
return 0;
}
4.程序实现结果: