操作系统实验导航
实验一:银行家算法 https://blog.csdn.net/weixin_46291251/article/details/115384510
实验二:多级队列调度和多级反馈队列调度算法 https://blog.csdn.net/weixin_46291251/article/details/115530582
实验三:动态分区式内存管理 https://blog.csdn.net/weixin_46291251/article/details/115772341
实验四:Linux下多进程通信 https://blog.csdn.net/weixin_46291251/article/details/116274665
实验五:进程通信的三种方式 https://blog.csdn.net/weixin_46291251/article/details/116301250
实验六:Linux文件系统实验 https://blog.csdn.net/weixin_46291251/article/details/116423798
实验七:自制简单U盘引导程序 https://blog.csdn.net/weixin_46291251/article/details/116427629
实验八:磁盘调度算法 https://blog.csdn.net/weixin_46291251/article/details/116431907
实验九:请求分页系统中的置换算法 https://blog.csdn.net/weixin_46291251/article/details/116443021
学习笔记:操作系统复习笔记 https://blog.csdn.net/weixin_46291251/article/details/117086851

背景知识:

关于内存管理的几种方式:

固定分区:

原理:又称定长分区或静态分区模式,是满足多道程序设计需要的最简单的存储管理技术。基本思想:给进入主存的用户作业划分一块连续存储区域,把作业装入该连续存储区域,若有多个作业装入主存,则它们可并发执行。

使用大小相等的固定分区有两个难点:程序可能太大而不能放到一个分区中,内存的,利用率很低。由于被装入的数据块小于分区大小,从而导致分区内部有浪费现象,成为“内部碎片”。对与大小不等的分区策略,最简单的方法就是把每个进程分配到能够容纳它的最小分区中。

目前已经基本上没有什么场合使用固定分区。

优势:实现简单,只需要极少的操作系统开销

缺点:有内部碎片,对内存的使用不充分,活动进程的最大数目是固定的。

可变分区:

可变分区存储管理不是预先把内存中的用户区域划分成若干固定分区,而是在作业要求装入内存时,根据用户作业的大小和当时内存空间使用情况决定是否为该作业分配一个分区。因此分区大小不是预先固定的,而是按作业需求量来划分的;分区的个数和位置也不是预先确定的。它有效地克服了固定分区方式中,由于分区内部剩余内存空置造成浪费的问题。
可变分区方式常用的内存分配算法有以下几种:

  • 1、 最先适应分配算法
      每次分配总是顺序查找空闲区表,找到能满足长度要求的空闲区就分配。优点是实现简单,缺点是可能将大的空闲区分割成许多小的空闲区,形成许多不连续的“碎片”。碎片长度可能不能满足作业要求,降低了内存利用率。
      改进方法,可把空闲区按地址顺序从小到大登记在空闲区表中,有利于大作业。问题是归还空区时须按地址插入表中适当位置。

  • 2、最优适应分配算法
      按作业要求从所有空闲区中挑选一个能满足要求的最小空闲区,这样保证不去分割一个更大的区域,使装入大作业时比较容易得到满足。实现办法:将空闲区按长度以递增次序登记在表中,分配时按空闲区表顺序查找即可。缺点是可能碎片更小而无法使用。回收时也要按长度扦入。

  • 3、最坏适应分配算法
      这种算法总是挑选一个最大的空闲区分割一部分给作业使用,使剩下部分不致太小,仍可供分配使用。实现办法:空闲区表中的登记项按空闲区长度递减顺序排列,按序查找分配。

题目描述:动态分区式存贮区管理

设计一个动态分区式存贮区管理程序,要求支持不同的放置策略。如首次、最佳、最坏。

说明:

(1)分区描述器rd如下:
flag size next
  要求空闲区队列按链表组织。主存大小假设为maxsize(单位为节=rd的大小)。
(2)主程序结构如下:
  输入放置策略     申请一块内存作为主存循环处理用户的请求(包括申请、释放)

需设计两个函数处理用户请求:

  • 申请函数 Addr=Request(size)
  • 释放函数 Release(addr)
(3)数据实例:maxsize=512
    J1申请162,J2申请64,J3申请120,J4申请86,J1完成,J3完成,J5申请72,J6申请100,J2完成,J7申请36,J8申请60,J4完成, J9申请110,J10申请42。

备注:

  • (a)所有大小其单位为节(1节=rd的大小)
  • (b)作业申请n节,实际分配的分区大小应为n+1节。 其中一节作为分区描述器,其他n节提供给作业。
  • (c)已分配区放在高地址处。
  • (d)合并时应考虑四种情况: 假设回收区为r,上邻为f1(f1需搜索自由主存队列),下邻为f2(f2可直接计算)
    A)f1空闲,f2已分配;
    B)f1已分配,f2空闲;
    C)f1空闲,f2空闲;
    D)f1已分配,f2已分配;

设计思路:

  • 向系统申请内存:
    需要申请的内存单元的个数:totalNum
    每个内存单元的大小:sizeof(rd)
    程序实际向系统申请到达大小:maxsize
    即:
    const int totalNum = 512;//需要申请的块数
    const int maxsize = totalNum * sizeof(rd);//实际需要申请的大小
    rd* MainHead = (rd*)malloc(maxsize);//申请到的资源

  • 进程向程序申请内存Request:
    原型:rd* Request(int need,int choice);
    参数:need表示需要申请的大小,choice表示分配时采取的方法。
    返回值:返回一个指针,指向申请到的空间的首地址,若为空则表示申请失败。

  • 进程释放内存Release:
    原型:bool Release(rd* r);
    参数:r表示待释放内存的首地址
    返回值:返回1表示释放成功,0表示失败

  • 可视化展示内存使用情况:
    每一次读取用户的请求后,程序均输出当前的内存使用情况,方便用户查看,包括空闲队列的情况、已用队列的情况、内存总体分配情况。

数据结构:

  • 分区描述器:
    设计一个结构体rd用来表示分区描述器,其包含:
    Flag:分配标志,空闲为0,不为0则表示进程编号
    Size:分区大小,分区可用字数+分区描述器大小
    *next:指向下一个同类型的区域
struct rd {//分区描述器int flag = -1;  //分配标志,空闲为0,不为0则表示进程编号int size = -1;   //分区大小,分区可用字数+分区描述器大小rd* next = NULL;   //指向下一个同类型的区域
};
  • 空闲内存队列和已用内存队列:
    两个队列都通过链表的形式组织
    分别为其设置头节点Free和Used:
rd* Free = new rd, * Used = new rd;//分别用来存空闲队列和已占用队列

算法设计

三种算法:
  首次:按物理地址的顺序排列,优先地址最小的最佳:按照空闲容量递增排列,优先最小的最坏:按照空闲容量递减排列,优先最大的

三种算法在分配和释放的操作上没有区别。
区别在于:空闲队列变化以后需要排序,该排序对应的排序算法不同(依据是以上原理)。但在本程序中不使用排序算法,而是每次都遍历搜索满足条件的最值。

进程向程序申请内存Request:

无论是利用上述的哪种方法,都需要从已经给定的头节点开始遍历,找到>=需求的一块空间。
找到满足上述三种算法中最优的内存块:找到内存大小比need大的内存块中地址最小的、内存最小的、内存最大的。
上述最优的内存块分两种情况:

  • 内存=need:这种情况只需要在free队列删除该节点,并将其加入used队列即可。
  • 内存>need:这种情况需要将原节点信息中的size减小need大小然后在used队列中插入一个新节点。
进程释放内存Release:

释放进程时需要分以下四种情况:假设三块连续内存分别为f1、r、f2
上邻为f1(f1需搜索自由主存队列),下邻为f2(f2可直接计算)

  • A)f1空闲,f2已分配; //f和f1合并,修改表项f1,数量不变
  • B)f1已分配,f2空闲; //f和f2合并,修改表项f2,数量不变
  • C)f1空闲,f2空闲; //f1 f f2合并,修改表项f1,删除f2
  • D)f1已分配,f2已分配; //增加空闲表项f

这里设计两个信号量bool f1_is_free = 1, f2_is_free = 1;分别用来判断f1、f2是否已分配,然后程序遍历两个队列正确修改信号量的值后,程序根据这两个值分四种情况对两个队列做出相应的修改即可。

可视化展示内存使用情况:

首先遍历输出两个队列的以下信息:

  • 1.头节点相对于总内存起点的偏移量
  • 2.该段内存实际大小

由于本程序中插入队列元素时没有对其排序,上述输出不直观,所以继续采用以下方法输出:
由内存的起点开始向后同时遍历两个队列,每次找到与之最相邻的那个内存块。
找到内存块后由flag判断其是否已占用,由size判断这块内存的大小。
输出头节点相对于总内存起点的偏移量然后输出不同数量的符号表示这块空间
原则:未占用用‘-’表示,已占用用‘+’表示
输出的数量(长度)与内存大小成正比。
输出示例:

整体实现的代码:

#include <iostream>
#include <fstream>
using namespace std;struct rd {//分区描述器int flag = -1;  //分配标志,空闲为0,//不为0则表示进程编号int size = -1;   //分区大小,分区可用字数+分区描述器大小rd* next = NULL;   //对空闲区:指向下一个空闲分区,对已分配区:此项为零。
};
rd* Free = new rd, * Used = new rd;//分别用来存空闲队列和已占用队列const int totalNum = 512;//需要申请的块数
const int maxsize = 512 * sizeof(rd);//实际需要申请的大小
rd* MainHead = (rd*)malloc(maxsize);//申请到的资源rd* Request(int need,int choice) {//申请地址的函数,返回申请到的地址,为NULL则无法申请。//size为要申请的danwei大小的个数,实际大小为size* sizeof(rd);   int tem;rd* Ans = NULL;rd* p = NULL, * prep = new rd, * preAns = new rd;if (choice == 1) {//首次算法:按物理地址的顺序排列,优先地址最小的tem = INT_MAX;for (p = Free->next, prep->next = p; p != NULL; p = p->next, prep = prep->next)if ((int)p < tem && p->size > need)tem = (int)p, Ans = p, preAns = prep;if (Ans == NULL)return NULL;}else if (choice == 2) {//最佳算法按:照空闲容量递增排列,优先最小的tem = totalNum;for (p = Free->next, prep->next = p; p != NULL; p=p->next, prep= prep->next)if (p->size < tem && p->size > need)tem = p->size, Ans = p, preAns = prep;if (Ans == NULL)return NULL;}else  if (choice == 3) {//最坏算法:按照空闲容量递减排列,优先最大的tem = 0;for (p = Free->next, prep->next = p; p != NULL; p = p->next, prep = prep->next)if (p->size > tem) tem = p->size, Ans = p, preAns = prep;if (tem < need)return NULL;}//修改free队列if (Ans->size == need)preAns->next = Ans->next;//删除表项else {Ans->size -= need;//Ans =(Ans+Ans->size * sizeof(rd));//后一半划分出去//       cout<<"当前分配地址距离开始" << Ans - MainHead << endl;Ans += Ans->size;}Ans->flag = 1;Ans->size = need;//修改used队列if (Used->next == NULL) {Used->next = Ans;Ans->next = NULL;}   else {Ans->next = Used->next;Used->next = Ans;}return Ans;
}
bool Release(rd* r) {//释放所给出的分区的地址,返回1表示正常释放if (r->flag == 0) {cout << "该空间未使用,无法释放" << endl;return 0;}rd* p = NULL;rd* f1 = NULL, * f2 = NULL;//先判断usedfor (p = Used->next; p != NULL; p = p->next) {if (p->next == r)f1 = p;if (r->next == p)f2 = p;}//再判断freefor (p = Free->next; p != NULL; p = p->next) {if (p+p->size == r)f1 = p;if (r+r->size == p)f2 = p;}//修改used队列for (p = Used->next; p != NULL; p = p->next)if (p->next == r) {p->next = r->next;break;}bool f1_is_free = 1, f2_is_free = 1;if (f1->flag)f1_is_free = 0;if (f2 == NULL || f2->flag)f2_is_free = 0;//修改free队列if (f1_is_free && !f2_is_free) {     // A)f1空闲,f2已分配;     //f和f1合并,修改表项f1,数量不变f1->next = r->next;f1->size += r->size;}else if (!f1_is_free && f2_is_free) { // B)f1已分配,f2空闲;     //f和f2合并,修改表项f2,数量不变r->next = f2->next;r->flag = 0;r->size += f2->size;for (p = Free; p != NULL; p = p->next)//找到f2的前驱if (p->next == f2) {p->next = r;//后继修改为rbreak;}}else if (f1_is_free && f2_is_free) {  // C)f1空闲,f2空闲;       //f1 f f2合并,修改表项f1,删除f2for (p = Free; p != NULL; p = p->next)//找到f1的前驱if (p->next == f1) break;if (p != f2->next)//防止构成环路f1->next = f2->next;f1->size += (f2->size+r->size);for (p = Free; p != NULL; p = p->next)//找到f2的前驱if (p->next == f2) {p->next = f2->next;//后继修改为rbreak;}}else if (!f1_is_free && !f2_is_free) {// D)f1已分配,f2已分配;   //增加空闲表项fr->flag = 0;if (Free->next == NULL) {Free->next = r;r->next = NULL;}else {r->next = Free->next;Free->next = r;}}return 1;
}
void show_detail() {//展示内存的使用情况rd* p = NULL;rd* Cur = MainHead;//先判断usedcout <<"Used:--";for (p = Used->next; p != NULL; p = p->next) {cout << p - MainHead <<"_"<<p->size<< "--->";}cout << endl;//再判断freecout << "Free:--";for (p = Free->next; p != NULL; p = p->next) {cout << p - MainHead << "_" << p->size << "--->";}cout << endl;do{//直到当前是最后一块为止for (p = Free->next; p != NULL; p = p->next)if (Cur == p) {break;}for (p = Used->next; p != NULL; p = p->next)if (Cur == p) {break;}//到此就找到了上一次的Cur后跟的块(free或者used)//输出可视化//512个字节,共分为26段,已用用+++++表示,未用用======表示,每段用三个符号表示string u = "+++";string f = "---";if (Cur->flag == 0) {//为free//  cout << Cur->size;cout << Cur - MainHead;for (int i = 0; i < (Cur->size / 20); i++)cout << f;}if (Cur->flag != 0) {//为freecout << Cur - MainHead;for (int i = 0; i < (Cur->size / 20); i++)cout << u;}Cur += Cur->size;} while ((Cur - MainHead )  < totalNum-1);cout << "|||";cout << endl << endl << endl << endl;}int main()
{while (1) {cout << "——————————请输入放置策略————————" << endl;cout << "—— 首次:1     最佳:2    最坏:3    退出:0——" << endl;cout << "—————————————————————————" << endl;int method ;cin >> method;if(method==0) {system("cls");cout << "——————————Thanks!————————" << endl;return 0;}if (method != 1 && method != 2 && method != 3) {cout << "您的输入有误,请重新输入:" << endl;continue;}MainHead = (rd*)malloc(maxsize);//申请到的资源Free = new rd, Used = new rd;//分别用来存空闲队列和已占用队列MainHead->next = NULL;rd* fir = MainHead;fir->next = NULL; fir->flag = 0;  fir->size = totalNum - 1;Free->next = fir;char operat;int id, need;fstream In;In.open("请求.txt");if (!In.is_open())cout << "文件打开失败" << endl;cout << "初始状态:" << endl; show_detail();//展示初始状态while (!In.eof()) {In >> operat;if (operat == '+') {In >> id >> need;rd* req = Request(need+1, method);//req为申请到的地址if (req == NULL) {cout << "     失败___" << id << " +++ " << need << " " << endl;break;}cout << "     成功___" <<id<< " +++ " << need << " " << endl;req->flag = id;req->size = need+1;}else if (operat == '-') {In >> id ;rd* rel = Used->next;for (; rel != NULL; rel=rel->next)if (rel->flag == id)break;int rel_ans = 1;int rel_size = rel->size;if (rel == Used->next) {cout << "进程" << id << "没有正在使用的空间" << endl;break;}else rel_ans = Release(rel);if (rel_ans == 0) {cout << "进程___" << id << "无法释放" << endl;}else cout << "     成功___" << id << " --- " << rel_size << "  " << endl;}else {cout << operat << "读取失败" << endl;break;}show_detail();//展示每次修改后的结果}free(MainHead);
}return 0;
}

程序运行情况:

测试数据:
运行截图:
  • 方法二:最佳优先算法

动态分区式内存管理(完整代码)相关推荐

  1. 连续内存分区式内存管理

    目录 前言 分区式内存管理 动态分区内存管理 总结 本笔记参考黄工的https://mp.weixin.qq.com/s/k0W_LqI1zBAYC1GU1U2HQA 前言 内存管理模块主要负责内存的 ...

  2. java 动态分区 链表_大二作业——操作系统实验——C语言用双向链表,模拟实现动态分区式存储管理...

    实验:动态分区式存储管理 实验内容: 编写程序模拟完成动态分区存储管理方式的内存分配和回收.实验具体包括:首先确定内存空闲分配表:然后采用最佳适应算法完成内存空间的分配和回收:最后编写主函数对所做工作 ...

  3. 【OS学习笔记】三十 保护模式九:段页式内存管理机制概述

    上几篇文章学习了任务切换相关知识,如下: [OS学习笔记]二十六 保护模式八:任务门-任务切换 [OS学习笔记]二十七 保护模式八:任务切换的方法之----jmp与call的区别以及任务的中断嵌套 今 ...

  4. 22-23 - 页式内存管理

    ---- 整理自狄泰软件唐佐林老师课程 查看所有文章链接:(更新中)深入浅出操作系统 - 目录 文章目录 1. 段式内存管理回顾 1.1 段式内存管理介绍 1.2 软硬件技术的发展 1.2.1 可行的 ...

  5. CUDA编程: GPU与CPU之间使用全局统一内存的完整代码及编译

    CUDA编程: GPU与CPU之间使用全局统一内存的完整代码及编译 最近碰到一个应用场景,需要从GPU访问host上创建的,一个很大的布隆过滤器(准确说是改进后的布谷鸟过滤器).由于GPU卡上的显存有 ...

  6. ARM和英特尔的386系列内存管理MMU硬件机制不同,ARM是基于协处理CP15(核心是C2 TTRB0,1)分页式,386是以段寄存器和CR3寄存器分段分页式内存管理

    ARM和英特尔的386系列内存管理MMU硬件机制不同,ARM是基于协处理CP15(核心是C2 TTRB0,1)分页式,386是以段寄存器和CR3寄存器分段分页式内存管理

  7. Linux 操作系统原理 — 内存管理 — 页式内存管理技术

    目录 文章目录 目录 虚拟内存技术 页式内存管理技术 虚拟内存技术 虚拟内存技术是操作系统实现的一种高效的物理内存管理方式,具有以下作用: 使得进程间彼此隔离:通过将物理内存和虚拟地址空间联系起来,并 ...

  8. 操作系统【五】分段内存管理+段页式内存管理

    基本分段存储管理 与分页最大的区别:离散分配时所分配地址空间的基本单位不同 进程的地址空间:按照程序自身的逻辑关系划分为若干个段,每个段都有一个段名,每段从0开始编址 内存分配规则:以段位单位进行分配 ...

  9. Go内存管理之代码的逃逸分析

    基本上,每种编程语言都有其自己的内存模型.每个变量,常量都存储在内存的某个物理位置上,这些存储位置通过内存指针访问. 至于变量,就是程序里赋予内存存储位置的名称.程序可以根据需要进行操作,并且可以将新 ...

最新文章

  1. Leetcode 124题:求⼆叉树中最⼤路径和
  2. NYOJ 888 取石子(九)
  3. Java黑皮书课后题第5章:*5.30(金融应用:复利值)假设你每月在储蓄账户上多存100美元,年利率为5%,那么每月利率是0.05 / 12 = 0.00417。编写程序提示用户输入数据显示定月钱数
  4. Win32ASM学习[3]:局部变量
  5. LSGO软件技术团队2015~2016学年第五周(0928~1004)总结
  6. 前端基础-git(三):git和GitHub的一些基础操作
  7. 基本功:超全面 IO 流教程,小白也能看懂
  8. 值得电商美工借鉴的购物APP页面设计,让人无法自拔
  9. mysql 组内排名_【原】MySQL分组排序(包含组内排名、求中位数)
  10. 二手青春——代码之路1
  11. 台式计算机颜色如何矫正,台式机怎么颜色校正操作教程
  12. macbook重装系统 选择方案_Mac如何重装系统?mac重装系统教程
  13. ARM CORTEX M3
  14. c语言————开辟动态内存空间
  15. 浅墨博客《Real Time Rendering 3rd》提炼总结 截取(一)
  16. 秀米中如何添加链接、文件链接、小程序链接?
  17. 2014广东计算机一级试题及答案,广东计算机一级试题2014版
  18. 电子学会图形化scratch编程等级考试二级真题答案解析(选择题)2020-9A卷
  19. MYSQL数据库密码的加密方式及破解方法
  20. 数睿数据资讯 |《洞见・大咖说》对话数睿数据李争辉 探讨400%高增长背后的关键抓手

热门文章

  1. CentOS7 ftp服务离线安装
  2. MATLAB信号处理---学习小案例(10)---Z反变换
  3. 配置Hiveserver 高可用,新增Hiveserver2启动失败
  4. 蜂房问题-蜜蜂在蜂房中只能向下或者右上方爬,从a位置爬到b位置有多少种途径
  5. 人间哪知星空遥:荣耀30系列的巡天计划
  6. 时过一年,我还在原地踏步么
  7. php无法访问_php突然不能访问的原因
  8. 关于物联网的误区,你有没有中招?
  9. 跳出横向的、孤立的牵绊,真正站在新的角度来看待Web3.0
  10. 2022-05-14 ubuntu下OpenCV环境搭建成功