FF首次适应算法与BF最佳适应算法(C++实现)

【实验目的】

本实验主要练习主存的各种分配和回收。所谓分配,就是解决多道作业或多进程如何共享主存空间的问题。所谓回收,就是当作业运行完成时,将作业或进程所占用的主存空间归还给系统。主存的分配和回收的实现就是与主存储器的管理方式有关的。通过本实验理解在不同的存储管理方式下,如何实现主存空间的分配与回收。

【实验原理】

  1. 首次适应算法(First Fit)
    该算法从空闲分区链首开始查找,直至找到一个能满足其大小要求的空闲分区为止。然后再按照作业的大小,从该分区中划出一块内存分配给请求者,余下的空闲分区仍留在空闲分区链中。
    特点:该算法倾向于使用内存中低地址部分的空闲区,在高地址部分的空闲区很少被利用,从而保留了高地址部分的大空闲区。显然为以后到达的大作业分配大的内存空间创造了条件。
    缺点:低地址部分不断被划分,留下许多难以利用、很小的空闲区,而每次查找又都从低地址部分开始,会增加查找的开销。
  2. 最佳适应算法(Best Fit)
    该算法总是把既能满足要求,又是最小的空闲分区分配给作业。为了加速查找,该算法要求将所有的空闲区按其大小排序后,以递增顺序形成一个空白链。这样每次找到的第一个满足要求的空闲区,必然是最优的。孤立地看,该算法似乎是最优的,但事实上并不一定。因为每次分配后剩余的空间一定是最小的,在存储器中将留下许多难以利用的小空闲区。同时每次分配后必须重新排序,这也带来了一定的开销。
    特点:每次分配给文件的都是最合适该文件大小的分区。
    缺点:内存中留下许多难以利用的小的空闲区。

【实验内容】

采用可变式分区管理,使用首次或最佳适应算法实现主存的分配与回收。

1.数据结构与符号说明

1.1全局变量
FF首次适应算法与BF最佳适应算法(C++实现)_第1张图片
1.2 PST 类
FF首次适应算法与BF最佳适应算法(C++实现)_第2张图片
1.3 Memory 类
FF首次适应算法与BF最佳适应算法(C++实现)_第3张图片

2. 程序流程图与源程序

2.1 程序流程图
首次适应算法分配框图和首次适应算法回收框图分别如图3.1和图3.2所示,最佳适应算法则在首次适应算法基础上加上对内存按大小排序,流程图几乎一致,这里不再给出。
FF首次适应算法与BF最佳适应算法(C++实现)_第4张图片
FF首次适应算法与BF最佳适应算法(C++实现)_第5张图片
2.2 源程序

#include
#include
#define OK 0
#define ERROR -1
#define MAXSIZE 10	// 默认表长最大为10
using namespace std;
string Unallocated = "Unallocated";	// 未分配
string Allocated = "Allocated";	// 已分配
string Empty = "Empty";		// 空表目

class Memory;
class PST	// Partition Specification Table分区说明表
{
public:
	PST();	// 构造函数
	void display();	// 打印分区说明表
	void sort();	// 表目按分区从小到大排序
	int search(int start);	// 寻找起始地址为start的分区,并返回其编号
	void Move_Forward(int n);	// 从第n个到最后一个非空表项,每一表项向前移
	void Move_Backwark(int n);	// 从第n个到最后一个非空表项,每一表项向后移
	friend class Memory;
private:
	int *Start_Address;		// 分区起始地址
	int *Partition_Size;	// 分区大小
	string *Partition_Status;	// 分区状态
	int Real_Length;		// 实际表长(即表中的分区数)
};
PST::PST()	// 构造函数,初始化
{
	Start_Address = new int[MAXSIZE];
	Partition_Size = new int[MAXSIZE];
	Partition_Status = new string[MAXSIZE];
	for (int i = 0; i < MAXSIZE; i++) {
		Start_Address[i] = ERROR;	// 将所有表目置空
		Partition_Size[i] = ERROR;
		Partition_Status[i] = Empty;
	}
	Real_Length = 0;	// 实际表长为0
}
void PST::display()// 打印分区说明表
{
	cout << "\tstart" << "\tsize" << "\tstatus" << endl;
	cout << "\t━━━━━━━━━━━━━━━━━━━━━━━━━━━━"<<endl;
	if (Real_Length > 0) {
		for (int i = 0; i < Real_Length; i++) {
			cout << "\t" << Start_Address[i] << "(KB)\t" << Partition_Size[i] << "(KB)\t" << Partition_Status[i] << endl;
		}
	}
	for (int i = Real_Length; i < MAXSIZE; i++) {
		cout << "\t\t\tEmpty" << endl;
	}
	cout << endl;
}
void PST::sort()	// 表目按分区从小到大排序(最佳适应算法中使用)
{
	int i, j, temp;
	for (i = 0; i < Real_Length - 1; i++) {	// 使用冒泡排序将分区按大小升序排列
		for (j = 0; j < Real_Length - 1 - i; j++) {
			if (Partition_Size[j] > Partition_Size[j + 1]) {
				temp = Partition_Size[j];
				Partition_Size[j] = Partition_Size[j + 1];
				Partition_Size[j + 1] = temp;
				temp = Start_Address[j];
				Start_Address[j] = Start_Address[j + 1];
				Start_Address[j + 1] = temp;
			}
		}
	}
}
int PST::search(int start)	// 寻找起始地址为start的分区,并返回其编号
{
	for (int i = 0; i < Real_Length; i++) {
		if (Start_Address[i]==start){
			return i;
		}
	}
	return ERROR;	// 若不存在则返回-1
}
void PST::Move_Forward(int n) // 从第n个到最后一个非空表项,每一表项向前移
{
	for (int i = n ; i < Real_Length; i++) {
		Start_Address[i] = Start_Address[i + 1];
		Partition_Size[i] = Partition_Size[i + 1];
		Partition_Status[i] = Partition_Status[i + 1];
	}
}
void PST::Move_Backwark(int n)	// 从第n个到最后一个非空表项,每一表项向后移
{
	for (int i = Real_Length - 1; i > n; i--) {
		Start_Address[i] = Start_Address[i - 1];
		Partition_Size[i] = Partition_Size[i - 1];
		Partition_Status[i] = Partition_Status[i - 1];
	}
	Start_Address[n] = Partition_Size[n] = ERROR;
	Partition_Status[n] = Empty;
}

class Memory
{
public:
	Memory();	// 构造函数
	void display();	// 打印主存状态
	int allocate(int jobs, int choice);	// 分配
	void recycle(int start);	// 回收
private:
	PST Free_Table, Allocated_Table;	// 空闲分区表,已分配区表
};
Memory::Memory()	// 构造函数,手动初始化主存空间状态
{	
	int num;	// num:分区数
	cout << "\n    Input the num of partitions in Memory:    ";
	cin >> num;	// 手动输入分区数
	int start, size, state;	// 分区起始地址,大小,状态
	cout << "\n    Input the start address, size, status of each partition:" << endl;
	cout << "    (Tip : About the status, 0 is unallocated, and 1 is allocated.)" << endl;
	for (int i = 0; i < num; i++){
		cout <<"\t"<< i + 1 << ".  ";
		cin >> start >> size >> state;// 手动输入分区起始地址,大小,状态
		if (state==0){
			Free_Table.Start_Address[Free_Table.Real_Length] = start;
			Free_Table.Partition_Size[Free_Table.Real_Length] = size;
			Free_Table.Partition_Status[Free_Table.Real_Length] = Unallocated;
			Free_Table.Real_Length++;
		}
		else if(state == 1){
			Allocated_Table.Start_Address[Allocated_Table.Real_Length] = start;
			Allocated_Table.Partition_Size[Allocated_Table.Real_Length] = size;
			Allocated_Table.Partition_Status[Allocated_Table.Real_Length] = Allocated;
			Allocated_Table.Real_Length++;
		}
	}
}
void Memory::display()
{
	cout << endl;
	cout << "    Memory: " << endl;
	cout << "\tstart" << "\tsize" << "\tstatus" << endl;	// 按地址顺序打印各个分区及其状态
	cout << "\t━━━━━━━━━━━━━━━━━━━━━━━━━━━━" << endl;
	int i, num;
	int start = 0;
	for (i = 0; i < Free_Table.Real_Length + Allocated_Table.Real_Length; i++) {
		num = Free_Table.search(start);
		if (num != ERROR) {
			cout << "\t" << Free_Table.Start_Address[num] << "(KB)\t" << Free_Table.Partition_Size[num] << "(KB)\t" << Free_Table.Partition_Status[num] << endl;
			start += Free_Table.Partition_Size[num];
			continue;
		}
		num = Allocated_Table.search(start);
		if (num != ERROR) {
			cout << "\t" << Allocated_Table.Start_Address[num] << "(KB)\t" << Allocated_Table.Partition_Size[num] << "(KB)\t" << Allocated_Table.Partition_Status[num] << endl;
			start += Allocated_Table.Partition_Size[num];
			continue;
		}
	}
	cout << endl;
	cout << "    Free partition table:" << endl;
	Free_Table.display();
}
int Memory::allocate(int XK, int choice)	// XK为作业大小,choice为算法选择,1为首次适应,2为最佳适应
{
	if (choice == 2) {	// 选择最佳适应分配
		Free_Table.sort();	// 最佳适应才会执行,将分区按大小升序排列
	}
	for (int j = 0; j < Free_Table.Real_Length; j++) {
		if (Free_Table.Partition_Status[j] == Unallocated) {	// 第i个表项中分区未分配
			if (Free_Table.Partition_Size[j] > XK) {	// 分区大小 > 作业大小
				// 登记已分配表
				Allocated_Table.Partition_Size[Allocated_Table.Real_Length] = XK;	// 已分配分区大小
				Allocated_Table.Start_Address[Allocated_Table.Real_Length] = Free_Table.Start_Address[j]; // 已分配分区起始地址
				Allocated_Table.Partition_Status[Allocated_Table.Real_Length] = Allocated;	// 表目状态为已分配
				Allocated_Table.Real_Length++;
				// 修改空闲分区表
				Free_Table.Partition_Size[j] -= XK;	// 分区剩余大小
				Free_Table.Start_Address[j] += XK;	// 分区新的起始地址
				return OK;
			}
			else if (Free_Table.Partition_Size[j] == XK){	// 分区大小 = 作业大小
				// 登记已分配表
				Allocated_Table.Partition_Size[Allocated_Table.Real_Length] = XK;	// 已分配分区大小
				Allocated_Table.Start_Address[Allocated_Table.Real_Length] = Free_Table.Start_Address[j];// 已分配分区起始地址
				Allocated_Table.Partition_Status[Allocated_Table.Real_Length] = Allocated;	// 表目状态为已分配
				Allocated_Table.Real_Length++;
				// 修改空闲分区表
				Free_Table.Start_Address[j] = Free_Table.Partition_Size[j] = ERROR;
				Free_Table.Partition_Status[j] == Empty;	// 空闲分区表中,该表项置空
				Free_Table.Move_Forward(j);	// 置空后将该表项后移
				Free_Table.Real_Length--;	// 实际表长减1
				return OK;
			}
			else {	// 分区大小 < 作业大小
				if (j + 1 == Free_Table.Real_Length) {	// 后一个表项为空表项,则作业等待
					return ERROR;
				}
				else {	// 后一个表项非空,则查看后一个表项
					continue;
				}
			}
		}
		else {	// 第i个表项为空表目
			if (j + 1 == Free_Table.Real_Length) {// 后一个表项为空表项,则作业等待
				return ERROR;
			}
			else {	// 后一个表项非空,则查看后一个表项
				continue;
			}
		}
	}
}
void Memory::recycle(int start)	// 回收起始地址为start的分区空间
{
	int num = Allocated_Table.search(start);// num为待回收的分区的编号
	int end = start + Allocated_Table.Partition_Size[num];	// end为与待回收的分区尾部邻接的分区起始地址
	int end_adjoin = Free_Table.search(end);// 与待回收的分区尾部邻接的分区编号
	int start_adjoin = ERROR;	// 与待回收的分区起始地址邻接的分区编号
	int i;
	for (i = 0; i < Free_Table.Real_Length; i++) {
		if (Free_Table.Start_Address[i]+Free_Table.Partition_Size[i]==start){
			start_adjoin = i;
			break;
		}
	}
	if (start_adjoin != ERROR && end_adjoin != ERROR) {// 待回收分区头尾都有空闲分区邻接
		Free_Table.Partition_Size[start_adjoin] += (Allocated_Table.Partition_Size[num] + Free_Table.Partition_Size[end_adjoin]);
		Free_Table.Move_Forward(end_adjoin);
		Free_Table.Real_Length--;
	}
	else if (start_adjoin != ERROR) {	// 待回收分区起始处有空闲分区邻接
		Free_Table.Partition_Size[start_adjoin] += Allocated_Table.Partition_Size[num];
	}
	else if (end_adjoin != ERROR) {	// 待回收分区尾部有空闲分区邻接
		Free_Table.Start_Address[end_adjoin] = start;
		Free_Table.Partition_Size[end_adjoin] += Allocated_Table.Partition_Size[num];
	}
	else {	// 待回收分区不与任何空闲分区邻接
		Free_Table.Start_Address[Free_Table.Real_Length] = start;
		Free_Table.Partition_Size[Free_Table.Real_Length] = Allocated_Table.Partition_Size[num];
		Free_Table.Partition_Status[Free_Table.Real_Length] = Unallocated;
		Free_Table.Real_Length++;
	}
	Allocated_Table.Move_Forward(num);
}

int main()
{
	Memory m;// 主存
	int operate_choice = 1;// 对下一步操作的选择
	while (operate_choice == 1 || operate_choice == 2) {
		m.display();	// 显示主存与空闲分区表
		cout << endl;
		cout << "\n    Operation options:\n\t1.Allocate space for a job; \n\t2.Recycle space from a job;\n\t3.Any key to Quit"<<endl;
		cout << "    Choose: ";
		cin >> operate_choice;
		if (operate_choice == 1) {
			cout << "\n    Input the size of job: ";
			int job;
			cin >> job;

			int algorithm_choice;
			cout << "\n    Allocate algorithm options: \n\t1.First Fits;\n\t2.Best Fits." << endl;
			cout << "    Choose: ";
			cin >> algorithm_choice;
			int allocate_result = m.allocate(job, algorithm_choice);
			if (allocate_result != ERROR) {
				cout << "\n----Allocate Succeed!----" << endl;	// 分配成功,则打印主存
				m.display();
			}
			else {
				cout << "\n----Allocate failed!----" << endl;	// 分配失败
			}
			cout << endl;
		}
		else if (operate_choice == 2) {
			int start;
			cout << "\n    Input the start address to recycle: ";	// 输入待回收的分区起始地址
			cin >> start;
			m.recycle(start);
			cout << "\n----Recycle succeed!----" << endl;	// 回收完成
			m.display();
		}
		system("pause");
		system("cls");
	}
	system("pause");
	return 0;
}

3. 运行结果

3.1 手动初始化主存
手动输入主存主存初始状态的分区数,这里输入的是6个;再手动输入每个分区的起始地址,长度,状态。
FF首次适应算法与BF最佳适应算法(C++实现)_第6张图片
输入完毕,打印主存和空闲分区表:
FF首次适应算法与BF最佳适应算法(C++实现)_第7张图片
3.2 首次适应算法
操作选择,输入1,为作业分配空间;这里输入作业大小为30(KB);算法选择,输入1,首次适应算法。
FF首次适应算法与BF最佳适应算法(C++实现)_第8张图片
分配成功后的主存和空闲分区表:(从起始地址为110KB的分区分配了30KB给作业)
FF首次适应算法与BF最佳适应算法(C++实现)_第9张图片
操作选择,输入2,回收作业空间;这里回收的空间起始地址为0KB;
FF首次适应算法与BF最佳适应算法(C++实现)_第10张图片
成功回收地址为0KB的分区(低地址,高地址都不邻接,新的空闲分区加入表中):
FF首次适应算法与BF最佳适应算法(C++实现)_第11张图片
3.3 最佳适应算法
操作选择,输入1,为作业分配空间;这里输入作业大小为5(KB);算法选择,输入2,首次适应算法。
FF首次适应算法与BF最佳适应算法(C++实现)_第12张图片
分配成功后的主存和空闲分区表:(从起始地址为0KB的分区分配了5KB给作业)
FF首次适应算法与BF最佳适应算法(C++实现)_第13张图片
3.4 回收:逐个回收被分配出去的分区。
成功回收起始地址为0KB的分区(高地址邻接)
FF首次适应算法与BF最佳适应算法(C++实现)_第14张图片
成功回收起始地址为10KB的分区(低地址邻接)
FF首次适应算法与BF最佳适应算法(C++实现)_第15张图片
成功回收起始地址为110KB的分区(高地址邻接)
FF首次适应算法与BF最佳适应算法(C++实现)_第16张图片
成功回收起始地址为20KB的分区(低地址、高地址都邻接)
FF首次适应算法与BF最佳适应算法(C++实现)_第17张图片
成功回收起始地址为65KB的分区(低地址、高地址都邻接)
FF首次适应算法与BF最佳适应算法(C++实现)_第18张图片
3.5 退出
FF首次适应算法与BF最佳适应算法(C++实现)_第19张图片

【小结或讨论】

实验过程中,发现首次适应算法和最佳适应算法的流程中,唯一区别在于最佳适应算法在分配前将空闲分区表按升序排列(即先分配空间小的分区),所以将两个算法合并在同一个allocate()函数中,通过与用户交互来选择分配方法。

你可能感兴趣的:(操作系统算法实验)