模拟动态分区分配(友好用户界面显示、图形化交互界面、可视化、操作系统作业)

本文介绍了一个模拟动态分区分配的程序,实现了首次适应、循环首次适应和最佳适应算法。通过友好的图形化用户界面,程序演示了内存分配和回收的过程,以及空闲分区的变化。在内存回收后,程序会检查数据合法性,并根据选定的算法进行内存分配,展示内存区域的动态变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


一、 前言

编写一个内存动态分区分配模拟程序,分别实现:首次适应、循环首次适应、最佳适应算法,对内存的分配和回收过程,此程序没有实现“紧凑”。
每次分配和回收后把空闲分区的变化情况以及进程的申请、释放情况最好以图形方式显示,尽可能设计一个友好的用户界面,直观显示内存区域经分配、回收的动态变化情况。


二、实验结果

1、首次适应算法-程序执行结果演示

  • 进行分配算法测试数据:(进程-1表示此内存块是空闲区,没有进程占用) 申请空间的进程顺序及大小:
    在这里插入图片描述程序执行结果
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    【进行内存回收的测试:】

    进行内存回收的测试数据:(因为回收内存时,不需要内存空间大小数据,当点击确定按钮后,会检测数据是否合法,即内存中是否有此进程的id,若合法则进行回收算法,并置内存空间大小的textbox为空)
    释放的(内存块/进程)序号的顺序为:1、0、2、4、3
    进程1: 前后没有空闲区 ,没有合并空闲区操作
    进程0: 后有空闲区——>所以0、1空闲区合并一个空闲区
    进程2: 前有(0、1合并后的)空闲区——>所以(0、1)与4和并一个空闲区
    进程4: 前后没有空闲区
    进程3: 前(有0、1、2合并后的空闲区)后(有空闲区4)都有空闲区 ,合并成一个大空闲区(0、1、2、3、4)

  • 程序执行结果
    在这里插入图片描述
    【注:释放已经释放的进程空间会提示错误,如,下图,进程1已经释成功了,再点击确定按钮,想要释放就提示错误】
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    【释放内存后再申请空间去测试,首次适应算法是从内存开始位置进行申请。如,在上图测试之后,进程6 申请内存 20 M,存在两个空闲区,如下图】
    在这里插入图片描述
    【 但首次适应算法,会选择另一个内存块,结果如下,所以首次适应算法演示成功】
    在这里插入图片描述
    在这里插入图片描述

2、循环首次算法-程序执行结果演示

  • 建议循环首次算法测试数据为:
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    【 回收过程:回收进程4的内存“】
    在这里插入图片描述
    【回收进程2的内存:】
    在这里插入图片描述
    【此时空闲区有:25MB(4原来的释放区) 、28MB(2原来的释放区)、18MB(以及剩余的从未用过的空闲区)】
    在这里插入图片描述【分配:进程6 24MB 按空闲区递增排序,选择起始地址为33MB的内存块】
    在这里插入图片描述
    【回收6进程,分配:7进程 23MB】
    在这里插入图片描述
    【此时,3个空闲区如表,但起始地址为33MB大小的空闲区已经分配过了,所以会在之后的量的空闲区选择,如图,得以说明,循环首次适应算法模拟成功】
    在这里插入图片描述
    在这里插入图片描述

3、最佳适应算法-程序执行结果演示

与上类似,且可以各算法切换测试。

三、算法:

1、采用的数据结构

采用的数据结构:链表而非分区表表示内存使用情况。自定义Nodespace类用于记录每个内存块的使用情况包括空闲块和占用块。因为C#里没有指针,所以为实现链表的功能,采用C#的list类实现,Removeat():进行空闲块的合并(即为空闲块的删除)、Insert():进程申请内存空间成功把新内存块信息加入到链表,等等方法、、、、

public class Nodespace
    {
   
   
        public int taskid;   // 任务号 
        public int begin;    // 开始地址 
        public int size;     // 大小 
        public int status;   // 状态 0代表占用,1代表空闲 

        public Nodespace(int taskid, int begin, int size, int status)
        {
   
   
            this.taskid = taskid;
            this.begin = begin;
            this.size = size;
            this.status = status;
        }

2、首次适应算法

  1. 注: public static List ls = new List();
    c#中Listfind的使用参考链接如下,总结的很简单,截图如下
    c#中List < T > find使用示例
    在这里插入图片描述
  2. 函数完整代码:
 #region 首次适应算法
        public bool FirstPartition(int taskid,int size)
        {
   
   
            
            int index = ls.FindIndex(param => param.size >= size && param.status == 1);
            if (index != -1)
            {
   
   
                if (ls[index].size > size)
                {
   
   
                    //int teskid, int begin, int size, int status
                    Nodespace t = new Nodespace(taskid, ls[index].begin, size, 0);
                    ls[index].begin += size;
                    ls[index].size -= size;
                    ls.Insert(index, t);
                }
                else
                {
   
   
                    ls[index].status = 0;
                    ls[index].size = size;
                    ls[index].taskid = taskid;
                }
            }
            else
            {
   
   
                return false;
            }
            return true;
        }
        #endregion

3、循环首次适应算法

  1. 注:查找在list中从startIndex(全局变量,初始值0)到list.ount大于申请的size的空闲内存块,若没找到,再从0~startIndex找
  2. 每一次切换到循环首次算法后,都要把startIndex = 0;(我认为各内存分配算法,只是在分配时分配方法不一样,free内存释放过程一样,所以此程序中途是可以切换使用分配算法的)
 private void rb_cyclepart_CheckedChanged(object sender, EventArgs e)
        {
   
   
            startIndex = 0;
        }
  1. 函数完整代码:
 #region 循环首次适应算法
        public bool CyclyFirstPartition(int taskid, int size)
        {
   
   
            int index = ls.FindIndex(startIndex,param => param.size >= size && param.status == 1);
            if (index == -1)
            index = ls.FindIndex(0, startIndex, param => param.size >= size && param.status == 1);
            if (index != -1)
            {
   
   
                if (ls[index].size == size)
                {
   
   
                    ls[index].status = 0;
                    ls[index].taskid = taskid;
                }
                else
                {
   
   
                    Nodespace t = new Nodespace(taskid, ls[index].begin, size, 0);
                    ls[index].begin += size;
                    ls[index].size -= size;
                    ls.Insert(index, t);
                    startIndex = (index+1)%ls.Count;
                }
                return true;
            }
            else
            {
   
   
                return false;
            }
        }
        #endregion

4、最佳适应算法

  1. C#中List.Sort()–集合排序方法有四种,我选用方法一、对Nodespace类继承IComparable接口,实现CompareTo()方法,参考文章链接及截图如下,
    C#中List.Sort()–集合排序方法分析
    在这里插入图片描述
  2. 之所以设置两个排序类,因为分配时,空闲区按size大小从小到大排序从而找到最适宜的大小的空闲内存块;回收时,空闲区按开始地址排序,查找释放区的前后有无空闲区从而对应着释放空间时的4种情况(如图1),从而有空闲区进行合并的不同方式。但用NodespaceCompare_partition类进行size从小到大的排序后,后续的释放会打乱原有内存块地址递增的顺序。我在写的时候没想到这么做,出现图2所示的出现画不出内存块从而变白色,以及超过绘图范围0~200MB的情况、各种小问题。
    图1:
    图1图2:
    图2
//分配时,空闲区按size大小从小到大排序
    public class NodespaceCompare_partition : IComparer<Nodespace>
    {
   
   
        public int Compare(Nodespace x, Nodespace y)
        {
   
   
            return (x.size.CompareTo(y.size));
        }
    }
    //回收时,空闲区按开始地址排序
    public class NodespaceCompare_free : IComparer<Nodespace>
    {
   
   
        public int Compare(Nodespace x, Nodespace y)
        {
   
   
            return (x.begin.CompareTo(y.begin));
        }
  1. 函数完整代码:
 #region 最佳适应
        public bool BestPartiton(int taskid, int size) {
   
   
            //因为这三个算法目的只是选择合适的内存空间,所以中间可以切换执行算法,从而不能确保执行最佳算法之前也执行的是执行最佳算法。从而无法确保他是有序的
                ls.Sort(new NodespaceCompare_partition());
            int index = ls.FindIndex(param => param.size >= size && param.status == 1);
            if (index!= -1)
            {
   
   
                if (ls[index].size == size)
                {
   
   
                    ls[index].status = 0;
                    ls[index].taskid = taskid;
                }
                else
                {
   
   
                    Nodespace t = new Nodespace(taskid, ls[index].begin, size, 0);
                    ls[index].begin += size;
                    ls[index].size -= size;
                    ls.Insert(index,t);
                }
                return true;
            }
            return false;
        }
        #endregion

5、点击开始按钮-进行界面和数据的初始化

  1. 初始界面:程序默认选择分配内存,首次适应算法
    如图
    在这里插入图片描述

  2. 数据初始化后界面,即点击开始按钮进行数据初始化,清空内存块的进程。(点击开始按钮如下界面,左、右侧表示的内存块是(0~200MB大小的内存),左侧表示进行分配或回收的算法之前的内存状况,右侧是进行分配或回收的算法之后的内存状况)
    如图在这里插入图片描述

  3. 使用4个piicturebox,一个listview,分别对应着程序中的画图部分的图一、图二、图三、图四,图四,以及右边的表格。图四初始后固定,图1、2、3每次执行(点击确定按钮,并检测输入数据合法后执行)都会重绘

  4. 开始画图的时候,我不知道原来在bitmap中画,只要考虑相对位置就好了(因为bitmap放到picturebox,picturebox本身就带有坐标位置),就产生了四个画板什么都没有的情况,而且相对位置画这构图时简单了好多

  5. 清空并初始化数据:

			ls.Clear();
 			//加入进程名-1,起始位置0,size=200,状态1的内存块
            Nodespace nodespace = new Nodespace(-1, 0, 200, 1);
            ls.Add(nodespace);
  1. 绘图中,我觉得我的表示内存块的矩形太丑了,出现了只有右、下有边情况
    简单的更改方法: 先fill再draw.后来我觉得是画笔对齐 PenAlignment方式问题,没空就不测试了。这个参考链接的作者在写GDI学习记录系列的文章特别简单、扼要、易懂,值得学习:GDI+学习记录(4)-画笔对齐 PenAlignment
			g3.FillRectangle(sbs[0], r1);
            g3.DrawRectangle(penBlack, r1);

如图
在这里插入图片描述

在这里插入图片描述

  1. 函数完整代码:
        #region 开始按钮
        private void bt_start_Click(object sender, EventArgs e)
        {
   
   
            ls.Clear();
            Nodespace nodespace = new Nodespace(-1, 0, 200, 1);
            ls.Add(nodespace);
            
            Graphics g12 = pb_mem1.CreateGraphics();
            g12.Clear(Color.White);

            Graphics g22 = pb_mem2.CreateGraphics();
            g22.Clear(Color.White);

            Graphics g32 = pb_mem3.CreateGraphics();
            g32.Clear(Color.White);
            
            Bitmap b1 = new Bitmap(200, 480);
            Graphics g1 = Graphics.FromImage(b1);
            Rectangle r1 = new Rectangle(30,40,90,ls[0].size);
            g1.FillRectangle(sbs[0], r1);
            g1.DrawRectangle(penBlack, r1);
            g1.DrawString("0MB", myfont, sbs[3], new Point(0, 30));
            g1.DrawString("200MB", myfont, sbs[3], new Point(0, 240));
            g12.DrawImage(b1, 0, 0);
            
            Bitmap b3 = new Bitmap(200, 480);
            Graphics g3 = Graphics.FromImage(b3);
            g3.FillRectangle(sbs[0], r1);
            g3.DrawRectangle(penBlack, r1);
            g3.DrawString("0MB", myfont, sbs[3], new Point(0, 30));
            g3.DrawString("200MB", myfont, sbs[3], new Point(0, 240));
            g32.DrawImage(b3, 0, 0);

            //free 图1
            b1.Dispose();
            b1 = null;
            g1.Dispose();
            g1 = null;
            g12.Dispose();
            g12 = null;
            //free 图3
            b3.Dispose();
            b3 
实验内容: 编写一个动态分区分配算法模拟程序,加深对动态分区存储管理方式及其实现过程的理解。 要求: 1.空闲分区通过空闲区链进行管理,在内存分配时,优先考虑低地址部分的空闲区。 2.分别采用首次适应算法、最佳适应算法和最坏适应算法模拟内存空间的动态分配与回收,每次分配和回收后显示出空闲区链的详细情况(说明:在申请不成功时,需要打印当前内存的占用情况信息)。 3.进程对内存空间的申请和释放可由用户自定义输入。 4.参考请求序列如下: (1) 初始状态下可用内存空间为640KB; (2) 进程1申请130KB; (3) 进程2申请60KB; (4) 进程3申请100KB; (5) 进程2释放60KB; (6) 进程4申请200KB; (7) 进程3释放100KB; (8) 进程1释放130KB; (9) 进程5申请140KB; (10) 进程6申请60KB; (11) 进程7申请50KB; (12) 进程6释放60KB。 测试用例格式如下: 输入: 动态分区分配算法选择 可用内存空间容量 序号/进程号/申请或释放操作/申请或释放的容量 其中: (1) 动态分区分配算法:1----首次适应,2----最佳适应,3----最坏适应 (2) 申请或释放操作: 1----申请操作,2----释放操作 输出: 序号/内存空间状态1/内存空间状态2...... 内存空间状态表示分为两种情况: (1) 内存空间被占用: 内存空间起始地址-内存空间结束地址.1.占用的进程号 (2) 内存空间空闲 内存空间起始地址-内存空间结束地址.0
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值