操作系统实验:页面置换算法的模拟实现及命中率对比(学习笔记)
- 题目要求
- 输入要求
- 输出要求
- 编程平台
- 实验成果
- 开始模拟
- 错误输入
- 退出程序
- 代码实现
- 抽象数据类型定义
- 指令地址流生成
- 指令地址流到页地址流的转换
- OPT算法命中率计算
- FIFO算法命中率计算
- LRU算法命中率计算
- FIFO与LRU的命中率大小对比
- bat文件制作
题目要求
使用Java进行编程,程序可以多次计算在400条页地址访问情况下,内存容量从4页框增至40页框的过程中使用OPT(最佳置换)、FIFO(先进先出)和 LRU(最近最少使用)算法的命中率(即1-缺页率),并统计了FIFO与LRU这两种算法命中率大小的对比结果,方便用户在多次比较中总结FIFO与LRU算法的平均性能。
程序最终以双击bat文件的形式来运行。
输入要求
本程序有效输入的形式为正整数,输入值为1或0。
输入1则开始模拟页面置换算法,输入0则退出程序,其他输入均会让程序提示错误信息。
输出要求
本程序输出的形式为简体中文、正整数、浮点数以及界面修饰符号。简体中文和正整数用于说明程序信息和输出内容;浮点数是保留小数点4位的double型数据,用于展示三种页面置换算法的命中率;界面修饰符号让结果展示得更加清晰易懂。
输入1之后,程序首先以每行10个展示随机生成的400条指令地址,范围为[0,399];接着展示由这400条指令地址转换成的400条页地址,范围为[0,39];最后展示页框数从4到40的情况下OPT、FIFO、LRU这三种页面置换算法的命中率,以及FIFO与LRU算法的命中率大小对比结果。
编程平台
Eclipse 4.8.0 控制台
实验成果
开始模拟
错误输入
退出程序
代码实现
思路:编写三个Java包,分别为main、calculation和produceData。main存放程序主类Main.java,produceData存放用于产生指令地址流的RandomNumber.java和用于将指令地址流变换成页地址流的ConvertToPageNumber.java,calculation存放用于页面置换算法命中率的OutputResult.java。
(1)首先,Main函数调用RandomNumber.produce()产生随机的指令地址流,并调用RandomNumber.output()展示指令地址流。
(2)其次,Main函数调用ConvertToPageNumber.convert(addresses)将指令地址流变换成页地址流,并调用ConvertToPageNumber.output()展示页地址流。
(3)最后,Main函数在不同用户内存容量下分别调用OutputResult.calculteOPT(addresses, pageFrame)、OutputResult.calculteFIFO(addresses, pageFrame)、OutputResult.calculteLRU(addresses, pageFrame)计算三种页面置换算法的命中率,并调用OutputResult.output()输出命中率情况以及FIFO与LRU的命中率比较结果。
抽象数据类型定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // 在Main.java中记录生成的指令地址流和页地址流,并作为所调用函数的参数。 int[] addresses = new int[400]; // 在RandomNumber.java中记录最终结果,并作为函数返回值。 static int[] instructionAddresses = new int[400]; // 在ConvertToPageNumber.java中记录最终结果,并作为函数返回值。 static int[] pageAddresses = new int[400]; // 在OutputResult.java中记录最终结果。 // accuracy[0]、accuracy[1]、accuracy[2]分别存储不同页框数下三种页面置换算法的命中率。 // accuracy[3]存储FIFO与LRU算法的命中率大小对比结果。 static double[][] accuracy = new double[4][37]; // 在OutputResult.java中记录当前用户内存容量中页地址的信息。 // pageCapacity[pageFrame][0]记录当前页框的页地址; // pageCapacity[pageFrame][1]则根据OPT、FIFO和LRU算法的需要分别记录当前页框下页地址未来是否被访问、滞留在该页框的时长、最新被访问时间值的信息。 static int[][] pageCapacity = new int[pageFrame][2]; |
指令地址流生成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | // 存储指令地址 static int[] instructionAddresses; // 随机生成400条指令地址 public static int[] produce() { // 存储随机生成的400条指令地址 instructionAddresses = new int[400]; // 分区间采用非顺序与顺序方法轮流产生随机指令地址 int k = 0;// 各区间增幅 while(k < 200) { // [0,199]生成随机指令地址 instructionAddresses[0 + k] = (int) (Math.random() * 200); if(instructionAddresses[0 + k] != 199) { // 保证顺序执行后不跨区间 instructionAddresses[0 + k + 1] = instructionAddresses[0 + k] + 1; }else { instructionAddresses[0 + k + 1] = instructionAddresses[0 + k]; } // [200,399]生成随机指令地址 instructionAddresses[200 + k] = (int) (Math.random() * 200) + 200; if(instructionAddresses[200 + k] != 399) { // 保证顺序执行后不跨区间 instructionAddresses[200 + k + 1] = instructionAddresses[200 + k] + 1; }else { instructionAddresses[200 + k + 1] = instructionAddresses[200 + k]; } k += 2; } return instructionAddresses; } |
指令地址流到页地址流的转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // 存储变换生成的页地址(页号) static int[] pageAddresses; // 变换生成400条页地址(页号) public static int[] convert(int[] instructionAddresses) { // 存储变换生成的400条页地址(页号) pageAddresses = new int[400]; for(int i = 0; i < 400; i ++) { if(instructionAddresses[i] < 0 || instructionAddresses[i] >399) { System.out.println(); System.out.println("指令数据中存在错误数据!"); System.out.println(); }else { pageAddresses[i] = instructionAddresses[i] / 10; } } return pageAddresses; } |
OPT算法命中率计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | // 存储页框里的页地址信息 static int[][] pageCapacity; public static void calculteOPT(int[] pageAddresses, int pageFrame) { // pageCapacity[pageFrame][0]放置页地址; // pageCapacity[pageFrame][1]表示该页地址在未来是否出现(“是”用1表示,否则置0)。 pageCapacity = new int[pageFrame][2]; int currentPageFrame = 0;// 当前页框数 // 用于检查用户内存容量是否有余 for(currentPageFrame = 0; currentPageFrame < pageFrame; currentPageFrame ++) { pageCapacity[currentPageFrame][0] = -1; } double pageFault = 0;// 缺页次数 for(int k = 0; k < 400; k ++) { // 判断是否已存在该页地址 for(currentPageFrame = 0; currentPageFrame < pageFrame; currentPageFrame ++) { if(pageCapacity[currentPageFrame][0] == pageAddresses[k]) { break; } } // 处理需插入新页地址的情况 if(currentPageFrame == pageFrame) { // 当有剩余用户内存容量时 for(currentPageFrame = 0; currentPageFrame < pageFrame; currentPageFrame ++) { if(pageCapacity[currentPageFrame][0] == -1) { pageCapacity[currentPageFrame][0] = pageAddresses[k]; break; } } // 当需要置换页地址时 if(currentPageFrame == pageFrame) { int replacement = 0; // 检查pageAddresses[k]之后是否出现页地址与现有页框内容重复 for(int i = k + 1, flag = 0; i < 400; i ++) { for(currentPageFrame = 0; currentPageFrame < pageFrame; currentPageFrame ++) { if(pageCapacity[currentPageFrame][0] == pageAddresses[i]) { if(pageCapacity[currentPageFrame][1] == 0) { pageCapacity[currentPageFrame][1] = 1; // 标记该处与未来要访问的页地址重复 flag ++; } break; } } if(flag == pageFrame - 1) { // 如果出现pageFrame-1次重复,则置换剩下那一个未出现重复的页地址 break; } } // 找到页框中在未来不出现重复的第一个页地址 for(currentPageFrame = 0; currentPageFrame < pageFrame; currentPageFrame ++) { if(pageCapacity[currentPageFrame][1] == 0) { replacement = currentPageFrame; break; } } pageCapacity[replacement][0] = pageAddresses[k]; pageFault ++; // 增加缺页中断次数 // 清楚检查痕迹 for(currentPageFrame = 0; currentPageFrame < pageFrame; currentPageFrame ++) { pageCapacity[currentPageFrame][1] = 0; } } } } accuracy[0][pageFrame - 4] = 1 - pageFault / 400; pageFault = 0; // 清空数据 } |
FIFO算法命中率计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | public static void calculteFIFO(int[] pageAddresses, int pageFrame) { // pageCapacity[pageFrame][0]放置页地址; // pageCapacity[pageFrame][1]表示该页地址滞留在该页框的时长。 pageCapacity = new int[pageFrame][2]; int currentPageFrame = 0; // 当前页框数 // 用于检查用户内存容量是否有余 for(currentPageFrame = 0; currentPageFrame < pageFrame; currentPageFrame ++) { pageCapacity[currentPageFrame][0] = -1; } double pageFault = 0; // 缺页次数 for(int k = 0; k < 400; k ++) { // 判断是否已存在该页地址 for(currentPageFrame = 0; currentPageFrame < pageFrame; currentPageFrame ++) { if(pageCapacity[currentPageFrame][0] == pageAddresses[k]) { // 增加页框中有效页地址的滞留时长 for(int i = 0; i < pageFrame; i ++) { if(pageCapacity[i][0] != -1) { pageCapacity[i][1] ++; } } break; } } // 处理需插入新页地址的情况 if(currentPageFrame == pageFrame) { // 当有剩余用户内存容量时 for(currentPageFrame = 0; currentPageFrame < pageFrame; currentPageFrame ++) { if(pageCapacity[currentPageFrame][0] == -1) { pageCapacity[currentPageFrame][0] = pageAddresses[k]; // 增加页框中有效页地址的滞留时长,当前页地址的滞留时长置为1 for(int i = 0; i < pageFrame; i ++) { if(pageCapacity[i][0] != -1) { pageCapacity[i][1] ++; } } pageCapacity[currentPageFrame][1] = 1; break; } } // 当需要置换页地址时 if(currentPageFrame == pageFrame) { int replacement = 0; // 比较选出当前滞留时长最大的页地址,出现相同情况则按顺序选取 for(currentPageFrame = 1; currentPageFrame < pageFrame; currentPageFrame ++) { if(pageCapacity[currentPageFrame][1] > pageCapacity[replacement][1]) { replacement = currentPageFrame; } } pageCapacity[replacement][0] = pageAddresses[k]; // 增加页框中各页地址的滞留时长,当前页地址的滞留时长置为1 for(currentPageFrame = 0; currentPageFrame < pageFrame; currentPageFrame ++) { pageCapacity[currentPageFrame][1] ++; } pageCapacity[replacement][1] = 1; pageFault ++; // 增加缺页中断次数 } } } accuracy[1][pageFrame - 4] = 1 - pageFault / 400; pageFault = 0; // 清空数据 } |
LRU算法命中率计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | public static void calculteLRU(int[] pageAddresses, int pageFrame) { // pageCapacity[pageFrame][0]放置页地址; // pageCapacity[pageFrame][1]表示该页地址的最新被访问时间值。 pageCapacity = new int[pageFrame][2]; int currentPageFrame = 0; // 当前页框数 // 用于检查用户内存容量是否有余 for(currentPageFrame = 0; currentPageFrame < pageFrame; currentPageFrame ++) { pageCapacity[currentPageFrame][0] = -1; } double pageFault = 0; // 缺页次数 for(int k = 0; k < 400; k ++) { // 判断是否已存在该页地址 for(currentPageFrame = 0; currentPageFrame < pageFrame; currentPageFrame ++) { if(pageCapacity[currentPageFrame][0] == pageAddresses[k]) { pageCapacity[currentPageFrame][1] = 0; break; } } // 处理需插入新页地址的情况 if(currentPageFrame == pageFrame) { // 当有剩余用户内存容量时 for(currentPageFrame = 0; currentPageFrame < pageFrame; currentPageFrame ++) { if(pageCapacity[currentPageFrame][0] == -1) { pageCapacity[currentPageFrame][0] = pageAddresses[k]; // 增加页框中有效页地址的最新被访问时间值,当前页地址的最新被访问时间值置为0 for(int i = 0; i < pageFrame; i ++) { if(pageCapacity[i][0] != -1) { pageCapacity[i][1] ++; } } pageCapacity[currentPageFrame][1] = 0; break; } } // 当需要置换页地址时 if(currentPageFrame == pageFrame) { int replacement = 0; // 比较选出当前最新被访问时间值最大的页地址,出现相同情况则按顺序选取 for(currentPageFrame = 1; currentPageFrame < pageFrame; currentPageFrame ++) { if(pageCapacity[currentPageFrame][1] > pageCapacity[replacement][1]) { replacement = currentPageFrame; } } pageCapacity[replacement][0] = pageAddresses[k]; // 增加页框中有效页地址的最新被访问时间值,当前页地址的最新被访问时间值置为0 for(currentPageFrame = 0; currentPageFrame < pageFrame; currentPageFrame ++) { pageCapacity[currentPageFrame][1] ++; } pageCapacity[replacement][1] = 0; pageFault ++; // 增加缺页中断次数 } } } accuracy[2][pageFrame - 4] = 1 - pageFault / 400; pageFault = 0; // 清空数据 } |
FIFO与LRU的命中率大小对比
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | System.out.println("FIFO与LRU的命中率大小对比结果:"); // 用accuracy[3][0]、accuracy[3][1]和accuracy[3][2]记录三种对比结果出现次数的统计 for(int j = 0; j < 37; j ++) { if(accuracy[1][j] > accuracy[2][j]) { accuracy[3][0] ++; }else if(accuracy[1][j] == accuracy[2][j]) { accuracy[3][1] ++; }else { accuracy[3][2] ++; } } System.out.println("FIFO > LRU : "+ (int)accuracy[3][0] + "次"); System.out.println("FIFO = LRU : "+ (int)accuracy[3][1] + "次"); System.out.println("FIFO < LRU : "+ (int)accuracy[3][2] + "次"); |
bat文件制作
(1)将整个Java项目导出成一个jar包,如:PageReplacementAlgorithm.jar
(2)在jar包所在目录下新建txt文件,文件内容为:java -jar PageReplacementAlgorithm.jar
(3)将txt文件修改成bat文件,如:点我运行.bat
(4)双击“点我运行.bat”即可运行。
如果文章内容出错或者您有更好的解决方法,欢迎到评论区指正和讨论!