操作系统实验:页面置换算法的模拟实现及命中率对比(学习笔记)
题目要求
使用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的命中率比较结果。
抽象数据类型定义
// 在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];
指令地址流生成
// 存储指令地址
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;
}
指令地址流到页地址流的转换
// 存储变换生成的页地址(页号)
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算法命中率计算
// 存储页框里的页地址信息
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算法命中率计算
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算法命中率计算
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的命中率大小对比
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”即可运行。
如果文章内容出错或者您有更好的解决方法,欢迎到评论区指正和讨论!