操作系统之存储管理——FIFO算法和LRU算法

简介: 存储管理的主要功能之一是合理地分配空间。请求页式管理是一种常用的虚拟存储管理技术。本实验的目的是通过请求页式管理中页面置换算法模拟设计,了解虚拟存储技术的特点,掌握请求页式存储管理的页面置换算法。

要求



一、实验目的


存储管理的主要功能之一是合理地分配空间。请求页式管理是一种常用的虚拟存储管理技术。

本实验的目的是通过请求页式管理中页面置换算法模拟设计,了解虚拟存储技术的特点,掌握请求页式存储管理的页面置换算法。


二、实验内容


(1)通过计算不同算法的命中率比较算法的优劣。同时也考虑了用户内存容量对命中率的影响。


页面失效次数为每次访问相应指令时,该指令所对应的页不在内存中的次数。

在本实验中,假定页面大小为1k,用户虚存容量为32k,用户内存容量为4页到32页。


(2)produce_addstream通过随机数产生一个指令序列,共320条指令。


A、指令的地址按下述原则生成:


1)50%的指令是顺序执行的

2)25%的指令是均匀分布在前地址部分

3)25%的指令是均匀分布在后地址部分


B、具体的实施方法是:


1)在[0,319]的指令地址之间随机选取一起点m;

2)顺序执行一条指令,即执行地址为m 1的指令;

3)在前地址[0,m 1]中随机选取一条指令并执行,该指令的地址为m’;

4)顺序执行一条指令,地址为m’ 1的指令

5)在后地址[m’ 2,319]中随机选取一条指令并执行;

6)重复上述步骤1)~5),直到执行320次指令


C、将指令序列变换称为页地址流


在用户虚存中,按每k存放10条指令排列虚存地址,即320条指令在虚存中的存放方式为:

第0条~第9条指令为第0页(对应虚存地址为[0,9]);

第10条~第19条指令为第1页(对应虚存地址为[10,19]);

。。。。。。

第310条~第319条指令为第31页(对应虚存地址为[310,319]);


按以上方式,用户指令可组成32页。

(3)计算并输出下属算法在不同内存容量下的命中率。

1)先进先出的算法(FIFO);

2)最近最少使用算法(LRU);

3)最佳淘汰算法(OPT);

4)最少访问页面算法(LFR);

其中3)和4)为选择内容


三、系统框图



20181204155745451.png


一、运行结果


a、运行程序:终端先显示:

Start memory management.

Producing address flow, wait for while, please.


b、地址流、地址页号流生成后,终端显示:


There are algorithms in the program

1、Optimization algorithm

2、Least recently used algorithm

3、First in first out algorithm

4、Least frequently used algorithm

Select an algorithm number, please.


用户输入适当淘汰算法的号码,并按回车,若是第一次选择,输出相应的地址页号流。然后输出该算法分别计算的用户内存从2k ~ 32k时的命中率,若输入的号码不再1~4中,则显示:


there is not the algorithm in the program,并重复b。


c、输出结果后,终端显示 “do you try again with anther algorithm(y/n)”。若键入y则重复b,否则

结束。(一般讲四种算法都用过后结束,以便比较)。


二、运行结果讨论


1、比较各种算法的命中率

2、分析当用户内存容量增加是对命中率的影响


分析



上面就是实验要求,因为时间关系,只写了fifo和lru两种,但是这两个会了,剩下的了解算法原理就很容易实现。


对于两种算法的理解和实现为:


先进先出算法算法(Fifo):


这个算法原理没有算法,就是先进先出。对于这个结构最好采用的就算队列了,对于java而言,我用的是list集合,每次添加数据的时候添加到第0位置,而如果移除的时候移除末尾的页数。在这个过程中,每执行一条指令的时候,如果这个指令对应的地址(指令/10)在list中,那么就命中,否则就是缺页,需要移除尾,在0位置添加元素。


举个例子,页面内存为3,(只能存三页),要执行指令地址页面对应为:4 2 3 4 1 2 1 5 6 3


那么流程顺序为:(4)缺页—>(2,4)缺页—>(3,2,4)缺页—>4命中—>(1,3,2)缺页4被置换—>2命中—>1命中—>(5,1,3)缺页2被替换—>(6,5,1)缺页2被替换—>(3,6,5)缺页1被替换。


最近最少使用算法(LRU):


这个算法跟fifo其实还是挺像的,但是有一点区别,最近最少使用。也就是说在一个正常序列的时候如果命中的化,就会把这个地址的页号移动到首位(或者链表首位)。而如果缺页的时候,要把这个链表的末尾位置移除,因为末尾的元素是最近用的最少的(很久前才有的)。对于数据结构,我依然选择Arraylist。每次遇到指令地址的时候我只需要特殊判断下就可以了。我只为了实验的目的,没有考虑性能,因为检查是否存在地址的时候我用了list.contains()方法,这是线性查询复杂度O(n),如果数据量大可以list map组合使用,将查询降低到O(logn).还可以开一个boolean数组标记让查询复杂度降低到O(1),(但是这样的化增大空间),还好数据量不大,影响不大。


如果页面内存为3,(只能存三页),要执行指令地址页面对应为:4 2 3 4 1 2 1 5 6 3

那么流程顺序为(4)—>(2,4)—>(3,2,4)—>(4,3,2)命中—>(1,4,3)缺页2被替换—>(2,1,4)缺页—>(1,2,4)命中—>(5,1,2)缺页—>(6,5,1)缺页—>(3,6,5)缺页


代码



大致流程就是这样,有一点区别。

下面附上我的ac代码。有的解释会在注释中:


package 实验;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.Scanner;
public class storemanage {
  static int mingzhong;
  static int lenyeliu=320;
  static int errortime;//失效次数
  static int random[]=new int[330];//随机指令
  static Scanner sc=new  Scanner(System.in);
  static void init()//初始随机数
  {
    int index=0;
    while(index<320)
    {
      int m=(int) (Math.random()*320);
      random[index++]=m;//顺序指令
      int m1=(int) (Math.random()*(m+1));
      if(m1==320) {continue;}//跳出问本次
      random[index++]=m1;//前地址部分 最大317
      if(m1+1==320) {continue;}//跳出问本次
      random[index++]=m1+1;//顺序指令
      int m2=(int) (Math.random()*(319-(m1+2))+m1+2);
      if(m2==320) {continue;}//跳出问本次
      random[index++]=m2;//后地址
      if(index>320)
      {
        break;
      }     
    }
  }
  private static void  lur()
  {
    for(int memory=2;memory<=32;memory++)
    {
      mingzhong=0;errortime=0;
        Queue<Integer>q1=new ArrayDeque<>();
        int index=0;
        while(index<320)
        {
          if(q1.size()<memory)
          {
            if(q1.contains(random[index]/10))
            {
             q1.remove(random[index]/10);
             mingzhong++;
             q1.add(random[index]/10);
            }
            else
            {
              q1.add(random[index]/10);
              errortime++;
            }
          }
          else
          {
            if(q1.contains(random[index]/10))
              {
               q1.remove(index);
               mingzhong++;
              }
              else
              {
                q1.poll();//抛出
                q1.add(random[index]/10);
                errortime++;
              }
          }
          index++;
        }
        double minz=1-(double)(errortime/(double)(320));//mingzhong/320一样
        String minzhon=String.format("%.4f", minz);
        System.out.println("lur :memory:"+memory+" 缺失个数:"+errortime+" 命中个数"+mingzhong+"  命中率:"+minzhon);
    }
    System.out.println("书否继续Y/N");
    String team=sc.next();
    if(team.equals("Y"))
    {
      System.out.println("请输入编号");
      int index=sc.nextInt();
      if(index==1) {fifo();}
      else
      {
        lur();
      }
    }
  }
  private static void fifo() {
    // TODO 自动生成的方法存根
    for(int memory=2;memory<=32;memory++)
    {
      mingzhong=0;errortime=0;
        List<Integer>list=new ArrayList<>();
        int index=0;
        while(index<320)
        {
          if(list.size()<memory)//小于内存
          {
           if(list.contains(random[index]/10)) {mingzhong++;}
           else
           {
             list.add(random[index]/10);
             errortime++;
           }           
          }
          else//内存慢了
          {
               if(list.contains(random[index]/10)) {mingzhong++;}
             else
             {
               list.remove(0);//移除第一个
               list.add(random[index]/10);
               errortime++;
             }
          }
          index++;
         // System.out.println(index);
         }
        double minz=1-(double)(errortime/(double)(320));//mingzhong/320一样
        String minzhon=String.format("%.4f", minz);
        System.out.println("fifo :memory:"+memory+" 缺失个数:"+errortime+" 命中个数"+mingzhong+"  命中率:"+minzhon);
    }
    System.out.println("书否继续Y/N");
    String team=sc.next();
    if(team.equals("Y"))
    {
      System.out.println("请输入编号");
      int index=sc.nextInt();
      if(index==1) {fifo();}
      else
      {
        lur();
      }
    }
  }
  public static void main(String[] args) {
    init();
    System.out.println("随机数为");
    for(int i=0;i<320;i++)
    {
      System.out.print(String.format("%5d", random[i]));
      if((i+1)%20==0)System.out.println();
    }
    System.out.println("输入选择算法的计算方式序号\n1:先进先出的算法(FIFO);\n" + 
        "2:最近最少使用算法(LRU);\n" + 
        "3:最佳淘汰算法(OPT);\n" + 
        "4:最少访问页面算法(LFR);");
    int index=sc.nextInt();
    if(index==1) {
      fifo();
    }
    else if(index==2)
    {
      lur();
    }   
  }
}


执行结果




20190721130945495.png


fifo


20190721131005456.png


lur


20190721131056581.png


可以看得出成功了。并且最后都是0.9因为固定缺页数(首次命中)达到0.1.


目录
相关文章
|
3月前
|
算法 调度 UED
探索操作系统的心脏:调度算法的奥秘与影响
【10月更文挑战第9天】 本文深入探讨了操作系统中至关重要的组件——调度算法,它如同人体的心脏,维持着系统资源的有序流动和任务的高效执行。我们将揭开调度算法的神秘面纱,从基本概念到实际应用,全面剖析其在操作系统中的核心地位,以及如何通过优化调度算法来提升系统性能。
|
2月前
|
算法 调度 Python
深入理解操作系统中的进程调度算法
在操作系统中,进程调度是核心任务之一,它决定了哪个进程将获得CPU的使用权。本文通过浅显易懂的语言和生动的比喻,带领读者了解进程调度算法的重要性及其工作原理,同时提供代码示例帮助理解。
|
2月前
|
算法 大数据 Linux
深入理解操作系统之进程调度算法
【10月更文挑战第24天】本文旨在通过浅显易懂的语言,带领读者深入了解操作系统中的进程调度算法。我们将从进程的基本概念出发,逐步解析进程调度的目的、重要性以及常见的几种调度算法。文章将通过比喻和实例,使复杂的技术内容变得生动有趣,帮助读者建立对操作系统进程调度机制的清晰认识。最后,我们还将探讨这些调度算法在现代操作系统中的应用和发展趋势。
|
3月前
|
算法 调度 UED
深入理解操作系统的进程调度算法
【10月更文挑战第7天】在操作系统的心脏——内核中,进程调度算法扮演着至关重要的角色。它不仅影响系统的性能和用户体验,还直接关系到资源的合理分配。本文将通过浅显易懂的语言和生动的比喻,带你一探进程调度的秘密花园,从最简单的先来先服务到复杂的多级反馈队列,我们将一起见证算法如何在微观世界里编织宏观世界的和谐乐章。
|
3月前
|
边缘计算 算法 调度
探究操作系统的心脏:调度算法的进化与影响
【10月更文挑战第2天】 本文深入探讨了操作系统中核心组件——调度算法的历史演变、关键技术突破及其对现代计算的影响。通过详细回顾从单任务到多任务、实时系统及分布式计算环境下调度算法的发展,文章揭示了这些算法如何塑造我们的数字世界,并对未来的趋势进行了展望。不同于传统的摘要,本文特别聚焦于技术细节与实际应用的结合点,为读者提供一幅清晰的技术演进蓝图。
74 4
|
3月前
|
算法 调度 UED
探索操作系统的心脏:进程调度算法
【9月更文挑战第32天】在数字世界的每一次心跳中,都隐藏着一个不为人知的英雄——进程调度算法。它默默地在后台运作,确保我们的命令得到快速响应,应用程序平稳运行。本文将带你走进操作系统的核心,一探进程调度的奥秘,并通过代码示例揭示其背后的智慧。准备好跟随我一起深入这趟技术之旅了吗?让我们开始吧!
|
4月前
|
算法 调度
操作系统的心脏:深入解析进程调度算法
本文旨在深入探讨现代操作系统中的核心功能之一——进程调度。进程调度算法是操作系统用于分配CPU时间片给各个进程的机制,以确保系统资源的高效利用和公平分配。本文将详细介绍几种主要的进程调度算法,包括先来先服务(FCFS)、短作业优先(SJF)、时间片轮转(RR)以及优先级调度(PS)。我们将分析每种算法的基本原理、优缺点及其适用场景。同时,本文还将讨论多级反馈队列(MFQ)调度算法,并探讨这些算法在实际应用中的表现及未来发展趋势。通过深入解析这些内容,希望能够为读者提供对操作系统进程调度机制的全面理解。
|
4月前
|
存储 算法 前端开发
深入理解操作系统:进程调度与优先级队列算法
【9月更文挑战第25天】在操作系统的复杂世界中,进程调度是维持系统稳定运行的核心机制之一。本文将深入探讨进程调度的基本概念,分析不同的进程调度算法,并着重介绍优先级队列算法的原理和实现。通过简洁明了的语言,我们将一起探索如何优化进程调度,提高操作系统的效率和响应速度。无论你是计算机科学的初学者还是希望深化理解的专业人士,这篇文章都将为你提供有价值的见解。
|
4月前
|
人工智能 算法 大数据
探究操作系统的心脏:调度算法的进化与影响
本文深入探讨了操作系统中核心组件——调度算法的发展及其对系统性能的影响。通过分析先来先服务、短作业优先、时间片轮转等传统调度算法,阐述了它们的原理和优缺点。同时,讨论了现代调度算法如多级队列和优先级调度在提高系统响应速度和处理能力方面的作用。文章还探讨了实时系统中的调度挑战,以及如何通过优化调度策略来满足不同应用场景下的性能需求。
|
4月前
|
机器学习/深度学习 算法 物联网
探究操作系统的心脏:调度算法的演变与优化
本文旨在深入探讨操作系统中核心组件——调度算法的发展脉络与优化策略。通过分析从单任务到多任务、实时系统的演进过程,揭示调度算法如何作为系统性能瓶颈的解决关键,以及在云计算和物联网新兴领域中的应用前景。不同于传统摘要,本文将注重于概念阐释与实例分析相结合,为读者提供直观且全面的理解视角。