页面置换算法的模拟实现
一、实验目的和要求
掌握虚拟存储管理中页面置换算法的原理,设计恰当的数据结构和算法,模拟实现页面置换算法。
熟练掌握一种以上的开发工具,如C++、JAVA、Delphi、VB等,掌握基本的编程技巧,自由选择一种编程语言设计并实现实验内容。
二、实验方法与步骤(需求分析、算法设计思路、流程图等)
需求分析:作业要访问的页面不在主存时,由缺页中断处理程序将所需的页面调入主存,若此时主存没有空闲物理块,则系统将按照一定的页面置换算法选择一些页面移除,以便装入所缺的页面。本实验模拟最近最久未用置换算法-LRU置换算法实现,是一种通用的广泛采用的有效算法。
算法设计思路:LRU置换算法每次都选择最久未使用的页面淘汰,即总是淘汰最后一次访问时间距当前时间间隔最长的页面。该算法的实现中,主要是标记在主存中的页面那个最近最久未被使用。本实验采用堆栈法的策略,按照页面被访问的时间次序依次将页面排列到堆栈中,因此每次最近被访问的页面总是在栈顶,而栈底的页面就是最近最久未使用的页面。当主存中没有空闲的物理块时,需要将栈底页面置换出去,其余页面依次下移,要访问的页面就可以添加到栈顶了。
流程图:
三、实验原始纪录(源程序、数据结构等)
1.数据结构(页面对象):
public class Page {
int pagenum; //页面号
int time; //页面调入主存后存在的时间
}
2.算法实现代码:
public static void lru(List<Page> list, int p) {
//淘汰页面集合
List<Page> out = new ArrayList<>();
//主存内页面集合,栈式存储
List<Page> base = new ArrayList<>();
//缺页中断次数
int count = 0;
//命中次数
int n = 0;
for (Page page : list) {
//判断访问的页面是否已在主存
boolean flag = false;
//如果页面已在主存中,无需淘汰页面,该页面序列之上的页面依次下移
for (Page pageIn : base) {
if (page.pagenum == pageIn.pagenum) {
for (int i = base.indexOf(pageIn); i+1 < base.size(); i++) {
base.set(i,base.get(i+1));
flag = true;
}
//页面已在主存中,命中,
n++;
}
}
//如果页面不在主存
if (!flag) {
//若此时主存未满则直接添加到堆栈中
if (base.size() < p) {
base.add(page);
} else {
//若此时主存已满,则淘汰栈底元素,其余页面依次下移
out.add(base.get(0));
for (int i = 0; i + 1 < base.size(); i++) {
base.set(i, base.get(i + 1));
}
}
//页面不在主存中,发生缺页中断
count++;
}
//要访问的页面置于栈顶
base.set(base.size()-1,page);
System.out.print("各物理块页号情况:");
for (Page pageIn : base) {
System.out.print(pageIn.pagenum + " ");
}
System.out.println();
}
System.out.print("淘汰页号:");
for (Page page : out) {
System.out.print(page.pagenum + " ");
}
System.out.println();
double qyl = (double) count/(double) list.size();
System.out.println("缺页中断次数:"+count+","+"缺页率为:"+ qyl);
double mzl = (double) n/(double) list.size();
System.out.println("命中次数:"+n+","+"命中率:"+mzl);
}
测试类:
public class Test {
public static void main(String[] args) {
List<Page> list = new ArrayList<>();
Scanner sc = new Scanner(System.in);
System.out.println("请输入访问页面的个数:");
int n = sc.nextInt();
System.out.println("请输入页面访问的序列:");
for (int i = 0; i < n; i++) {
int pagenum = sc.nextInt();
Page page = new Page(pagenum, 0);
list.add(page);
}
Page.lru(list, 3);
}
}
四、实验结果及分析(计算过程与结果、数据曲线、图表等)
测试设计分配3个主存块,模拟访问13个页面,用表格来描述页面置换过程:
页面访问顺序 | 1 | 2 | 3 | 4 | 2 | 1 | 2 | ||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 2 | 3 | 4 | 4 | 3 | 5 | 2 | 3 | 7 | 6 | |
2 | 2 | 3 | 4 | 2 | 1 | 1 | 2 | 3 | 5 | 2 | 3 | ||
3 | 4 | 2 | 1 | 2 | 2 | 3 | 5 | 2 | 3 | 7 | |||
1 | 1 | 1 | 2 | 3 | 4 | 4 | 3 | 5 | 2 | 3 | 7 | 6 | |
淘汰页面 | 1 | 3 | 4 | 1 | 5 | 2 | |||||||
缺页中断 | √ | √ | √ | √ | √ | √ | √ | √ | √ | ||||
命中 | √ | √ | √ | √ |
分析:访问页面时,页面未存在于主存中,发生缺页中断。(若此时主存空闲,直接添加堆栈。若此时主存已满,则需要淘汰栈底页面后调入新页面)
若页面已存在于主存中,则命中,需要将页面置于栈顶。
计算:缺页中断次数为9,缺页率9/13=69.2%;命中次数为4,命中率4/13=30.7%。
实验结果分析:根据结果分析,他们共享缓存器资源包子余量count,首先是p1被执行,包子加1,余量为1。接着c1被执行,包子减1,余量为0 。其余步骤类似,直至第11步,此时c1执行,发现缓存器中包子余量为0,进入等待状态并唤醒生产者。同理可知,如果某个时刻执行生产者进程时,发现缓存器中包子余量为3,则该生产者进程也会进入等待状态并唤醒消费者。
五、实验改进与思考
通过此次实验,加深了我对操作系统存储管理的认识,了解了操作系统中各种资源分配算法的实现,特别是对虚拟存储和页面置换的各个算法有了深刻的理解。
这次实验总体难度不是很大,需要实现的算法数目虽然不少,但基本思路较为相似,只要理解了每种算法的特点,理清思路实现起来也并不是很困难。本实验主要对LRU算法作了介绍。从页面数据结构的设计到算法的一步步实现,不断尝试、修改、优化,锻炼了我的编程能力,通过阅读课件再加上自己的理解,我了解了用堆栈法的设计思路。虽然方法名字叫堆栈法,但由于要频繁对栈底元素操作因此并没有真正用到栈,而是利用了栈先进后出的这一特点,方便我们找寻最近最久未使用的页面,感觉这个思路极其巧妙,设计中用到的方法和技巧体现出的很多思想值得我学习。