操作系统课程实验3-可变分区存储管理
一、实验介绍
1.1 实验目的
- 加深对可变分区存储管理的理解;
- 提高用C语言编制大型系统程序的能力,特别是掌握C语言编程的难点:指针和指针作为函数参数;
- 掌握用指针实现链表和在链表上的基本操作。
1.2 实验内容
参照教材P137-P140的内容,编写一个C程序,用循环首次适应算法、最佳适应算法和最坏适应算法,模拟可变分区存储管理,实现对内存区的分配和回收管理。
1.3 实验要求
- 每次分配和释放后显示空闲分区表。
- 空闲分区表可采用结构数组的形式(最低要求)或双向链表的形式。
1.4 参考测试数据
操作系统在低地址占用100KB的空间,用户区主存从100KB处开始占用512KB。初始时,用户区全部为空闲,分配时截取空闲分区的低地址部分作为已分配区。执行以下申请、释放操作序列后:请求300KB,请求100KB,释放300KB,请求150KB,请求90KB,释放100KB。
二、实现代码
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
#define ALLOCATED 1 // 定义已分配状态为1
#define FREE 0 // 定义空闲状态为0
#define SEQUENCELENGTH 6 // 定义操作序列长度为6
typedef struct CHAIN { // 定义链表结构体 Table
int status; // 记录块的状态:ALLOCATED 或 FREE
int pid; // 记录分配给的进程ID
int size; // 记录块的大小
int addr; // 记录块的起始地址
struct CHAIN *Next_Block; // 指向下一块的指针
struct CHAIN *Pre_Block; // 指向前一块的指针
} Table;
Table *Head; // 头指针,指向链表的头部
int OCPSequence[SEQUENCELENGTH][3] = { // 操作序列数组,包含分配和回收操作
{1, 1, 300}, {1, 2, 100}, {2, 1, 300}, {1, 3, 150}, {1, 4, 90}, {2, 2, 100}
};
void start(); // 声明启动函数
void FirstFit(); // 声明首次适应算法函数
void BestFit(); // 声明最佳适应算法函数
void WorstFit(); // 声明最坏适应算法函数
void CirFirstFit(); // 声明循环首次适应算法函数
bool recycle(int pid); // 声明回收函数
void linkprint(void); // 声明链表打印函数
void CommomDeal(int alg); // 声明通用处理函数
Table* FindFF(int size); // 声明首次适应算法查找函数
Table* FindBF(int size); // 声明最佳适应算法查找函数
Table* FindWF(int size); // 声明最坏适应算法查找函数
Table* FindCFF(int size); // 声明循环首次适应算法查找函数
void HeadReset(void); // 声明头节点重置函数
int main(void) {
Head = (Table*)malloc(sizeof(Table)); // 分配内存给头节点
Head->Next_Block = Head->Pre_Block = NULL; // 初始化头节点的前后指针
HeadReset(); // 重置头节点
start(); // 启动模拟
return 0;
}
void start() {
cout << "we are gonna do a simulation about memory allocation" << endl;
cout << "next,there are some algorithm is waiting for you,you need to choose one of them in every circle" << endl;
cout << "1.首次适应算法\t2.最佳适应算法\t3.最坏适应算法\t4.循环首次适应算法" << endl << endl << endl << endl;
int IDofAlgorithm; // 声明算法ID变量
for (int i = 0; i < 4; i++) { // 循环4次,模拟4个算法
cout << "输入算法ID:";
cin >> IDofAlgorithm; // 输入算法ID
while (IDofAlgorithm < 1 || IDofAlgorithm > 4) { // 检查输入是否合法
cout << "无效的算法ID,请重新输入:";
cin >> IDofAlgorithm;
}
switch (IDofAlgorithm) { // 根据算法ID选择对应算法
case 1:
printf("--------------------------------------------------------------------------------------\n 首次适应算法 \n--------------------------------------------------------------------------------------\n"); // 打印首次适应算法标题
FirstFit(); // 调用首次适应算法函数
break;
case 2:
printf("--------------------------------------------------------------------------------------\n 最佳适应算法 \n--------------------------------------------------------------------------------------\n"); // 打印最佳适应算法标题
BestFit(); // 调用最佳适应算法函数
break;
case 3:
printf("--------------------------------------------------------------------------------------\n 最坏适应算法 \n--------------------------------------------------------------------------------------\n"); // 打印最坏适应算法标题
WorstFit(); // 调用最坏适应算法函数
break;
case 4:
printf("--------------------------------------------------------------------------------------\n 循环首次适应算法 \n--------------------------------------------------------------------------------------\n"); // 打印循环首次适应算法标题
CirFirstFit(); // 调用循环首次适应算法函数
break;
}
HeadReset(); // 重置头节点,准备下一个算法测试
}
}
void FirstFit() {
CommomDeal(1); // 调用通用处理函数,参数为1(首次适应算法)
}
void BestFit() {
CommomDeal(2); // 调用通用处理函数,参数为2(最佳适应算法)
}
void WorstFit() {
CommomDeal(3); // 调用通用处理函数,参数为3(最坏适应算法)
}
Table* FindFF(int size) {
Table * p = Head; // 初始化指针p为头节点
while (p != NULL ) { // 遍历链表
if (p->size >= size && p->status == FREE) // 找到第一个合适的空闲块
return p; // 返回该块指针
p = p->Next_Block; // 移动到下一个块
}
return NULL; // 如果没有合适的块,返回NULL
}
Table* FindBF(int size) {
Table * p = Head; // 初始化指针p为头节点
Table * best = NULL; // 初始化最优块指针为NULL
while (p != NULL ) { // 遍历链表
if (p->status == FREE && p->size >= size && (best == NULL || p->size < best->size)) // 找到更小的合适空闲块
best = p; // 更新最优块指针
p = p->Next_Block; // 移动到下一个块
}
return best; // 返回最优块指针
}
Table* FindWF(int size) {
Table * p = Head; // 初始化指针p为头节点
Table * Worst = NULL; // 初始化最差块指针为NULL
while (p != NULL ) { // 遍历链表
if (p->status == FREE && p->size >= size && (Worst == NULL || p->size > Worst->size)) // 找到更大的合适空闲块
Worst = p; // 更新最差块指针
p = p->Next_Block; // 移动到下一个块
}
return Worst; // 返回最差块指针
}
Table* FindCFF(Table * p, int size) {
Table * Flag = p; // 保存初始块指针
if (p == NULL) // 如果初始块为空
p = Head; // 从头开始
while (p != NULL) { // 遍历链表
if (p->status == FREE && p->size >= size) // 找到合适的空闲块
return p; // 返回该块指针
if (!p->Next_Block) // 如果到达链表末尾
p = Head; // 回到头节点
else
p = p->Next_Block; // 移动到下一个块
if (p == Flag) // 如果回到初始块
return NULL; // 返回NULL表示没有找到合适块
}
}
void CommomDeal(int alg) {
Table* (*Tar)(int); // 定义函数指针
switch (alg) { // 根据算法选择对应查找函数
case 1:
Tar = FindFF; // 指向首次适应算法查找函数
break;
case 2:
Tar = FindBF; // 指向最佳适应算法查找函数
break;
case 3:
Tar = FindWF; // 指向最坏适应算法查找函数
break;
}
for (int i = 0; i < SEQUENCELENGTH; i++) { // 遍历操作序列
if (OCPSequence[i][0] == 1) { // 分配操作
int ID, size;
ID = OCPSequence[i][1]; // 获取进程ID
size = OCPSequence[i][2]; // 获取请求大小
Table *pReturn;
printf("进程%d请求%dKB\n", ID, size); // 打印请求信息
printf("--------------------------------------------------------------------------------------\n");
if ((pReturn = Tar(size)) != NULL) { // 调用查找函数,找到合适块
if (pReturn->size == size) { // 如果块大小正好
pReturn->pid = ID; // 设置块的进程ID
pReturn->status = ALLOCATED; // 设置块的状态为已分配
} else { // 如果块大小大于请求大小
Table * NewFreeNode = (Table*)malloc(sizeof(Table)); // 创建新空闲块
NewFreeNode->pid = -1; // 初始化新块
NewFreeNode->addr = pReturn->addr + size; // 设置新块地址
NewFreeNode->size = pReturn->size - size; // 设置新块大小
NewFreeNode->status = FREE; // 设置新块状态为空闲
pReturn->pid = ID; // 设置当前块的进程ID
pReturn->size = size; // 设置当前块的大小
pReturn->status = ALLOCATED; // 设置当前块的状态为已分配
NewFreeNode->Pre_Block = pReturn; // 设置新块的前指针
NewFreeNode->Next_Block = pReturn->Next_Block; // 设置新块的后指针
pReturn->Next_Block = NewFreeNode; // 设置当前块的后指针为新块
}
} else { // 如果没有合适的块
cout << "内存不足,分配失败" << endl; // 打印失败信息
}
} else { // 回收操作
int pid;
pid = OCPSequence[i][1]; // 获取要回收的进程ID
printf("请求回收进程%d,即将释放内存空间%dKB\n", pid, OCPSequence[i][2]); // 打印回收请求信息
if (!recycle(pid)) // 调用回收函数
cout << "进程不存在,回收失败" << endl; // 打印回收失败信息
else
cout << "回收成功" << endl; // 打印回收成功信息
}
linkprint(); // 打印当前链表状态
printf("--------------------------------------------------------------------------------------\n");
}
cout << endl << endl << endl;
}
void CirFirstFit() {
Table *Current = NULL; // 初始化当前指针为空
for (int i = 0; i < SEQUENCELENGTH; i++) { // 遍历操作序列
if (OCPSequence[i][0] == 1) { // 分配操作
int ID, size;
ID = OCPSequence[i][1]; // 获取进程ID
size = OCPSequence[i][2]; // 获取请求大小
printf("进程%d请求%dKB\n", ID, size); // 打印请求信息
printf("--------------------------------------------------------------------------------------\n");
if ((Current = FindCFF(Current ? Current->Next_Block : Head, size)) != NULL) { // 调用循环首次适应查找函数
if (Current->size == size) { // 如果块大小正好
Current->pid = ID; // 设置块的进程ID
Current->status = ALLOCATED; // 设置块的状态为已分配
} else { // 如果块大小大于请求大小
Table * NewFreeNode = (Table*)malloc(sizeof(Table)); // 创建新空闲块
NewFreeNode->pid = -1; // 初始化新块
NewFreeNode->addr = Current->addr + size; // 设置新块地址
NewFreeNode->size = Current->size - size; // 设置新块大小
NewFreeNode->status = FREE; // 设置新块状态为空闲
Current->pid = ID; // 设置当前块的进程ID
Current->size = size; // 设置当前块的大小
Current->status = ALLOCATED; // 设置当前块的状态为已分配
NewFreeNode->Pre_Block = Current; // 设置新块的前指针
NewFreeNode->Next_Block = Current->Next_Block; // 设置新块的后指针
Current->Next_Block = NewFreeNode; // 设置当前块的后指针为新块
}
} else { // 如果没有合适的块
cout << "内存不足,分配失败" << endl; // 打印失败信息
}
} else { // 回收操作
int pid;
pid = OCPSequence[i][1]; // 获取要回收的进程ID
printf("请求回收进程%d,即将释放内存空间%dKB\n", pid, OCPSequence[i][2]); // 打印回收请求信息
if (!recycle(pid)) // 调用回收函数
cout << "进程不存在,回收失败" << endl; // 打印回收失败信息
else
cout << "回收成功" << endl; // 打印回收成功信息
}
linkprint(); // 打印当前链表状态
printf("--------------------------------------------------------------------------------------\n");
}
cout << endl << endl << endl;
}
bool recycle(int pid) {
Table *p = Head; // 初始化指针p为头节点
while (p) { // 遍历链表
if (p->pid == pid) { // 找到匹配的进程ID
p->status = FREE; // 设置块状态为空闲
// 合并相邻的空闲块
if (p->Next_Block && p->Next_Block->status == FREE) { // 如果下一块也是空闲
Table *next = p->Next_Block; // 保存下一块指针
p->size += next->size; // 合并大小
p->Next_Block = next->Next_Block; // 更新当前块的后指针
if (next->Next_Block) {
next->Next_Block->Pre_Block = p; // 更新下一块的前指针
}
free(next); // 释放合并的块
}
if (p->Pre_Block && p->Pre_Block->status == FREE) { // 如果前一块也是空闲
Table *prev = p->Pre_Block; // 保存前一块指针
prev->size += p->size; // 合并大小
prev->Next_Block = p->Next_Block; // 更新前一块的后指针
if (p->Next_Block) {
p->Next_Block->Pre_Block = prev; // 更新当前块的前指针
}
free(p); // 释放合并的块
p = prev; // 更新指针p为前一块
}
return true; // 返回回收成功
}
p = p->Next_Block; // 移动到下一个块
}
return false; // 如果没有找到匹配的进程ID,返回回收失败
}
void linkprint(void) {
Table *p = Head;
while (p) {
if (p->status == 0)
cout << "" << p->addr << "~" << p->addr + p->size << ":" << p->status << ""; // 打印空闲块信息
else
cout << "" << p->addr << "~" << p->addr + p->size << ":" << p->status << " by " << p->pid << ""; // 打印已分配块信息
if (p->Next_Block)
cout << "-->";
p = p->Next_Block;
}
cout << endl;
}
void HeadReset(void) {
Table* p = Head->Next_Block; // 初始化指针p为头节点的下一块
Table *temp; // 声明临时指针
while (p) { // 遍历链表
temp = p->Next_Block; // 保存下一块指针
free(p); // 释放当前块
p = temp; // 更新指针p为下一块
}
Head->addr = 100; // 重置头节点地址
Head->Next_Block = NULL; // 重置头节点后指针
Head->Pre_Block = NULL; // 重置头节点前指针
Head->pid = -1; // 重置头节点进程ID
Head->size = 500; // 重置头节点大小
Head->status = FREE; // 重置头节点状态为空闲
}
三、心灵的救赎
- 人间没有永恒的夜晚,世界没有永恒的冬天。
- 当生活把无边的严寒铺盖在你身上时,一定会给你一根火柴。