您的位置:1010cc时时彩经典版 > 操作系统 > 操作系统实验,主存空间的分配和回收模拟

操作系统实验,主存空间的分配和回收模拟

发布时间:2019-12-01 17:52编辑:操作系统浏览(172)

    动态分区存储管理方式主存的分配与回收

    16网络工程二班 孙书魁

      实验四、主存空间的分配和回收模拟

      上篇博客介绍了处理机调度的相关知识——我的操作系统复习——处理机调度,本篇开始讲跟处理机打交道最多的计算机部件——存储器。存储器包括常说的内存和外存。存储器管理,一般指的是内存管理。外存也属于存储器,不过应该算作文件管理。

    1、可把存储器分为:寄存器、、主存储器和高速缓冲存储器、辅助存储器(包括磁带、软盘、硬盘、光盘等)三个层次。

    实验:动态分区式存储管理

     

                    13物联网工程    刘烨   201306104146

    一、存储器层次分类

      存储器按存储层次分可以分为三类,分别是寄存器、主存、辅存。寄存器位于CPU内,主存又称内存,辅存即硬盘。仔细划分的话,主存还可以分为高速缓存、主存、磁盘缓存。如下图所示,层次越往上,存储介质访问速度越快,价格越贵、相对存储容量也越贵。寄存器和主存这里大概说一说,辅存(外存)就留到文件系统的时候再说。

      1010cc时时彩经典版 1

     

    实验内容:

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

    目的:

               1,了解动态分区分配中,使用的数据结构和算法

              2,深入了解动态分区存储管理方式,主存分配与回收的实现

              3,进一步加深动态分区存储管理方式及其实现过程的了解

    一、 实验目的

      (1)寄存器

      寄存器位于CPU内,是CPU的组成部分。它是计算机系统内CPU访问速度最快的存储部件,完全能与CPU协调工作。不过价格太贵,只能做得很小。寄存器是用来存放系统最常访问的数据,如,指令寄存器用来存放从内存读到的正在执行的指令,程序计数器存放下一条指令所在单元的地址。其本质就是用来存放供CPU最频繁访问的一批数据。寄存器就是为了解决CPU访问主存速度过慢的问题。通常,CPU从主存读取数据,放入寄存器内,以便频繁访问。

    2、寄存器是计算机系统中价格最昂贵的寄存器。它的存取速度最快,但容量小,一般每个寄存器只能存储一个字长的信息,故只用来存放临时的工作数据和控制信息。常用的寄存器有:(1)指令寄存器:用于存放当前从主存储器中读出的指令;

    实验提示

    由于是实验,没有真正的内存分配。所以在实验中首先应建立一张空闲区表,初始状态只有一个空闲登记项和一张所有状态都为“空”的已分配区表。假定内存空间110KB,OS占用10KB,其余为空闲区。然后可以选择进行内存分配或回收:若是分配,要求输入作业名和所需内存空间大小;若是回收,输入回收作业的作业名。程序循环进行内存分配和回收,直到用户选择退出系统。在每次作业提交及作业结束时显示两张表的内容,以检查内存的分配和回收是否正确。

    具体实现:

                确定主存分配表,然后采用最佳适应算法,完成完成主存分配和回收,最后编写主函数,进行主函数进行测试。

        为了合理地分配和使用这些存储空间,当用户提出申请主存储器空间时,存储管理必须根据申请者的要求,按一定的策略分析主存空间和使用情况,找出足够的空闲区域给申请者。当作业撤离归还主存资源时,则存储管理要收回占用的主存空间。主存的分配和回收的实现是与主存储器的管理方式有关的,通过本实验帮助我们理解在不同的存储管理方式下应怎样实现主存空间的分配和回收。

      (2)主存

      主存即内存。CPU可以通过指令直接存取主存中的数据,所以CPU对主存的访问速度也很快,不过这个速度也远低于CPU的执行速度。为了解决这个问题,引入了寄存器和高速缓存。高速缓存是什么?高速缓存也是属于内存,不过它与通常的主存的实现形式不同,它一般是由静态存储芯片(SRAM)组成,访问速度比主存高得多, 接近于CPU的速度。而主存通常使用动态MOS随机读写存储器DRAM组成,速度比SRAM快得多。高速缓存的作用就是存放主存中一些经常被访问的信息。磁盘缓存的本质就是主存划分的一个小区域,为了减少CPU透过I/O读取磁盘机的次数,提升磁盘I/O的效率,用一块区域来储存存取较频繁的磁盘内容。

     

       (2)通用寄存器:用于存放当前参加运行的操作数、运算结果等;

    用C语言编程实现:

    #include<stdio.h>#include<malloc.h>typedef struct storage{    int name;    int size;    int startaddress;    int stuta;//0表示空闲;1表示已分配    storage* next;    storage* front;}storage;//初始化void initialize(storage *s,int name){    s->name=name;    s->size=0;    s->startaddress=0;    s->stuta=0;    s->front=NULL;    s->next=NULL;}//判断是否可以分配0表示不能分配,1表示可以分配int IFallocation(storage *s,int Size){    storage *p;    while (s!=NULL)    {        p=s->next;        if(s->stuta==0 && s->size>Size)//空闲而且存在 够分的情况        {            return 1;        }        s=p;    }    printf("不允许分配n");    return 0;}//分配void allocation(storage* head,int name,int size){    //找最佳位置        //创建两个指针 一个扫描移动 一个记录最佳        //假设头指针就是最佳插入位置        //扫描 先看是不是空闲区  在看能不能分配  在看是不是最佳位置    storage *h,*p,*great;    h=head;    while{        p=h->next;        if(h->stuta==0)        {            great=h;            if(h->size>size)            {                if(h->size<great->size)                {                    great=h;                }            }        }        h=p;    }    //创建节点    p=malloc(sizeof;    initialize(p,great->name);    //修改数据    p->size=great->size-size;    p->startaddress=great->startaddress size;    great->size=size;    great->stuta=1;    //链接        //分为尾部为空的链接  和不为空的链接    if(great->next==NULL)    {        p->next=great->next;        p->front=great;        great->next=p;    }    else    {        p->next=great->next;        p->next->front=p;        great->next=p;        p->front=great;    }    printf("分配成功n");}//回收有四种情况//return 0则是找不到name return 1是成功int recycle(storage** head,int name){    //根据名字找到节点    storage *h,*p;    h=*head;    while (h!=NULL)    {        p=h->next;        if(h->name==name && h->stuta==1)        {            break;        }        h=p;    }    if(h==NULL)    {        printf("任务不存在n");        return 0;    }    //根据几点前后 区块 和区块 空闲情况回收        //如果不用合并只需要把状态设置为0        //如果下面有节点而且空闲则合并        //如果上面有几点而且空闲则合并    h->stuta=0;    if(h->next && h->next->stuta==0)    {        //修改值        h->next->size =h->size;        h->next->startaddress=h->startaddress;        //链接        if(h==*head)        {            *head=h->next;            h->next->front=NULL;        }        else{            h->next->front=h->front;            h->front->next=h->next;        }        //释放        p=h->next;        free;        h=p;    }    if(h->front &&h->front->stuta==0)    {        //修改值        h->front->size =h->size;        //链接        if(h->next)        {            h->next->front=h->front;            h->front->next=h->next;        }        else{            h->front->next=NULL;        }        //释放        free;    }    printf("回收成功n");    return 1;}//显示分配情况void display(storage*head){    storage*p;    while     {        p=head->next;        printf("片号%d,大小%d,状态%d,起始位置%dn",head->name,head->size,head->stuta,head->startaddress);        head=p;    }}void Menu(){    printf("按1添加任务,按2删除任务,按0退出n");}//退出void Exit(storage*head){    printf("1n");    storage*p,*h;    h=head;    while     {        p=h->next;        free;        h=p;    }}int main(){    int menu;    storage*head;    head=malloc(sizeof;        initialize(head,1);    head->size=100;    Menu();    scanf("%d",&menu);    while     {        display;        if(menu==1)        {            int name,size;            printf("请输入任务号");            scanf("%d",&name);            printf("请输入任务大小");            scanf("%d",&size);            if(IFallocation(head,size))                {                allocation(head,name,size);                }        }        if(menu==2)        {            int name;            printf("请输入要删除的任务号");            scanf("%d",&name);            recycle(&head,name);        }        printf("本操作结束请再次选择操作");        scanf("%d",&menu);        Menu();    }    Exit;    return 0;}
    

    欢迎批评指正。

    具体实现:

                主存分配之前的之态,主存分配过程中的状态,回收后的状态

     

      1 #include <stdio.h>   
      2 #include <string.h>
      3 #define MAX 600  //设置总内存大小为512k
      4 
      5 struct partition {
      6     char    pn[10];//分区名字
      7     int     begin;//起始地址
      8     int     size;//分区大小 
      9     int     end;//结束地址
     10     char    status;//分区状态
     11  };
     12  struct partition    part[MAX];
     13  int    p = 0; //标记上次扫描结束处 
     14  
     15  void Init()//初始化分区地址、大小以及状态
     16 {
     17     int i;
     18     for ( i = 0; i < MAX; i   )
     19          part[i].status = '-';
     20      strcpy( part[0].pn, "SYSTEM" );
     21      part[0].begin    = 0;
     22      part[0].size    = 100;
     23      part[0].status    = 'u';
     24   
     25      strcpy( part[1].pn, "-----" );
     26      part[1].begin    = 100;
     27      part[1].size    = 100;
     28      part[1].status    = 'f';
     29      strcpy( part[2].pn, "A" );
     30      part[2].begin    = 200;
     31      part[2].size    = 50;
     32      part[2].status    = 'u';
     33      strcpy( part[3].pn, "-----" );
     34      part[3].begin    = 250;
     35      part[3].size    = 50;
     36      part[3].status    = 'f';
     37      strcpy( part[4].pn, "B" );
     38      part[4].begin    = 300;
     39      part[4].size    = 100;
     40      part[4].status    = 'u';
     41      strcpy( part[5].pn, "-----" );
     42      part[5].begin    = 400;
     43      part[5].size    = 200;
     44      part[5].status    = 'f';
     45      for ( i = 0; i < MAX; i   )
     46          part[i].end = part[i].begin   part[i].size-1;
     47  }
     48   
     49 
     50   void Output( int i ) //以行的形式输出结构体的数据
     51  {
     52      printf( "t%s", part[i].pn );
     53      printf( "t%d", part[i].begin );
     54      printf( "t%d", part[i].size );
     55      printf( "t%d", part[i].end );
     56      printf( "t%c", part[i].status );
     57  }
     58  
     59 
     60  void display() //显示分区 
     61  {
     62      int    i;
     63      int    n; //用n来记录分区的个数
     64      printf("n");
     65      printf( "n        已分配分区表Used:" );
     66      printf( "ntNo.tpronametbegintsizetendtstatus" );
     67      printf("n");
     68      n = 1;
     69      for ( i = 0; i < MAX; i   )
     70      {
     71          if ( part[i].status == '-' )
     72              break;
     73          if ( part[i].status == 'u' )
     74          {
     75              printf( "ntNo.%d", n );
     76              Output( i );
     77              n  ;// 记录已分配使用的分区个数
     78          }
     79      }
     80      printf("n");
     81      printf( "n        空闲分区表Free:" );
     82      printf( "ntNo.tpronametbegintsizetendtstatus" );
     83      printf("n");
     84      n = 1;
     85      for ( i = 0; i < MAX; i   )
     86      {
     87          if ( part[i].status == '-' )
     88               break;
     89         if ( part[i].status == 'f' )
     90           {
     91               printf( "ntNo.%d", n );
     92            Output( i );
     93               n  ;  //记录空闲分区的个数
     94           }
     95     }
     96     // printf( "n" );
     97      printf("n");
     98      printf( "n        内存使用情况,按起始址增长的排:" );
     99      //printf( "n        printf sorted by address:" );
    100      printf( "ntNo.tpronametbegintsizetendtstatus" );
    101      printf("n");
    102      n = 1;
    103      for ( i = 0; i < MAX; i   )
    104      {
    105          if ( part[i].status == '-' )
    106              break;
    107          printf( "ntNo.%d", n );
    108          Output( i );
    109         n  ;//记录已分配分区以及空闲分区之和的总个数
    110     }
    111      getch();
    112  }
    113  
    114  void Fit( int a, char workName[], int workSize ) //新作业把一个分区分配成两个分区:已使用分区和空闲分区 
    115  {
    116      int i;
    117      for ( i = MAX; i > a   1; i-- )
    118      {
    119         //通过逆向遍历,把在a地址后的所有分区往后退一个分区,目的在于增加一个分区
    120          if ( part[i - 1].status == '-' )
    121              continue;
    122          part[i]=part[i-1];
    123     }
    124      strcpy( part[a   1].pn, "-----" );
    125      part[a   1].begin    = part[a].begin   workSize;
    126      part[a   1].size    = part[a].size - workSize;
    127      part[a   1].end        = part[a].end-1;
    128      part[a   1].status    = 'f';
    129     strcpy( part[a].pn, workName );
    130      part[a].size    = workSize;
    131      part[a].end    = part[a].begin   part[a].size-1;
    132      part[a].status    = 'u';
    133  }
    134  void fenpei() // 分配 
    135  {
    136      int    i;
    137      int    a;
    138     int    workSize;
    139      char    workName[10];
    140      int    pFree;
    141      printf( "n请输入作业名称:" );
    142      scanf( "%s", &workName );
    143      for(i=0;i<MAX;i  )
    144     {
    145          if(!strcmp(part[i].pn,workName))//判断作业名称是否已经存在
    146          {
    147              printf("n作业已经存在,不必再次分配!n");
    148             return;
    149          }
    150      }
    151      printf( "请输入作业大小(k):" );
    152      scanf( "%d", &workSize );
    153      for ( i = 0; i < MAX; i   )//通过循环在空闲区找是否有适合区间存储作业
    154      {
    155          if ( part[i].status == 'f' && part[i].size >= workSize )
    156          {
    157              pFree = i;
    158              break;
    159          }
    160     }
    161     if ( i == MAX )
    162     {
    163          printf( "n该作业大小超出最大可分配空间" );
    164          getch();
    165          return;
    166      }
    167      
    168          for ( i = 0; i < MAX; i   )//最佳适应算法
    169             if ( part[i].status == 'f' && part[i].size >= workSize )
    170                  if ( part[pFree].size > part[i].size )
    171                      pFree = i;//通过遍历所有区间,每次都找到最小空闲分区进行分配
    172          Fit( pFree, workName, workSize );
    173     printf( "n分配成功!" );
    174     getch();
    175  }
    176  void hebing() //合并连续的空闲分区 
    177  {
    178     int i = 0;
    179     while ( i != MAX - 1 )
    180     {
    181         for ( i = 0; i < MAX - 1; i   )
    182         {
    183             if ( part[i].status == 'f' )
    184                  if ( part[i   1].status == 'f' )
    185                 {
    186                      part[i].size    = part[i].size   part[i   1].size;
    187                      part[i].end    = part[i].begin   part[i].size-1;
    188                      i  ;
    189                      for ( i; i < MAX - 1; i   )
    190                     {
    191                         if ( part[i   1].status == '-' )
    192                         {
    193                             part[i].status = '-';
    194                             break;
    195   
    196                         }
    197                         
    198                         part[i]=part[i 1];
    199                     }
    200                      part[MAX - 1].status = '-';
    201                      break;
    202                  }
    203         }
    204     }
    205  }
    206  
    207  
    208  void huishou() // 回收分区 
    209  {
    210      int    i;
    211      int    number;
    212      int    n=0;
    213      printf( "n请输入回收的分区号:" );
    214      scanf( "%d", &number );
    215      if ( number == 1 )
    216      {
    217          printf( "n系统分区无法回收" );
    218          return;
    219      }
    220      for ( i = 0; i < MAX; i   )//通过循环查找要回收的已使用分区区号
    221      {
    222          if ( part[i].status == 'u' )
    223          {
    224              n  ;
    225              if ( n == number )
    226             {
    227                  strcpy( part[i].pn, "-----" );
    228                  part[i].status = 'f';
    229             }
    230          }
    231      }
    232      if ( i == MAX - 1 )
    233      {
    234          printf( "n找不到分区" );
    235          return;
    236      }
    237      hebing();//合并连续的空闲分区
    238      printf( "n回收成功!" );
    239      getch();
    240  }
    241  
    242  
    243  void main()
    244 {
    245      int selection;
    246      Init();
    247      printf( "初始化完成,设内存容量%dk", MAX );
    248      printf( "n系统文件从低址存储,占%dk", part[0].size );
    249      while ( 1 )
    250      {
    251          printf( "n----------选择----------" );
    252          printf( "n|  0、退出系统         |" );
    253          printf( "n|  1、显示分区         |" );
    254          printf( "n|  2、分配分区         |" );
    255          printf( "n|  3、回收分区         |" );
    256          printf( "n------------------------");
    257         printf( "n请选择 > " );
    258          while ( 1 )
    259          {
    260              scanf( "%d", &selection );
    261              if ( selection == 0 ||selection == 1 || selection == 2 || selection == 3 )
    262                  break;
    263              printf( "输入错误,请重新输入:" );
    264          }
    265          switch ( selection )
    266          {
    267            case 0:
    268            exit(0); //退出系统
    269              break;
    270          case 1:
    271              display(); //显示分区
    272              break;
    273         case 2:
    274              fenpei(); //分配作业
    275              break;
    276          case 3:
    277              huishou();  //回收分区
    278              break;
    279          default:
    280              break;
    281          }
    282      }
    283  }
    

     

    1010cc时时彩经典版 2

    1010cc时时彩经典版 3

    1010cc时时彩经典版 4

    1010cc时时彩经典版 5

    二、 实验内容和要求

    二、程序的装入和链接

      程序装入就是把程序和数据放入内存。程序也不是一开始就有的。这里指的程序是最终在内存中运行的模块——装入模块。那么一份源代码是怎么变成可运行的程序的呢?学过C、C 的同学对这个最了解。首先是把源代码用编译程序编译成目标模块,每一份源代码文件对应一个目标模块。然后用链接程序将目标模块和程序所需要的库函数链接起来,变成一个可运行的程序。这个可运行的程序,实质是编译链接后的机器指令,CPU可以运行这些机器指令。程序运行时,装入模块将其放入内存并运行。其中,将这些机器指令何其指向的资源装入内存有3种方式:

       (3)控制寄存器:用于存放控制信息以保证程序的正确执行和系统的安全。

    1)实现特定的内存分配算法

      (1)装入:

        1)绝对装入方式(Absolute Loading Mode)

      程序中使用的地址是直接指向内存的绝对地址,那么在把程序装入内存的时候,不需要对程序地址做任何修改,这种装入方式就叫做绝对装入方式。绝对装入方式只能将程序装入到内存中指定的位置,它只适合单道处理环境,这样就不会有内存冲突了。

        2)可重定位装入方式(Relocation Loading Mode)

      可重定位装入方式指的是,将程序装入内存的时候,将程序地址都相对于内存当前地址偏移。这时程序中的地址都是相对地址。值得注意的是,装入时对程序中指令和数据地址的修改过程叫做重定位。

        3)动态运行时装入方式(Dynamic Run-time Loading)

      如果程序在运行时位置需要改变,应该采用动态运行时装入方式。动态运行时装入方式指的是程序中的相对地址并不在装入时就转换成内存中的绝对地址,而是等到真正运行的时候才会转换。

      主存储器:存储容量较大,存取速度也很快。

    2)实现内存回收模拟

      (2)链接:

      与程序装入相对应的是程序的链接方式。程序的链接方式也有3种方式,分别是静态链接方式、装入时动态链接和运行时动态链接。分别对应的是程序链接时的3个时间。其中静态链接是程序的目标模块在装入之前就链接好,而装入时动态链接,顾名思义,就是目标模块实在装入内存的时候动态的进行链接,这种方式链接的程序的目标模块是分开存放的,若一个目标模块需要链接给其他多个模块是非常方便的。而在静态链接方式中要实现这个功能,需要其他多个模块都含有该模块的拷贝。

     

      高速缓冲存储器:存取速度快于主存储器,但造价要比主存储器高,因而存储容量不大。

    3)每种内存分配策略对应的碎片数统计

    三、内存分配方式——连续分配方式

      将内存分配给程序,最典型的方式就是将一个连续的内存空间分配给程序,这就是连续分配方式。这种分配方式细分可以分为单一连续分配、固定分区分配、动态分区分配和动态重定位分区分配。需要了解的是,前面的程序装入内存的过程就是典型的内存分配。就是说,内存的分配通常可能是动态,在程序运行过程中,通常伴随着动态的内存创建和内存回收,其中还涉及到很多缓存、优化之类的策略。在各种内存分配和回收的过程中,会产生很多空余碎片。内存分配就是要尽可能利用内存空间,避免内存浪费。

      辅助存储器:存储容量大,可长期存储,处理器不能直接读写,必须把信息读到主存储器中才能被访问。

    2.2  固定分区存储管理

      (1)单一连续分配

      这种分配方式就是简单的把内存分为系统区和用户区,系统区给操作系统用,用户区给用户用。这种分配方式非常简单,并未考虑多用户内存冲突和多任务内存冲突的情况,所以只适用于单用户、单任务的OS。值得注意的是,系统区通常是分配在内存的低址部分。

     

        假设内存容量为120KB,并且分别划分成8,16,32,64KB大小的块各一块。

      (2)固定分区分配

      这种分配方式就是将内存划分为若干固定大小的区域,区域的大小是事先划分好的,每个区域装入一道作业、程序,这样多任务内存冲突的问题就解决了。这种划分方法适用于多道批处理系统——多任务并发的情况。但是,由于每个分区大小固定,存储空间的浪费是必然的。

    3、由于操作系统自身必须占用主处理器的一部分存储空间,用来存放操作系统的程序、数据、管理信息(PCB)以及操作系统与硬件的接口信息(新、旧PSW)等,我们把这部分空间称为系统区;除系统区外的其余主存空间可用来存放用户的的程序和数据,称为用户区。存储管理是对主存储器中的用户区域进行管理,包括主存空间的分配与回收、主存空间的共享与保护、地址转换以及主存空间的扩充等工作。

    一个进程所需要的内存为0到100个KB。同时假设一个进程在运行过程中所需内存的大小不变。

      (3)动态分区分配

      这种分配方式就是根据进程的实际需要,动态的分配内存空间。这种分配方式有3个问题需要注意。1、需要有一种数据结构来描述空闲分区和已分配分区的情况。2、需要按照一定的分配算法从空闲分区中选择空间来分配。3、需要有合适的分区分配和内存回收操作:

        1)描述空闲分区的数据结构:

        有2种数据结构可以描述空闲分区的数据结构,分别是空闲分区表和空闲分区链。其中,分区表很容易理解,分区链指的是通过在空闲分区的首尾设置2个指向其他空闲分区的指针,形成一个空闲分区的链,用来记录空闲的分区。

        2)分区分配算法:

      • 首次适应算法(first fit):分区链以地址递增的次序链接;分配内存时,从链首开始,查找到一个大小能满足要求的空闲分区就停止。这个算法说白了就先分配内存的低址部分,再分配高址部分。
      • 循环首次适应算法(next fit):这个分配算法与首次适应算法的区别在于,它分配内存时,不是从链首开始查找,而是从上次找到的空闲分区的下一个分区开始查找。
      • 最佳适应算法(best fit): 分区链以从小到大的顺序链接;分配内存时,从链首开始,查找到一个能满足要求的空闲分区就停止。
      • 最坏适应算法(worst fit): 分区链以从大到小的顺序连接;与最佳适应算法相反,每次都挑最大的空闲区来分配。
      • 快速适应算法(quick fit): 将空闲区根据大小进行分类,每一种类别单独设立一个链表。同时,用一个管理索引表来管理这些链表。那么分配内存的时候只需要查询管理索引表就行了,无需遍历链表,速度非常快。缺点是,这个算法需要一直维护着链表和管理索引表,需要一定系统开销。

        3)内存分配和回收:

        在分配空闲分区的时候,值得注意的是,通常空闲分区会有一个“不可再分割的剩余分区大小”的属性,规定了,当空闲分区所剩属性小于它的时候,分区不允许再继续分割,分区也将从空闲分分区链表中移除。

        内存回收的时候,值得注意的是,若回收的内存区与某个空闲分区相邻接,那么需要将它们合并。否则,需要为回收区建立新的空闲分区。 

        4)伙伴系统:

        我们知道1G的内存有220个字节,有224个字。那么根据指数,最多分为24个空闲分区链表。假设一个应用程序申请2MB的内存,2MB即215个字的大小,这时候查找大小为215的空闲分区链表,若找不到,那么查找大小为216的空闲分区链表,若216的空闲分区链表存在,那么把它分成2个,一个分配给请求,另一个分配为215的空闲分区链表,若若216的空闲分区链表不存在,那么继续往后查找,以此类推。

     

    模拟五个进程到达请求分配与运行完回收情况,输出主存分配表。

      (4)可重定位分区分配

        由于程序、资源间会有很多碎片,浪费了内存空间,可重定位分区分配就是为了解决这个问题,它可以直接移动内存中的程序、资源,使内存变得紧凑,同时也不影响程序的正常运行。可重定位分区分配要求程序的装入方式是动态运行时装入方式。程序装入内存后,所有地址仍旧是相对地址,直到运行时才会转变为绝对地址。程序在寄存器中有一个重定位寄存器,用来存放程序在硬盘中的实际地址的首地址。那么将程序在内存中的绝对地址移动,只需要移动后,改变重定位寄存器的值即可。这我们经常用的“磁盘碎片清理”就是一样的效果。

    4、绝对地址:把主存空间的地址编号称为主存储器的绝对地址,与绝对地址对应的主存空间称为物理地址空间

    2.3  动态分区分配存储管理

      (5)对换

        对换是一个需要了解一下的概念。还记得前面我们讲进程调度的时候,有一个特殊的调度类型,叫做中级调度。中级调度就是让暂时不能运行的进程挂起,释放内存资源,并把它们调到外存上去等待,这种操作,在内存看来,就叫对换。以进程为单位的对换叫进程对换。对换的情况下,外存中必须分配一定的区域用来存放对换的内存资源,叫做对换区。这个对换区本质是虚拟存储器,这个后面会讲。

     

     

        采用连续分配方式之动态分区分配存储管理,使用首次适应算法、下次适应算法、最佳适应算法和最坏适应算法4种算法完成设计(任选两种算法)。

    四、内存分配方式——离散分配方式

      连续的分配方式会产生很多碎片。离散的分配方式是将进程、资源装入不相邻的多个分区的内存分配方式。这种分配方式按照分配的单位是“页”还是“段”,分为分页存储管理、分段存储管理以及段页式存储管理。

    5、逻辑地址:为了方便用户,每个用户都可以认为自己作业的程序和数据存放在一组从“0”地址开始的连续空间中。用户程序中使用的地址称为逻辑地址,与逻辑地址对应的存储空间称为逻辑地址空间。

    (1)在程序运行过程,由用户指定申请与释放。

     (1)分页存储管理

      分页存储管理是根据程序作业中的“页”为单位离散分配内存的管理。

      1)页面(页)。

      分页存储管理的内存分配单位是页。什么是页?页就是一段指定大小的内存块。分页存储管理就是按照一定大小把进程的逻辑地址空间分成若干份,每一份就是一个页,把他们编号。然后按照页的大小把内存分为若干物理块,并编号。页的大小通常是512B到8KB之间。

      2)页表。

      每一个进程都有一张页表,用来记录进程的页号对应的物理块号。进程运行时,CPU会根据程序的逻辑地址和页号大小从页表找到实际的物理块和实际的物理地址。页表是经常被CPU访问的,CPU经常需要先访问页表再根据页表的地址访问内存,所以一般会设置一个“联想寄存器”,又称“块表”,存放最近频繁访问的页表。如果系统的内存特别大,页表中页面的逻辑地址就会特别大,就需要用多层的页表结构来对应物理块号。这种情况下,CPU会根据程序的逻辑地址和页面大小从多层的外部页表找到指定的页表,再从页表中找到实际的物理块和物理地址。

     

    (2)设计一个已占用分区表,以保存某时刻主存空间占用情况。

    (2)分段存储管理

      分段存储管理是根据程序作业中的“段”为单位离散分配内存的管理。

      1)段。

    1010cc时时彩经典版,  段指的是程序、作业中的一组逻辑信息。例如:全局变量可以设为一个段;每个函数的局部变量可以设为一个段;每个函数的代码部分可以设置为一个段。这样做有什么意义呢?相当于将程序中的这种逻辑信息根据大小离散的存储在内存中,而对于逻辑信息本身而言,他们在内存中是连续的,不会被分割的,这样有利于对逻辑信息的处理,如信息共享、信息保护等。

      2)段表。

      与页表类似的,每个进程都有一张段表,用来记录程序中每个段对应的物理位置。段表中每个记录都记录了段的物理地址和段的长度。同样,由于段表经常需要被访问,有些系统会把段表放在寄存器中。

      (PS:值得注意的是,运行时动态链接要求内存使用分段存储管理。)

    6、把逻辑地址转换成绝对地址的工作称为重定位或地址转换。重定位的方式可以有静态重定位和动态重定位两种。

    (3)设计一个空闲分区表,以保存某时刻主存空间剩余情况。

    (3)段页式存储管理

      段页式存储管理是根据“段”为单位,再将“段”细分为“页”,以这个为单位离散分配内存的管理。原理与分页、分段存储管理类似。  

     

     

    (4)用两个表的变化情况,反应各进程所需内存的申请与释放情况。

    五、虚拟存储器管理

       对于内存的连续分配方式,上文有一个“对换”的概念,就是将暂时不用的内存资源从内存中移出,放到外存的对换区中。当需要该内存资源的时候,需要及时能够把该内存资源从外存中移入内存。这里的对换区其实就是虚拟存储器。讲到虚拟存储器有需要了解一下程序执行的局部性原理,总结下来就是:

    • 程序的执行过程中,大部分的指令是执行一次或很少执行的,CPU主要是在执行一小部分指令。
    • 程序的执行过程中,大部分资源是很少被访问的。

      所以,程序一次性装入内存,而实际上大部分内存资源是被浪费的。基于这种情况,没必要把所有资源都一次性装入内存。仅需要将程序当前需要的运行的段(页)装入内存即可。如果程序运行时访问到内存中不存在的段(页),这种现象叫“缺段”(却页),这时候需要根据一定算法从外存的虚拟存储区将缺失的资源立即装入内存。

      这里有一个补充知识,见

      style="line-height: 1.5; background-color: initial;">  至于页表的问题是这样的,在系统初始化时,是直接对物理内存进行访问的,不经过页表,这是的工作模式叫实模式,等页表在内存中建立好了,再切换的保护模式,在保护模式就出现了虚拟地址向物理地址转译的过程了。 

    *  *CPU有两种工作模式,一个是实模式,就是直接访问物理内存,不分页的。另一个是保护模式,就是分页的,而且存在虚拟地址。保护模式下又有特权模式和用户模式两种。关系是这样子的。

      我给你讲,只要发生缺页中断,就会陷入内核,只是就进入了特权模式,控制权交给了操作系统,这一系列过程都是硬件完成的。至于换页使软件完成的,就是操作系统负责调页。MMU只是负责把虚拟地址转译成物理地址,他只能做这个,纯硬件实现的。操作系统有调页算法,就是在空闲的页找出来一个,把需要的内容从磁盘读出来,放到内存里,然后让进程重新运行那条指令。一切继续,就像没有缺页过一样。如果没有空闲的,就把最不经常使用的一页替换掉。

     

     参考:《计算机操作系统(汤子瀛)》

     

    7、静态重定位:在装入一个作业时,把作业中的指令地址和数据地址全部转换成绝对地址。由于地址转换工作是在作业执行前集中一次完成的,所以在作业执行过程中就无须再进行地址转换工作,这种地址转换方式称为静态重定位。

     

     

    1. 源程序名:实验二 1.c

    8、动态重定位:需要由软件和硬件相互配合来实现,在作业执行过程中,由硬件的地址转换机构动态的进行地址转换,在执行指令时只要把逻辑地址与基址寄存器的值相加就可得到绝对地址,这种定位方式是在执行指令过程中进行的,所以称为动态重定位。

    可执行程序名:1.exe

     

    1. 主要程序段及其解释:

    9、单用户连续存储管理是一种最简单的存储管理方式,在这种管理方式下,操作系统占了一部分主存空间,其余剩下的主存空间都分配给一个作业使用,即在任何时刻主存储器中最多只有一个作业,因此不必考虑作业在主存储器中的移动问题,于是可采用静态重定位方式进行地址转换,即在作业被装入到主存储器时一次性的完成地址转换。

     

     

    #include"stdio.h"

    10、处理器在执行指令时要检查其绝对地址知否≥界限地址,且≤最大地址。若绝对地址在规定的范围内,则可执行,否则产生一个“地址越界”中断事件,由操作系统进行处理,以达到存储保护的目的。

    #include"stdlib.h"

     

    #define n 10 

    11、固定分区存储管理是把主存储中可分配的用户区域预先划分成若干个连续区,每一个连续区称为一个分区。一旦划分好后,主存储器中分区的个数就固定了。每个分区的大小可以相同,也可以不同,但每个分区的大小不变。每个分区可以装入一个作业,所以当有多个分区时,就可同时在每个分区中装入一个作业,但不允许多个作业同时存放在同一个分区中。这种管理方式称为固定分区存储管理

    #define m 10

     

    #define minisize 100

    12、固定分区存储管理主存空间的分配与回收:设置“分区分配表”用来说明各分区的分配和使用情况。表中指出各分区的起始地址和长度,并为每个分区设置一个标志位。当标志位为“0”时,表示分区空闲,当标志位非“0”时,表示分区已被占用。

    struct{

     

     float address; /*已分分区起始地址*/

    13、固定分区存储管理地址转换:由于固定分区管理方式是预先把主存划分成若干个区,每个区只能用来装入一个作业,因此作业在执行过程中是不会改变存放区域的,于是可以采用静态重定位的方式把作业装入到所分配的分区中去。

        float length; /*已分分区长度,单位为字节*/

     

        int flag; 

    14、固定分区存储管理存储保护:设置下限寄存器和上限寄存器,当一个已经被装入主存储器的作业得到处理器运行时,进程调度应记录当前运行作业所在的分区号,且把该分区的下限地址和上线地址分别送入下限寄存器和上限寄存器中处理器执行改作业的指令时必须核对:下限地址≦绝对地址<上限地址。如果不等式不成立,则为防止破坏其他分区中的信息,硬件产生“地址越界”中断事件,停止执行该指令,已达到存储保护的目的。

    }used_table[n]; /*已分配区表*/

     

     

    15、提高固定分区存储管理的主存空间的利用率:(1)根据经常出现的作业的大小和数量来划分分区,尽可能使各个分区被充分利用;(2)划分分区时按分区的大小顺序排列,低地址部分是较小的分区,高地址部分是较大的分区;(3)按作业对主存空间的需求量排成多个作业队列。

    struct{

    注:采用多个作业队列的固定分区法能有效地防止小作业进入大分区,从而减少闲置的空间量。但是划分分区时应特别注意可能出现的作业大小和作业出现的频率,如果划分不得当,会造成某个作业队列经常是空队列,反而影响分区的使用效率。

     float address; /*空闲区起始地址*/

     

     float length; /*空闲区长度,单位为字节*/

    16、可变分区存储管理不是预先把主存储器中的用户区域划分成分区,而是在作业要求装入主存储器时,根据作业需要的主存空间的大小和当时主存空间使用情况来决定是否为作业分配一个分区。因此分区的长度不是预先固定的,而是按作业的实际需求来划分的;分区的个数也不是预先确定的,而是由装入的作业数决定的。

     int flag; 

     

    }free_table[m]; /*空闲区表*/

    17、可变分区存储管理主存空间的分配:(1)当有作业要装入主存储器时,根据作业对主存空间的需要量,从空闲区中划出一个与作业长度一致的分区来装入作业,剩余部分仍为空闲区;(2)当空闲区能满足需求时,作业可装入,当作业对主存空间的需要量超过空闲区长度时,则作业暂时不能装入。

     

     

    void main( )

    18、可变分区存储管理主存空间的回收:(1)当作业结束时,它的占用分区被收回。这个空闲区又可以根据新作业的大小重新用于分配,所以主存中的已占分区和空闲区的数目和大小都是在变化的;(2)可以用“空闲区表”来记录和管理,记录空闲区的起始地址和长度。

    {

     

    本文由1010cc时时彩经典版发布于操作系统,转载请注明出处:操作系统实验,主存空间的分配和回收模拟

    关键词: