天津理工大学《操作系统》实验二,存储器的分配与回收算法实现,代码详解,保姆式注释讲解

天津理工大学《操作系统》实验二,存储器的分配与回收算法实现,代码详解,保姆式注释讲解

实验内容

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);
}



测试用例及测试结果

在这里插入图片描述
在这里插入图片描述
希望对各位小伙伴有所帮助呀

  • 20
    点赞
  • 85
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
通用动态分区分配存储器管理系统是一个操作系统的重要组成部分,其主要作用是管理计算机的内存资源,有效地分配回收内存空间,以满足系统的运行需求。以下是一个简单的通用动态分区分配存储器管理系统的设计: 1. 内存分区管理 系统需要维护一个内存分区表,记录可用和已分配的内存分区的信息。每个内存分区表项包括以下信息: - 分区大小 - 起始地址 - 是否已分配 系统需要实现内存分区的分配回收操作。分配操作根据分配请求的大小,从可用分区中选择合适的分区进行分配回收操作将已分配的分区释放,并将该分区标记为可用。 2. 内存分配算法 系统需要实现不同的内存分配算法,以满足不同的应用场景需求。常见的内存分配算法包括: - 首次适应算法:按照分区起始地址从低到高的顺序查找,找到第一个符合大小要求的空闲分区进行分配。 - 最佳适应算法:按照分区大小从小到大的顺序查找,找到最小的符合大小要求的空闲分区进行分配。 - 最坏适应算法:按照分区大小从大到小的顺序查找,找到最大的符合大小要求的空闲分区进行分配。 3. 内存碎片整理 随着内存的不断分配回收,可能会产生很多小的空闲分区,导致内存碎片化。系统需要实现内存碎片整理算法,将多个小的空闲分区合并成一个大的空闲分区,以充分利用内存资源。 4. 进程内存管理 对于每个进程,系统需要维护其内存使用情况,包括已分配的内存大小和起始地址。当进程需要更多内存时,系统需要为其分配新的内存分区,当进程不需要某些内存时,系统需要回收这些内存分区。 5. 内存保护 系统需要实现内存保护机制,防止一个进程越界访问另一个进程的内存空间,或者访问操作系统的内存空间。系统可以通过硬件机制(如MMU)或软件机制(如地址转换和访问权限控制)来实现内存保护。 以上是一个简单的通用动态分区分配存储器管理系统的设计。实现这个系统需要考虑各种不同的情况和应用场景,需要仔细设计和测试,确保其稳定性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值