一、实验目的
了解动态分区分配方式中使用的数据结构和分配算法,并进一步加深对动态分区存储管理方式及其实现过程的理解。
二、实验环境
硬件环境:计算机一台,局域网环境;
软件环境: Windows或Linux操作系统, C语言编程环境。
三、实验内容
1、用C语言分别实现采用首次适应算法和最佳适应算法的动态分区分配过程alloc( )和回收过程free( )。其中,空闲分区通过空闲分区链来管理:在进行内存分配时,系统优先使用空闲区低端的空间。
2、假设初始状态下,可用的内存空间为640KB,并有下列的请求序列:
•作业1申请130KB。
•作业2申请60KB。
•作业3申请100KB。
•作业2释放60KB。
•作业4申请200KB。
•作业3释放100KB。
•作业1释放130KB。
•作业5申请140KB。
•作业6申请60KB。
•作业7申请50KB。
•作业6释放60KB。
请分别采用首次适应算法和最佳适应算法,对内存块进行分配和回收,要求每次分配和回收后显示出空闲分区链的情况。
①设计过程:
本实验采用内存块队列的方式进行存储管理。
- 首次适应算法:从头遍历队列,找到一个空闲且足够大的内存块,分配内存。当此空闲区大小大于请求空间大小时,将空闲区分为两部分,一部分分配给进程,另一部分为空闲区,它的大小为之前空闲区大小减去分配给进程的空间大小。
- 最佳适应算法:从头遍历队列,当找到第一个满足进程请求空间大小的空闲区时,记录此位置,并且记录该分区大小,如此后找到满足请求空间大小并且更小的分区时,更新位置。最后将位置上的分区进行分配。
- 内存回收算法:内存回收时,回收分区与空闲分区有三种关系。第一种情况为回收分区r上临一个空闲分区,第二种情况为回收分区r与下相邻空闲分区,第三种情况为回收部分r与上下空闲区相邻。Memory结构体中的flag用于判断是否空闲,进而判断回收分区是否与其合并。
②代码描述:
#include<iostream>
#include<windows.h>
#include<iomanip>
#include <vector>
#define MAX 640
using namespace std;
struct work
{
int id;
int size;
};
struct memory
{
int front_number; //开始地址
int number; //结束地址
int id; //占用程序的id
bool flag;//0为未被占用,可被回收
int size; //大小
};
work process1 = { 1,130 };
work process2 = { 2,60 };
work process3 = { 3,100 };
work process4 = { 4,200 };
work process5 = { 5,140 };
work process6 = { 6,60 };
work process7 = { 7,50 };
struct memory M[2] =
{
{0,0,0,1,0},
{0,MAX,0,0,MAX}
};//内存空间初始化,从1开始
memory temp;
int chose;
vector<memory> M_queue;//内存分区队列
//空闲分区合并
void M_merge(int mer_id)
{
//回收区地址小
if ((mer_id < M_queue.size() - 1) && (M_queue[mer_id + 1].flag == 0))
{
M_queue[mer_id + 1].size += M_queue[mer_id].size;
M_queue[mer_id + 1].front_number = M_queue[mer_id].front_number;
M_queue.erase(M_queue.begin() + mer_id);//删除队列里相应元素
}
//空闲区地址小
if (M_queue[mer_id - 1].flag == 0)
{
M_queue[mer_id - 1].size += M_queue[mer_id].size;
M_queue[mer_id - 1].number = M_queue[mer_id].number;
M_queue.erase(M_queue.begin() + mer_id);
}
}
void M_print()
{
cout << "--------------------------------------------" << endl;
cout << "内存空间占用\t程序ID\t\t内存大小" << endl;
cout << "--------------------------------------------" << endl;
for (int i = 1; i < M_queue.size(); i++)
cout << M_queue[i].front_number << " -- " << setw(3) << setfill(' ') << left << M_queue[i].number << "\t" << M_queue[i].id << "\t\t" << M_queue[i].size <<endl;
cout << "--------------------------------------------" << endl;
cout << endl << endl;
}
void alloc(work p1)
{
if (chose == 1) //首次适应算法
{
for (int i = 1; i < M_queue.size(); i++)
{
if ((M_queue[i].flag == 0) && p1.size < M_queue[i].size)
{
temp.flag = 1;
temp.id = p1.id;
temp.size = p1.size;
temp.front_number = M_queue[i].front_number;
temp.number = temp.front_number + temp.size;
//更新空闲区
M_queue[i].front_number = temp.number;
M_queue[i].size -= temp.size;
M_queue.insert(M_queue.begin() + i, temp);
break;
}
}
}
else if (chose == 2) //最佳适应算法
{
int best_num = MAX;//记录目标空闲区大小
int best_id = 1;//记录目标空闲区编号
for (int i = 1; i < M_queue.size(); i++)//找到只比p1.size大一点的空闲区,即目标空闲区
{
if ((M_queue[i].flag == 0) && (M_queue[i].size >= p1.size) && (M_queue[i].size <= best_num))
{
best_num = M_queue[i].size;
best_id = i;
}
}
temp.flag = 1;
temp.id = p1.id;
temp.size = p1.size;
temp.front_number = M_queue[best_id].front_number;
temp.number = temp.front_number + temp.size;
//更新空闲区
M_queue[best_id].front_number = temp.number;
M_queue[best_id].size -= p1.size;
M_queue.insert(M_queue.begin() + best_id, temp);
}
M_print();
}
void free(work p2)
{
int id;
for (int i = 0; i < M_queue.size(); i++)
{
if (p2.id == M_queue[i].id)
{
M_queue[i].flag = 0;
M_queue[i].id = 0;
id = i;
break;
}
}
M_merge(id);
M_print();
}
int main()
{
M_queue.push_back(M[0]);
M_queue.push_back(M[1]);//初始化内存分区队列
cout << "初始空闲区" << endl;
cout << "程序ID为0则该分区没有程序占用" << endl;
M_print();
cout << "1-首次适应算法\n2-最佳适应算法\n请选择算法:";
cin >> chose;
if (chose != 1 && chose != 2)
{
cout << "错误!" << endl;
return 0;
}
cout << endl;
cout << "作业1申请130KB" << endl;
alloc(process1);
cout << "作业2申请60KB" << endl;
alloc(process2);
cout << "作业3申请100KB" << endl;
alloc(process3);
cout << "作业2释放60KB" << endl;
free(process2);
cout << "作业4申请200KB" << endl;
alloc(process4);
cout << "作业3释放100KB" << endl;
free(process3);
cout << "作业1释放130KB" << endl;
free(process1);
cout << "作业5申请140KB" << endl;
alloc(process5);
cout << "作业6申请60KB" << endl;
alloc(process6);
cout << "作业7申请50KB" << endl;
alloc(process7);
cout << "作业6释放60KB" << endl;
free(process6);
return 0;
}
四、实验总结
存储管理可以有效地对外部存储资源和内存进行管理,可以完成存储分配,存储共享,存储保护,存储扩充,地址映射等重要功能,对操作系统的性能有很重要的影响。首次适应算法和最佳适应算法是存储管理中两个十分重要的页面置换算法。
首次适应算法从空闲分区链首开始查找,直至找到一个能满足其大小要求的空闲分区为止。然后再按照作业的大小,从该分区中划出一块内存分配给请求者,余下的空闲分区仍留在空闲分区链中。该算法倾向于使用内存中低地址部分的空闲区,在高地址部分的空闲区很少被利用,从而保留了高地址部分的大空闲区,为以后到达的大作业分配大的内存空间创造了条件。但是低地址部分不断被划分,留下许多碎片,而每次查找又都从低地址部分开始,会增加查找的开销。
最佳适应算法总是把既能满足要求,又是最小的空闲分区分配给作业。为了加速查找,该算法将所有的空闲区按大小排序后,以递增顺序形成一个空白链。这样每次找到的第一个满足要求的空闲区,必然是最优的。这样,每次分配给文件的都是最合适该文件大小的分区,但是内存中仍然存在碎片。