本实验主要练习主存的各种分配和回收。所谓分配,就是解决多道作业或多进程如何共享主存空间的问题。所谓回收,就是当作业运行完成时,将作业或进程所占用的主存空间归还给系统。主存的分配和回收的实现就是与主存储器的管理方式有关的。通过本实验理解在不同的存储管理方式下,如何实现主存空间的分配与回收。
采用可变式分区管理,使用首次或最佳适应算法实现主存的分配与回收。
1.1全局变量
1.2 PST 类
1.3 Memory 类
2.1 程序流程图
首次适应算法分配框图和首次适应算法回收框图分别如图3.1和图3.2所示,最佳适应算法则在首次适应算法基础上加上对内存按大小排序,流程图几乎一致,这里不再给出。
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.1 手动初始化主存
手动输入主存主存初始状态的分区数,这里输入的是6个;再手动输入每个分区的起始地址,长度,状态。
输入完毕,打印主存和空闲分区表:
3.2 首次适应算法
操作选择,输入1,为作业分配空间;这里输入作业大小为30(KB);算法选择,输入1,首次适应算法。
分配成功后的主存和空闲分区表:(从起始地址为110KB的分区分配了30KB给作业)
操作选择,输入2,回收作业空间;这里回收的空间起始地址为0KB;
成功回收地址为0KB的分区(低地址,高地址都不邻接,新的空闲分区加入表中):
3.3 最佳适应算法
操作选择,输入1,为作业分配空间;这里输入作业大小为5(KB);算法选择,输入2,首次适应算法。
分配成功后的主存和空闲分区表:(从起始地址为0KB的分区分配了5KB给作业)
3.4 回收:逐个回收被分配出去的分区。
成功回收起始地址为0KB的分区(高地址邻接)
成功回收起始地址为10KB的分区(低地址邻接)
成功回收起始地址为110KB的分区(高地址邻接)
成功回收起始地址为20KB的分区(低地址、高地址都邻接)
成功回收起始地址为65KB的分区(低地址、高地址都邻接)
3.5 退出
实验过程中,发现首次适应算法和最佳适应算法的流程中,唯一区别在于最佳适应算法在分配前将空闲分区表按升序排列(即先分配空间小的分区),所以将两个算法合并在同一个allocate()函数中,通过与用户交互来选择分配方法。