动态分区分配

一.目的

  1. 加深对动态分区分配的理解,进一步掌握首次适应算法和最佳适应算法的理解。了解动态分区分配方式中使用的数据结构和分配算法,进一步加深对动态分区存储管理方式及其实现过程的理解。提高学生设计实验、发现问题、分析问题和解决问题的能力。
  2. 学会可变式分区管理的原理是在处理作业过程中建立分区,使分区大小正好适合作业的需求。
  3. 当一个作业执行完成后,作业所占的分区应归还给系统。

二.原理

首次适应算法

以空闲分区链为例来说明采用FF算法时的分配情况。FF算法要求空闲分区链以地址递增的次序链接。在分配内存时,从链首开始顺序查找,直至找到一个大小能满足要求的分区为止;然后再按照作业的大小,从该分取中划出一块内存空间分配给请求者,余下的空闲分区仍留在空闲链中。若从链首直到链尾都不能找到一个能满足要求的分区,则此次内存分配失败,返回。该算法倾向于优先利用内存中低地址部分的空闲分区,从而保留了高址部分的大空闲区。这给为以后到达的大作业分配大的内存空间创造了条件,其缺点是低址部分不断被划分,会留下许多难以利用的、很小的空闲分区,而每次查找又都是从低址部分开始,这无疑会增加查找可用空闲分区时的开销。

最佳适应算法

所谓“最佳”是指每次为作业分配内存时,总是把能满足要求、又是最小的空闲分区分配给作业,避免“大材小用”。为了加速寻找,该算法要求将所有的空闲分区按其容量以从小到大的顺序形成以空闲分区链。这样,第一次找到的能满足要求的空闲区,必然是最佳的。孤立地看,最佳适应算法似乎是最佳的,然而在宏观上却不一定。因为每次分配后所割下来的剩余部分总是最小的,这样,在存储器中会留下许多难以利用的开销。

三.实验流程图

首次适用算法

最佳适用算法

四.结果截图

经过多次内存后:

回收作业1和作业4后:

此时分两种情况分别模拟首次使用算法和最佳使用算法为作业6分配40KB内存:

模拟首次适应算法:

最佳适用算法:

五.结果分析

通过多个分区分配,当回收作业1释放100K空间,回收作业4释放80K空间后,再为作业6分配40K空间。首次适用算法会优先拿作业1释放的100K空间为作业6分配内存。最佳适用算法则会优先拿作业4释放的80K空间为作业6分配内存。

根据实验得出结论,首次适用算法是从空闲分区表的头指针开始查找,把最先能够满足要求的空闲区分配给作业。该算法优先使用低址部分空闲区,在低址空间造成许多小的空闲区,在高地址空间保留大的空闲区,此算法比较节省时间。

最佳适用算法将可利用空闲表中一个不小于“请求”且最接近"请求"的空闲区的一部分分配给用户。分配与回收都需要对可利用空闲表从头至尾查询一遍。在分配时容易产生太小而无法利用的内存碎片,同时这种做法也保留了那些很大的内存块以备响应将来发生的内存量较大的用户的请求。这种分配算法适合请求分配内存大小范围较广的系统,此算法最费时间。

六.程序清单(VS 2013)

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<stdlib.h>

enum STATE
{
  Free,
  Busy
};

struct subAreaNode
{
  int addr;              // 起始地址
  int size;              // 分区大小
  int taskId;            // 作业号
  STATE state;             // 分区状态
  subAreaNode *pre;     // 分区前向指针
  subAreaNode *nxt;       // 分区后向指针
}subHead;

// 初始化空闲分区链
void intSubArea()
{
  // 分配初始分区内存
  subAreaNode *fir = (subAreaNode *)malloc(sizeof(subAreaNode));
  // 给首个分区赋值
  fir->addr = 0;
  fir->size = 1000;     // 内存初始大小
  fir->state = Free;
  fir->taskId = -1;
  fir->pre = &subHead;
  fir->nxt = NULL;
  // 初始化分区头部信息
  subHead.pre = NULL;
  subHead.nxt = fir;
}

// 首次适应算法
int firstFit(int taskId, int size)
{
  subAreaNode *p = subHead.nxt;
  while (p != NULL)
  {
    if (p->state == Free && p->size >= size)
    {
      // 找到要分配的空闲分区
      if (p->size - size <= 10)
      {
        // 整块分配
        p->state = Busy;
        p->taskId = taskId;
      }
      else {
        // 分配大小为size的区间
        subAreaNode *node = (subAreaNode *)malloc(sizeof(subAreaNode));
        node->addr = p->addr + size;
        node->size = p->size - size;
        node->state = Free;
        node->taskId = -1;
        // 修改分区链节点指针
        node->pre = p;
        node->nxt = p->nxt;
        if (p->nxt != NULL)
        {
          p->nxt->pre = node;
        }
        p->nxt = node;
        // 分配空闲区间
        p->size = size;
        p->state = Busy;
        p->taskId = taskId;
      }
      printf("内存分配成功!\n");
      return 1;
    }
    p = p->nxt;
  }
  printf("找不到合适的内存分区,分配失败...\n");
  return 0;
}

// 最佳适应算法
int bestFit(int taskId, int size)
{
  subAreaNode *tar = NULL;
  int tarSize = 1000 + 1;
  subAreaNode *p = subHead.nxt;
  while (p != NULL)
  {
    // 寻找最佳空闲区间
    if (p->state == Free && p->size >= size && p->size < tarSize)     {
      tar = p;
      tarSize = p->size;
    }
    p = p->nxt;
  }
  if (tar != NULL)   {
    // 找到要分配的空闲分区
    if (tar->size - size <= 10)
    {
      // 整块分配
      tar->state = Busy;
      tar->taskId = taskId;
    }
    else
    {
      // 分配大小为size的区间
      subAreaNode *node = (subAreaNode *)malloc(sizeof(subAreaNode));
      node->addr = tar->addr + size;
      node->size = tar->size - size;
      node->state = Free;
      node->taskId = -1;
      // 修改分区链节点指针
      node->pre = tar;
      node->nxt = tar->nxt;
      if (tar->nxt != NULL)
      {
        tar->nxt->pre = node;
      }
      tar->nxt = node;
      // 分配空闲区间
      tar->size = size;
      tar->state = Busy;
      tar->taskId = taskId;
    }
    printf("内存分配成功!\n");
    return 1;
  }
  else
  {
    printf("找不到合适的内存分区,分配失败...\n");
    return 0;
  }
}

int freeSubArea(int taskId)     // 回收内存
{
  int flag = 0;
  subAreaNode *p = subHead.nxt, *pp;
  while (p != NULL)
  {
    if (p->state == Busy && p->taskId == taskId)
    {
      flag = 1;
      if ((p->pre != &subHead && p->pre->state == Free)&& (p->nxt != NULL && p->nxt->state == Free))
      {
        // 情况1:合并上下两个分区
        // 先合并上区间
        pp = p;
        p = p->pre;
        p->size += pp->size;
        p->nxt = pp->nxt;
        pp->nxt->pre = p;
        free(pp);
        // 后合并下区间
        pp = p->nxt;
        p->size += pp->size;
        p->nxt = pp->nxt;
        if (pp->nxt != NULL)
        {
          pp->nxt->pre = p;
        }
        free(pp);
      }
      else if ((p->pre == &subHead || p->pre->state == Busy)&& (p->nxt != NULL && p->nxt->state == Free))
      {
        // 情况2:只合并下面的分区
        pp = p->nxt;
        p->size += pp->size;
        p->state = Free;
        p->taskId = -1;
        p->nxt = pp->nxt;
        if (pp->nxt != NULL)
        {
            pp->nxt->pre = p;
        }
        free(pp);
      }
      else if ((p->pre != &subHead && p->pre->state == Free)&& (p->nxt == NULL || p->nxt->state == Busy))
      {
        // 情况3:只合并上面的分区
        pp = p;
        p = p->pre;
        p->size += pp->size;
        p->nxt = pp->nxt;
        if (pp->nxt != NULL)
        {
          pp->nxt->pre = p;
        }
        free(pp);
      }
      else
      {
          // 情况4:上下分区均不用合并
          p->state = Free;
          p->taskId = -1;
      }
    }
    p = p->nxt;
  }
  if (flag == 1)
  {
    // 回收成功
    printf("内存分区回收成功...\n");
    return 1;
  }
  else
  {
    // 找不到目标作业,回收失败
    printf("找不到目标作业,内存分区回收失败...\n");
    return 0;
  }
}

// 显示空闲分区链情况
void showSubArea()
{
  printf("*********************************************\n");
  printf("**         当前的内存分配情况如下:        **\n");
  printf("*********************************************\n");
  printf("** 起始地址 | 空间大小 | 工作状态 | 作业号 **\n");
  subAreaNode *p = subHead.nxt;
  while (p != NULL)
  {
    printf("**-----------------------------------------**\n");
    printf("**");
    printf("  %3d  k  |", p->addr);
    printf("  %3d  k  |", p->size);
    printf("   %s   |", p->state == Free ? "Free" : "Busy");
    if (p->taskId > 0)
    {
      printf("   %2d   ", p->taskId);
    }
    else
    {
      printf("        ");
    }
    printf("**\n");
    p = p->nxt;
  }
  printf("*********************************************\n");
}

int main()
{
  int option, ope, taskId, size;
  // 初始化空闲分区链
  intSubArea();
  // 选择分配算法
  while (1)
  {
    printf("\n\n");
    printf("\t****************请选择要模拟的分配算法******************\n");
    printf("\n\n");
    printf("\t \t        0    首次适应算法  \n");
    printf("\n\n");
    printf("\t \t        1    最佳适应算法  \n");
    printf("\n\n");
    printf("\t\t\t\t你的选择是:");
    scanf("%d", &option);
    if (option == 0)
    {
      printf("你选择了首次适应算法,下面进行算法的模拟\n");
      break;
    }
    else if (option == 1)
    {
      printf("你选择了最佳适应算法,下面进行算法的模拟\n");
      break;
    }
    else
    {
      printf("错误:请输入 0/1\n\n");
    }
  }
  // 模拟动态分区分配算法
  while (1)
  {
    printf("\n");
    printf("*********************************************\n");
    printf("**  1: 分配内存  2: 回收内存  0: 退出     **\n");
    printf("*********************************************\n");
    scanf("%d", &ope);
    if (ope == 0) break;
    if (ope == 1)    {
      // 模拟分配内存
      printf("请输入作业号: ");
      scanf("%d", &taskId);
      printf("请输入需要分配的内存大小(KB): ");
      scanf("%d", &size);
      if (size <= 0)
      {
        printf("错误:分配内存大小必须为正值\n");
        continue;
      }
      // 调用分配算法
      if (option == 0)
      {
        firstFit(taskId, size);
      }
      else
      {
        bestFit(taskId, size);
      }
      // 显示空闲分区链情况
      showSubArea();
    }
    else if (ope == 2)
    {
      // 模拟回收内存
      printf("请输入要回收的作业号: ");
      scanf("%d", &taskId);
      freeSubArea(taskId);
      // 显示空闲分区链情况
      showSubArea();
    }
    else
    {
      printf("错误:请输入 0/1/2\n");
    }
  }
  printf("分配算法模拟结束\n");
  system("pause");
  return 0;
}

  

时间: 2024-08-07 01:17:00

动态分区分配的相关文章

使用动态分区分配方式的模拟

1实验目的 (1)了解动态分区分配方式中使用的数据结构和分配算法 (2)加深对动态分区存储管理方式及其实现过程的理解. 2实验内容 (1)分别实现采用首次适应算法和最佳适应算法的动态分区分配过程alloc()和回收过程free().其中,空闲分区通过空闲分区链来管理:在进行内存分配时,系统优先使用空闲区低端的空间. (2)假设初始状态下,可用的内存空间为640KB,并有下列的请求序列: •作业1申请130KB. •作业2申请60KB. •作业3申请100KB. •作业2释放60KB. •作业4申

操作系统——动态分区分配方式模拟

这里直接给出代码,如果不理解请参考左万历版<计算机操作系统教程>,先在给出四中模拟算法. 1.   设计目的 了解动态分区分配中使用的数据结构和分配算法,并进一步加深对动态分区存储管理方式及其实现过程的理解. 2.   设计内容 1)用C语言实现采用首次适应算法的动态分区分配过程alloc()和回收过程free().其中,空闲分区通过空闲分区链表来管理,在进行内存分配时,系统优先使用空闲区低端的空间. 2)假设初始状态如下,可用的内存空间为640KB,并有下列的请求序列: 作业1申请130KB

实验三:内存动态分区分配

内存动态分区分配和回收的模拟实现 实验目的 加深对内存管理的理解,进而对连续分配和离散分配有更深刻的认识. 通过内存管理,进一步理解进程等操作系统概念. 实验内容 模拟实现内存动态分区分配和回收 建立一个长度为1024的一维数组,用以模拟内存. 建立空闲分区表或空闲分区链,用来记录内存的使用情况. 为请求进入内存的作业分配内存,回收运行完成的作业所占内存. 键盘输入:表示一个请求进入内存的作业的三元组: (作业号(0–9),作业大小(1-1024),作业运行时间) 程序接受键盘输入,进行内存分配

第二十二章 动态分区管理(LPAR)

一.逻辑分区 Lpar即系统级的逻辑分区,它把一台计算机上的硬件资源划分成多个不同的逻辑服务器,每个逻辑服务器上单独运行一个私有的操作系统,这样就可以实现在一台服务器上多个操作系统的运行. 根据在逻辑分区中调配资源是否需要重启这个分区中的操作系统,可以把逻辑分区分成两种:静态Lpar和动态Lpar.静态Lpar是指系统资源(CPU.内存和I/O等)在不同的分区之间移动的时候需要重新启动所有影响到的Lpar,而动态Lpar则可以使用户在不同的分区之间灵活移动资源时不会影响到分区的正常运行,既不需要

Hive分区(静态分区+动态分区)

Hive分区的概念与传统关系型数据库分区不同. 传统数据库的分区方式:就oracle而言,分区独立存在于段里,里面存储真实的数据,在数据进行插入的时候自动分配分区. Hive的分区方式:由于Hive实际是存储在HDFS上的抽象,Hive的一个分区名对应一个目录名,子分区名就是子目录名,并不是一个实际字段. 所以可以这样理解,当我们在插入数据的时候指定分区,其实就是新建一个目录或者子目录,或者在原有的目录上添加数据文件. Hive分区的创建 Hive分区是在创建表的时候用Partitioned b

动态分区代码

管道程序 #include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <stdlib.h> int main(void) { int fds[2]; pid_t pid; if(pipe(fds) == -1) { perror("pipe error"); exit(1); } pid=fork(); if(pid == -1) { perror("

C++动态内存分配

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D实战核心技术详解>电子工业出版社等. CSDN视频网址:http://edu.csdn.net/lecturer/144 C / C ++中的动态内存分配是指程序员手动执行内存分配, 动态分配的内存分配给堆,非静态和局部变量获取在Stack上分配的内存.详情查看上篇博文:C程序的内存布局. 什么是应用程序? 动态分配的

【C语言天天练(九)】动态内存分配

引言:数组的元素存储于内存中连续的位置上.当一个数组被声明时.它所须要的内存在编译时就被分配. 可是,我们能够使用动态内存分配在执行时为它分配内存. 一块内存的生命周期能够分为四个阶段:分配.初始化.使用.释放. 内存的分配一般使用C函数库里的malloc函数(原型:void *malloc(size_t size)). 关于malloc函数应该注意一下几点: 1.malloc的參数就是须要分配的内存的字节数. 2.malloc所分配的是一块连续的内存. 3.分配成功.则返回指向分配内存起始地址

SQLite剖析之动态内存分配

SQLite通过动态内存分配来获取各种对象(例如数据库连接和SQL预处理语句)所需内存.建立数据库文件的内存Cache.以及保存查询结果.我们做了很多努力来让SQLite的动态内存分配子系统可靠.可预测.健壮并且高效.本文概述SQLite的动态内存分配,软件开发人员在使用SQLite时可以据此获得最佳性能. 1.特性    SQLite内核和它的内存分配子系统提供以下特性:    (1)对内存分配失败的健壮处理.如果一个内存分配请求失败(即malloc()或realloc()返回NULL),SQ