天津理工大学《操作系统》实验二,存储器的分配与回收算法实现,代码详解,保姆式注释讲解
实验内容
1. 本实验是模拟操作系统的主存分配,运用可变分区的存储管理算法设计主存分配和回收程序,并不实际启动装入作业。
2. 采用最先适应法、最佳适应法、最坏适应法分配主存空间。
3. 当一个新作业要求装入主存时,必须查空闲区表,从中找出一个足够大的空闲区。若找到的空闲区大于作业需要量,这是应把它分成二部分,一部分为占用区,加一部分又成为一个空闲区。
4. 当一个作业撤离时,归还的区域如果与其他空闲区相邻,则应合并成一个较大的空闲区,登在空闲区表中。
5. 设计的模拟系统中,进程数不小于5,进程调度方式可以采用实验一中的任何一种。
6. 运行所设计的程序,输出有关数据结构表项的变化和内存的当前状态。
设计思想
-
根据题意,我们需要建立三个二维数组分别代表,输入作业区,已分配作业区和空闲区,这些数组内部存放了对应的起始地址和内存所占空间,对于已分配区则多加了一个作业名称。
首先我们要先了解最先适应法、最佳适应法和最坏适应法。 -
最先适应法:
将进程往尽量低地址空闲区域放,放不下的话在将地址慢慢升高,每一次存放,都从低地址开始寻找满足的空闲区域,直至最高地址,每次存放都从0开始。 -
最佳适应法:
和最先适应算法相似,当进程申请空闲的时候,系统都是从头开始查找。空闲区域是从小到大记录的,每次查找都是从最小的开始,直到查找的满足要求的最小空间区域。 -
最坏适应法:
该算法与之前两种算法相反,用最大的空闲区域来存储东西 -
根据上述算法介绍我们了解到,进行作业分配前,我们先要了解用想进行哪种算法,最先和最佳适应算法比较相似,都是要将空闲区进行从小到大进行排序,最先是取0这个位置的空闲区,最佳是取最小的空闲区,当我们将空闲区排序后,这两个方法取的空闲区是一样的。如果是最坏适应算法是选择最大的空闲区,所以我们把空闲区从大到小排列,还是取第一个空闲区。
源码及注释
#include<iostream>
#include <queue>
using namespace std;
//work[10][0]代表作业名称,work[10][1]代表作业占用内存大小
int work[10][2];
//idle[i][0]空闲起始地址,idle[i][1]连续内存空间
int idle[10][2];
//fee[i][0]已分配作业名,fee[i][1]起始地址,fee[i][2]内存占用空间
int fee[10][3];
//ch1选择分配空间或者是回收空间,ch2选择算法
int num = 0,b=1,d,ch1,ch2;
void init(){
//起始地址为1,连续内存空间为100
idle[0][0]=1;idle[0][1]=100;
//作业名为0,起始地址为0,内存占用空间为0
fee[0][0]=0;fee[1][1]=0;fee[1][2]=0;
//作业名称为0,作业占用内存大小为0
work[0][0]=0;work[0][1]=0;
//进入循环,循环10次,分别将所有区域进行初始化操作
for (int i = 1; i <= 9; i++)
{
/* code */
idle[i][0]=0;idle[i][1]=0;
fee[i][0]=0;fee[i][1]=0;fee[i][2]=0;
work[i][0]=0;work[i][1]=0;
}
}
//设置一个计数器,求空闲单元数
void jishu(){
for(int i=0;i < 9;i++){
if(idle[i][1] != 0)
num++;
}
}
//设置一个计数器,求作业的个数
void jishu1(){
for(int i = 0;i < 9;i++){
if(work[i][1] != 0)
b++;
}
}
//最先适应算法
//将进程往尽量低地址空闲区域放,放不下的话在将地址慢慢升高,每一次存放,都从低地址开始寻找满足的空闲区域,直至最高地址,每次存放都从0开始。
void zuixian(){
num = 0;
//调用空闲计数器函数
jishu();
//对空闲区进行冒泡排序,按照空闲区各个位置的起始地址从小到大进行排序
for (int i = 0; i < num; i++){
for(int j = i; j < num - i - 1; j++){
if(idle[j][0] > idle[j+1][0]){
int temp = idle[j][0];
idle[j][0] = idle[j+1][0];
idle[j+1][0] = temp;
temp = idle[j][1];
idle[j][1] = idle[j+1][1];
idle[j+1][1] = temp;
}
}
}
}
//最佳适应算法
//和最先适应算法相似,当进程申请空闲的时候,系统都是从头开始查找。空闲区域是从小到大记录的,每次查找都是从最小的开始,直到查找的满足要求的最小空间区域。
void zuijia(){
//调用空闲计数器函数
num = 0;
jishu();
//对空闲区进行冒泡排序,按照空闲区各个位置的起始地址从小到大进行排序
for(int i = 0; i < num; i++){
for(int j = i; j < num - i - 1; j++){
if(idle[j][1] > idle[j+1][1]){
int temp = idle[j][0];
idle[j][0] = idle[j+1][0];
idle[j+1][0] = temp;
temp = idle[j][1];
idle[j][1] = idle[j+1][1];
idle[j+1][1] = temp;
}
}
}
}
//最坏适应算法
//该算法与之前两种算法相反,用最大的空闲区域来存储东西
void zuihuai(){
//调用空闲计数器函数
num = 0;
jishu();
//对空闲区进行冒泡排序,按照空闲区各个位置的起始地址从大到小进行排序
for(int i = 0; i < num; i++){
for(int j = i; j < num - 1 - i; j++){
if(idle[j][1] < idle[j+1][1]){
int temp = idle[j][0];
idle[j][0] = idle[j+1][0];
idle[j+1][0] = temp;
temp = idle[j][1];
idle[j][1] = idle[j+1][1];
idle[j+1][1] = temp;
}
}
}
}
//回收进程算法
//传入需要回收的作业名称
void huishou(int name){
//调用空闲计数器函数和输入作业计数器函数
num = 0;
b = 0;
jishu();
jishu1();
//参数c为要删除的作业名对应的已分配区域数组的索引,初始状态为-1
int c = -1;
//遍历数组查找作业名对应的索引
for(int k = 0; k <= b; k++){
if(fee[k][0] == name){
c = k;
break;
}
}
//如果上述循环中未在数组中找到与待查作业相同的作业名,则c还是初始值-1,表示要回收的作业不存在
if(c == -1) cout<<"要回收的作业不存在!"<<endl;
//以下是找到作业名对应索引值得情况,开始回收作业
else{
//先用冒泡法对空闲区进行排序(按照起始位置从小到大进行排序)(不包括新回收的)
for(int i = 0; i < num; i++){
for(int j = i; j < num - i - 1; j++){
if(idle[j][0] > idle[j+1][0]){
int temp = idle[j][0];
idle[j][0] = idle[j+1][0];
idle[j+1][0] = temp;
temp = idle[j][1];
idle[j][1] = idle[j+1][1];
idle[j+1][1] = temp;
}
}
}
}
//用冒泡法对空闲区进行排序(按照起始位置从小到大进行排序)(包括新回收的)
for(int q = 0; q < num; q++){
//将作业的起始位置与空闲区中的起始地址相对应,将这个位置后面的所有进程全部向后移动一个位置,将该作业插入到空闲数组中(这步操作是包括了新回收的部分)
if(fee[c][1] <= idle[q][0]){
for(int j = num; j >= q; j--){
idle[j+1][0] = idle[j][0];
idle[j+1][1] = idle[j][1];
idle[j][0] = fee[c][1];
idle[j][1] = fee[c][2];
b++;
//如果插入进来的作业地址后面的进程的起始地址等于该作业起始地址+作业所占用的空间
if(idle[j+1][0] == idle[j][0] + idle[j][1]){
//将该空闲区所占内存空间变成,该空闲内存空间+后面空闲区域所占的内存空间
idle[j][1] = idle[j][1] + idle[j+1][1];
int s;
//在这一节点之后所有空闲区域后移
for(int m = j+1; m <= num; m++){
//由于新回收的空闲区的内存空间加上了后面空闲区的内存空间,所以要将后面的所有空闲区域往前移动(简单记法:新增空闲空间的后面的空闲空间往前合并了,所以后续的所有空闲空间都要往前走一个位置)
idle[m][0] = idle[m+1][0];
idle[m][1] = idle[m+1][1];
//记录当前新回收的空闲空间的索引值
s = m;
}
//将该空间全部清0,原本是这里面是有待回收作业的信息的,所以要把他置0回收掉变成空闲空间
idle[s][0] = 0;
idle[s][1] = 0;
//由于回收了一个作业,所以作业的计数器记录的数据要减一
b--;
}
//上面的操作是针对待删除区域和后续空闲区域可以合并的情况
//下面的操作是针对待删除区域和前面空闲区可以合并的情况
if(idle[j-1][0] == idle[j][0]){
idle[j-1][1] = idle[j-1][1] + idle[j][1];
int s;
for(int n = j; j <= num; j++){
idle[n][0] = idle[n+1][0];
idle[n][1] = idle[n+1][1];
s = n;
}
idle[s][0] = 0;
idle[s][1] = 0;
b--;
}
}
break;
}
}
//ch2主要是用来记录用户选择那种适应算法的,不同的算法对应不同的参数数值
if(ch2 == 1) zuixian();
if(ch2 == 2) zuijia();
if(ch2 == 3) zuihuai();
//找到待删除作业在已分配区域的具体位置,将其后面的所有已分配区域均往前移动,将待删除区域覆盖,作业区域也是相同的原理
for(int p = c; c < b-1; c++){
fee[c][0] = fee[c+1][0];
fee[c][1] = fee[c+1][1];
fee[c][2] = fee[c+2][2];
work[c][0] = work[c+1][0];
work[c][1] = work[c+1][1];
}
//回收程序完成
cout<<"该程序已被成功回收!"<<endl;
}
void fp(){
//判断空闲区和请求区的大小
int tag = 0;
num = 0;
b = 0;
jishu();
jishu1();
for(int j = 0; j < num; j++){
//如果请求作业所占内存 < 空闲区所占内存
if(work[b][1] < idle[j][1]){
//记录作业名称
fee[b][0] = work[b][0];
//记录本次已分配作业的起始位置(等于其空闲区域的起始位置)
fee[b][1] = idle[j][0];
//记录本次已分配作业所占用的内存空间
fee[b][2] = work[b][1];
//将分配前的空闲区域的起始地址 == 空闲起始位置 + 作业所占内存空间
idle[j][0] = idle[j][0] + work[b][1];
//将分配前的空闲区所占空间 == 空闲区所占空间 - 作业所占空间
idle[j][1] = idle[j][1] - work[b][1];
//tag参数改为1,表示可分配
tag = 1;
break;
}
//如果请求作业所占内存 = 空闲区所占内存
else if(work[b][1] == idle[j][1]){
//记录作业名称
fee[b][0] = work[b][0];
//已分配作业所占内存空间 = 待分配的空闲区域内存空间
fee[b][1] = idle[j][0];
//记录本次已分配作业所占用的内存空间
fee[b][2] = work[b][1];
//tag参数改为1,表示可分配
tag = 1;
//由于新分配了一个作业,即少了一块空闲区域,将已分配的空闲区域的后面所有空闲区域全部向前移动一位,清除已分配的空闲区域
for(int i = j; i <= num - 1; i++){
idle[i][0] = idle[i+1][0];
idle[i][1] = idle[i+1][1];
}
break;
}
//如果tag还是0的话,则代表待分配作业内存空间 》 空闲区域的内存空间,无法分配
else tag = 0;
}
if(tag == 0) cout<<"作业过大没有足够的储存空间!"<<endl;
}
//输出展示给用户的函数
void print(){
num = 0;
b = 1;
jishu();
jishu1();
cout<<"已分配表为:"<<endl;
//遍历已分配区域的数组
for(int i = 0; i <= b; i++)
//如果已分配区域的内存空间 》 0则可以展示信息
if(fee[i][2] != 0)
cout<<"作业名:"<<fee[i][0]<<"内存起始地址:"<<fee[i][1]<<"占用内存空间: "<<fee[i][2]<<endl;
cout<<endl;
cout<<"空闲区表为:"<<endl;
for(int j = 0; j < num; j++)
//如果空闲区内存空间 》 0,则可以展示信息
if(idle[j][1] != 0)
cout<<"起始地址:"<<idle[j][0]<<"连续内存空间:"<<idle[j][1]<<endl;
cout<<endl;
}
//一下是进程调度部分
//PCB类(封装进程的信息)
class Pcb
{
private:
public:
// 进程id
int pid;
// 进程优先数
int priority;
// 要求运行时间
int time;
// 进程状态
bool state = true;
// //进程的就绪队列
// queue<Pcb> readyQueue;
//进程中作业的名称
int name;
//进程中作业所占内存空间
int space;
//进程中作业所选择的适应方法
int method;
Pcb();
Pcb(int pid, int priority, int time);
Pcb(int pid,int priority,int time,int name,int space,int method);
~Pcb();
//进程运行一个时间片
void run();
//状态置为结束
void stateFalse();
};
//进程运行一个时间片
void Pcb::run()
{
//进程每运行一个时间片,要求运行时间减少1,优先数减少1
this->priority--;
this->time--;
}
//状态置为结束
void Pcb::stateFalse()
{
//进程运行完成将状态置为结束
this->state = false;
}
Pcb::Pcb()
{
}
//构造函数,在创建PCB时输入运行时间和优先级
Pcb::Pcb(int pid, int priority, int time)
{
this->pid = pid;
this->priority = priority;
this->time = time;
}
Pcb::~Pcb()
{
}
//构造函数,用于作业进程调度
Pcb::Pcb(int pid,int priority,int time,int name,int space,int method)
{
this->pid = pid;
this->priority = priority;
this->time = time;
this->name = name;
this->space = space;
this->method = method;
}
//进程调度队列排序
queue<Pcb> queuesort(queue<Pcb> a)
{
int size = a.size();
Pcb list[size];
Pcb tmp;
for (int i = 0; i < size; i++)
{
list[i] = a.front();
a.pop();
}
for (int i = 0; i < size - 1; ++i)
{
for (int j = size - 1; j > i; --j)
{
if (list[j - 1].priority < list[j].priority)
{
tmp = list[j - 1];
list[j - 1] = list[j];
list[j] = tmp;
}
}
}
for (int i = 0; i < size; i++)
{
a.push(list[i]);
}
return a;
}
int main(){
init();
int n;
int priority, time = 1;
int name,space;
queue<Pcb> queuesort(queue<Pcb> a);
// 进程就绪队列
queue<Pcb> readyQueue;
int sum;
cout<<"1. 分配空间: 2. 回收空间:"<<endl;
cin>>ch1;
cout<<endl;
if(ch1 == 1){
cout<<"请输入进程作业个数"<<endl;
cin>>sum;
for(int i = 0 ; i < sum; i++){
cout<<"请输入进程作业的优先级"<<endl;
cin>>priority;
cout<<"请输入要分配内存的作业名及占用内存大小:";
cin>>name>>space;
cout<<endl;
cout<<"1. 最先适应法: 2. 最佳适应法: 3. 最坏适应法:"<<endl;
cin>>ch2;
cout<<endl;
//创建Pcb对象
Pcb newpcb(i, priority, time, name,space,ch2);
//将该进程置入就绪队列
readyQueue.push(newpcb);
}
while (!readyQueue.empty())
{
//先对队列中的进程进行排序,按优先级从大到小的顺序
readyQueue = queuesort(readyQueue);
if(readyQueue.front().method == 1){
work[b][0] = readyQueue.front().name;
work[b][1] = readyQueue.front().space;
zuixian();
fp();
}
else if(readyQueue.front().method == 2){
work[b][0] = readyQueue.front().name;
work[b][1] = readyQueue.front().space;
zuijia();
fp();
}
else if(readyQueue.front().method == 3){
work[b][0] = readyQueue.front().name;
work[b][1] = readyQueue.front().space;
zuihuai();
fp();
}
print();
//取出就绪队列队首的进程运行
readyQueue.front().run();
// 如果该进程运行时间完成,则将其状态置为结束,且退出队列
if (readyQueue.front().time <= 0)
{
readyQueue.front().stateFalse();
readyQueue.pop();
}
}
}
cout<<"输入要回收的作业名:"<<endl;
cin>>n;
huishou(n);
}
测试用例及测试结果
希望对各位小伙伴有所帮助呀