操作系统实验:页面置换算法的模拟实现及命中率对比(学习笔记)

题目要求

使用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”即可运行。

如果文章内容出错或者您有更好的解决方法,欢迎到评论区指正和讨论!

  • 16
    点赞
  • 109
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
一、课程设计目的 通过请求管理页面置换算法模拟设计,了解虚拟存储技术的特点,掌握请 求存储管理中的页面置换算法。 容 二、课程设计内容 模拟实现 OPT(最佳置换)、FIFOLRU 算法,并计算缺率。 示 三、要求及提示 本题目必须单人完成。 1、首先用随机数生成函数产生一个“指令将要访问的地址序列”,然后将地址序列变换 成相应的地址流(即访问序列),再计算不同算法下的命中率。 2、通过随机数产生一个地址序列,共产生 400 条。其中 50%的地址访问是顺序执行的, 另外 50%就是非顺序执行。且地址在前半部地址空间和后半部地址空间均匀分布。具体产 生方法如下: 1) 在前半部地址空间,即[0,199]中随机选一数 m,记录到地址流数组中(这是 非顺序执行); 2) 接着“顺序执行一条指令”,即执行地址为 m+1 的指令,把 m+1 记录下来; 3) 在后半部地址空间,[200,399]中随机选一数 m’,作为新指令地址; 4) 顺序执行一条指令,其地址为 m’+1; 5) 重复步骤 1~4,直到产生 400 个指令地址。 3、将指令地址流变换成地址(号)流,简化假设为: 1) 页面大小为 1K(这里 K 只是表示一个单位,不必是 1024B); 2) 用户虚存容量为 40K; 3) 用户内存容量为 4 个框到 40 个框; 6 4) 用户虚存中,每 K 存放 10 条指令,所以那 400 条指令访问地址所对应的地 址(号)流为:指令访问地址为[0,9]的地址为第 0 ;指令访问地址为[10, 19]的地址为第 1 ;……。按这种方,把 400 条指令组织进“40 ”,并 将“要访问的号序列”记录到地址流数组中。 4、循环运行,使用户内存容量从 4 框到 40 框。计算每个内存容量下不同页面置换 算法的命中率。输出结果可以为: 框数 OPT 缺FIFOLRU率 [4] OPT:0.5566 FIFO:0.4455 LRU:0.5500 [5] OPT:0.6644 FIFO:0.5544 LRU:0.5588 …… …… …… …… [39] OPT:0.9000 FIFO:0.9000 LRU:0.9000 [40] OPT:1.0000 FIFO:1.0000 LRU:1.0000 注 1:在某一次实验中,可能 FIFOLRU 性能更好,但足够多次的实验表明 LRU 的平均性能比 FIFO 更好。 注 2:计算缺率时,以框填满之前和之后的总缺次数计算。
设计一个虚拟存储区和内存工作区,并使用下列算法计算访问命中率. (1) 进先出的算法(FIFO) (2) 最近最少使用的算法(LRU) (3) 最佳淘汰算法(OPT)(4) 最少访问页面算法(LFU) (5) 最近最不经常使用算法(NUR) 命中率=1-页面失效次数/地址流长度 本实验的程序设计基本上按照实验内容进行。即首先用 srand()和 rand()函数定 义和产生指令序列,然后将指令序列变换成相应的地址流,并针对不同的算法 计算出相应的命中率。相关定义如下: 1 数据结构 (1)页面类型 typedef struct{ int pn,pfn,counter,time; }pl-type; 其中 pn 为号,pfn 为面号, counter 为一个周期内访问该页面的次数, time 为访问时间. (2) 页面控制结构 pfc-struct{ int pn,pfn; struct pfc_struct *next;} typedef struct pfc_struct pfc_type; pfc_type pfc_struct[total_vp],*freepf_head,*busypf_head; pfc_type *busypf_tail; 其中 pfc[total_vp]定义用户进程虚控制结构, *freepf_head 为空页面头的指针, *busypf_head 为忙页面头的指针, *busypf_tail 为忙页面尾的指针. 2.函数定义 (1)Void initialize( ):初始化函数,给每个相关的页面赋值. (2)Void FIFO( ):计算使用 FIFO 算法时的命中率. (3)Void LRU( ):计算使用 LRU 算法时的命中率. (4)Void OPT( ):计算使用 OPT 算法时的命中率. (5)Void LFU( ):计算使用 LFU 算法时的命中率. (6)Void NUR( ):计算使用 NUR 算法时的命中率. 3.变量定义 (1)int a[total_instruction]: 指令流数据组.(2)int page[total_instruction]: 每条指令所属的号. (3)int offset[total_instruction]: 每装入 10 条指令后取模运算号偏移 值. (4)int total_pf: 用户进程的内存页面数. (5)int disaffect: 页面失效次数.
SCAU综合实验课程中的文件操作与字符处理是一门非常重要的课程,它教授了如何使用计算机来操作文件以及对文件中的字符进行处理。 文件操作是指使用计算机来创建、打开、读取、写入、修改和关闭文件的过程。在实验中,我们学习了如何使用C++编程语言来实现这些文件操作。通过实践,我们可以掌握文件的基本操作,比如创建文件、打开已存在的文件、从文件中读取数据、向文件中写入数据和关闭文件等等。这些操作对于日常的文件处理工作非常有用。 字符处理是指对文件中的字符进行操作和处理的过程。我们学习了如何使用C++语言中的字符串类和字符数组来对字符进行处理。通过实验,我们可以掌握字符串的创建、拼接、截取、替换和转换等操作。这些操作可以帮助我们更好地处理文件中的文本数据,比如查找特定的字符、计算字符串的长度、在字符串中查找子字符串等等。 此外,我们还学习了一些常见的文件操作和字符处理的应用,比如统计一个文件中的字符数、单词数和行数,将一个文件中的大写字母转换为小写字母,将一个文件中的所有空格替换为换行符等等。这些应用可以帮助我们更好地理解文件操作和字符处理的实际应用场景,并提高我们的实际操作能力。 综上所述,SCAU综合实验中的文件操作与字符处理课程是非常实用和重要的一门课程,它教授了我们使用计算机来操作文件和处理字符的技能和知识。通过学习这门课程,我们可以提高我们的实际操作能力,为以后的工作做好准备。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值