操作系统的三种页面置换算法
原创作者:原创,在原创作者的基础上进行了改进,(最佳置换,原作者第十行应该是ProcessBlocks[flag][j] 第62行j参数取值不对 第64行temp参数错误),三种算法均已实现
实验目的
编程模拟实现存储器页面置换的常用算法,调试分析相关存储器管理程序,加深对存储器页面置换常用算法的理解和实现技巧
实验内容
1、 自定义存储器管理有关的数据结构;
2、 依据最佳置换算法(Optimal)、先进先出置换算法(FIFO)、最近最久未使用算法(LRU)原理,编写对应函数,模拟系统的存储器页面置换功能;
3、 为了更好地模拟和评价算法的性能,允许用户自行指定可用页块数并输入需访问的页面号序列;
4、 统计以上算法实际需要的总页面数、缺页中断次数以及它们的缺页率;
5、 比较/分析以上算法的置换性能,并作出自己的评价
实验步骤
定义变量
public class 页面置换算法 {
private static int MaxPage_num = 100;
private static int[] PageView = new int[MaxPage_num];// 访问页面顺序
private static int[][] Blocks = new int[MaxPage_num][MaxPage_num];// 物理块
private static int[] PageCount = new int[MaxPage_num];// 记录权重
private static int PageNum;// 访问页面个数
private static int BlockNum;// 物理块个数
// private static int temp = 0;
// private static int flag = 0;
private static int MissingPageNum;// 缺页个数
private static double MissingPageRate;// 缺页率
private static boolean found;// 判断是否找到
private static int j;
private static int i;
private static int k;
private static int NULL = -1;// 缺页
// 动态输入物理块、页面、访问页面顺序
public static void input() {
Scanner sc = new Scanner(System.in);
System.out.println("请输入页面个数");
PageNum = sc.nextInt();
System.out.println("请输入物理块个数");
BlockNum = sc.nextInt();
System.out.println("请输入页面访问顺序");
for (int i = 0; i < PageNum; i++) {
PageView[i] = sc.nextInt();
}
}
输出函数
// 输出结果
public static void output() {
// 输出访问页面
System.out.print("访问页面:");
for (i = 0; i < PageNum; i++) {
System.out.print(PageView[i] + " ");
}
System.out.println();
// 输出物理块
System.out.println("物理块");
for (int j = 0; j < BlockNum; j++) {
for (int i = 0; i < PageNum; i++) {
if (Blocks[i][j] == NULL)
System.out.print(" ");
else
System.out.print(Blocks[i][j] + " ");
}
System.out.println(" ");
}
MissingPageRate = (double) MissingPageNum / PageNum;
System.out.println();
System.out.println("总页面数" + PageNum + "缺页次数" + MissingPageNum + "缺页率" + MissingPageRate + "%");
}
初始化
// 初始化物理块
public static void original() {
for (i = 0; i < PageNum; i++) {
for (j = 0; j < BlockNum; j++) {
Blocks[i][j] = NULL;
}
}
MissingPageNum = 1;
}
先进先出
// 先进先出
public static void FIFO() {
original();
Blocks[0][0] = PageView[0];
int temp = 0, flag = 0;// temp标记本列,flag标记上一列
for (i = 0; i < PageNum; i++) {
// 如果页已进入物理块跳出循环
for (j = 0; j < BlockNum; j++) {
if (PageView[i] == Blocks[flag][j]) {
break;
}
}
//如果不存在
if (j == BlockNum) {
for (k = 0; k < BlockNum; k++) {
if (Blocks[flag][k] == NULL) {
break;
} else {
Blocks[i][k] = Blocks[flag][k];
}
}
temp++;
temp = temp % BlockNum;
Blocks[i][temp] = PageView[i];
MissingPageNum++;
flag = i;
} else {
continue;
}
}
output();
}
最佳置换
// 最佳置换
public static void OPT() {
original();
Blocks[0][0] = PageView[0];
int temp, flag = 0;
for (i = 0; i < PageNum; i++) {
// 不缺页
for (j = 0; j < BlockNum; j++) {
if (PageView[i] == Blocks[flag][j]) {
break;
}
}
if (j != BlockNum) { // 代表页面已经在内存块中不缺页
continue;
}
// 缺页
for (k = 0; k < BlockNum; k++) {
if (Blocks[flag][k] == NULL) {
break;
} else {
Blocks[i][k] = Blocks[flag][k];
}
}
// 页面选择,满和不满
// 缺页时物理块未满
for (j = 0; j < BlockNum; j++) {
if (Blocks[i][j] == NULL) {
Blocks[i][j] = PageView[i];
MissingPageNum++;
flag = i;
break;
}
}
if (j != BlockNum) {
continue;
}
// 缺页时内存块已满
temp = 0;
for (j = 0; j < BlockNum; j++) {
// 向后遍历,寻找最久不访问
for (k = i + 1; k < PageNum; k++) {
// 寻找最长时间内不被访问的页面 k是内存页面序列下标 存进PageCount数组内
if (Blocks[i][j] == PageView[k]) {
PageCount[j] = k;//找到,记录权重
break;
}
}
}
// 剩下的页面没有和当前物理块一样的,把最后一块BlockNum物理块置换
if (k == PageNum) {
PageCount[j-1] = PageNum;
}
// if (PageCount[temp] < PageCount[j]) {
// temp = j;
System.out.println("运行temp"+j);
// }
for(int a=0;a<BlockNum;a++) {
temp=temp>PageCount[a]?temp:a;
}
Blocks[i][temp] = PageView[i];
MissingPageNum++;
flag = i;
}
output();
}
最近最久未使用
// 最近最久未使用
public static void LRU() {
original();
Blocks[0][0] = PageView[0];
PageCount[0] = 0;
int temp = 0, flag = 0;// temp标记本列,flag标记上一列
for (i = 0; i < PageNum; i++) {
// 不缺页
for (j = 0; j < BlockNum; j++) {
if (PageView[i] == Blocks[flag][j]) {
PageCount[j] = i;
break;
}
}
if (j != BlockNum)
continue;
//缺页
for (k = 0; k < BlockNum; k++) {
if (Blocks[flag][k] == NULL) {
break;
} else {
Blocks[i][k] = Blocks[flag][k];
}
}
//缺页时物理块有空余
for (j = 0; j < BlockNum; j++) {
if (Blocks[i][j] == NULL) {
Blocks[i][j] = PageView[i];
PageCount[j] = i;
MissingPageNum++;
flag = i;
break;
}
}
if (j != BlockNum) {
continue;
}
//缺页时物理块没空余
temp = 0;
for (j = 0; j < BlockNum; j++) {
if (PageCount[temp] > PageCount[j]) {
temp = j;
}
}
Blocks[i][temp] = PageView[i];
PageCount[temp] = i;
MissingPageNum++;
flag = i;
}
output();
}
主函数
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
while (true) {
input();
System.out.println("1.先进先出 2.最佳置换算法 3.最近最久未使用 4.退出");
int c = sc.nextInt();
switch (c) {
case 1:
FIFO();
break;
case 2:
OPT();
break;
case 3:
LRU();
break;
case 4:
System.exit(0);
default:
System.out.println("输入错误 重新输入");
break;
}
}
}
}
完整代码
package 操作系统实验;
import java.util.Scanner;
public class 页面置换算法 {
private static int MaxPage_num = 100;
private static int[] PageView = new int[MaxPage_num];// 访问页面顺序
private static int[][] Blocks = new int[MaxPage_num][MaxPage_num];// 物理块
private static int[] PageCount = new int[MaxPage_num];// 记录权重
private static int PageNum;// 访问页面个数
private static int BlockNum;// 物理块个数
// private static int temp = 0;
// private static int flag = 0;
private static int MissingPageNum;// 缺页个数
private static double MissingPageRate;// 缺页率
private static boolean found;// 判断是否找到
private static int j;
private static int i;
private static int k;
private static int NULL = -1;// 缺页
// 动态输入物理块、页面、访问页面顺序
public static void input() {
Scanner sc = new Scanner(System.in);
System.out.println("请输入页面个数");
PageNum = sc.nextInt();
System.out.println("请输入物理块个数");
BlockNum = sc.nextInt();
System.out.println("请输入页面访问顺序");
for (int i = 0; i < PageNum; i++) {
PageView[i] = sc.nextInt();
}
}
// 输出结果
public static void output() {
// 输出访问页面
System.out.print("访问页面:");
for (i = 0; i < PageNum; i++) {
System.out.print(PageView[i] + " ");
}
System.out.println();
// 输出物理块
System.out.println("物理块");
for (int j = 0; j < BlockNum; j++) {
for (int i = 0; i < PageNum; i++) {
if (Blocks[i][j] == NULL)
System.out.print(" ");
else
System.out.print(Blocks[i][j] + " ");
}
System.out.println(" ");
}
MissingPageRate = (double) MissingPageNum / PageNum;
System.out.println();
System.out.println("总页面数" + PageNum + "缺页次数" + MissingPageNum + "缺页率" + MissingPageRate + "%");
}
// 初始化物理块
public static void original() {
for (i = 0; i < PageNum; i++) {
for (j = 0; j < BlockNum; j++) {
Blocks[i][j] = NULL;
}
}
MissingPageNum = 1;
}
// 先进先出
public static void FIFO() {
original();
Blocks[0][0] = PageView[0];
int temp = 0, flag = 0;// temp标记本列,flag标记上一列
for (i = 0; i < PageNum; i++) {
// 如果页已进入物理块跳出循环
for (j = 0; j < BlockNum; j++) {
if (PageView[i] == Blocks[flag][j]) {
break;
}
}
//如果不存在
if (j == BlockNum) {
for (k = 0; k < BlockNum; k++) {
if (Blocks[flag][k] == NULL) {
break;
} else {
Blocks[i][k] = Blocks[flag][k];
}
}
temp++;
temp = temp % BlockNum;
Blocks[i][temp] = PageView[i];
MissingPageNum++;
flag = i;
} else {
continue;
}
}
output();
}
// 最佳置换
public static void OPT() {
original();
Blocks[0][0] = PageView[0];
int temp, flag = 0;
for (i = 0; i < PageNum; i++) {
// 不缺页
for (j = 0; j < BlockNum; j++) {
if (PageView[i] == Blocks[flag][j]) {
break;
}
}
if (j != BlockNum) { // 代表页面已经在内存块中不缺页
continue;
}
// 缺页
for (k = 0; k < BlockNum; k++) {
if (Blocks[flag][k] == NULL) {
break;
} else {
Blocks[i][k] = Blocks[flag][k];
}
}
// 页面选择,满和不满
// 缺页时物理块未满
for (j = 0; j < BlockNum; j++) {
if (Blocks[i][j] == NULL) {
Blocks[i][j] = PageView[i];
MissingPageNum++;
flag = i;
break;
}
}
if (j != BlockNum) {
continue;
}
// 缺页时内存块已满
temp = 0;
for (j = 0; j < BlockNum; j++) {
// 向后遍历,寻找最久不访问
for (k = i + 1; k < PageNum; k++) {
// 寻找最长时间内不被访问的页面 k是内存页面序列下标 存进PageCount数组内
if (Blocks[i][j] == PageView[k]) {
PageCount[j] = k;//找到,记录权重
break;
}
}
}
// 剩下的页面没有和当前物理块一样的,把最后一块BlockNum物理块置换
if (k == PageNum) {
PageCount[j-1] = PageNum;
}
// if (PageCount[temp] < PageCount[j]) {
// temp = j;
// System.out.println("运行temp"+j);
// }
for(int a=0;a<BlockNum;a++) {
temp=temp>PageCount[a]?temp:a;
}
Blocks[i][temp] = PageView[i];
MissingPageNum++;
flag = i;
}
output();
}
// 最近最久未使用
public static void LRU() {
original();
Blocks[0][0] = PageView[0];
PageCount[0] = 0;
int temp = 0, flag = 0;// temp标记本列,flag标记上一列
for (i = 0; i < PageNum; i++) {
// 不缺页
for (j = 0; j < BlockNum; j++) {
if (PageView[i] == Blocks[flag][j]) {
PageCount[j] = i;
break;
}
}
if (j != BlockNum)
continue;
//缺页
for (k = 0; k < BlockNum; k++) {
if (Blocks[flag][k] == NULL) {
break;
} else {
Blocks[i][k] = Blocks[flag][k];
}
}
//缺页时物理块有空余
for (j = 0; j < BlockNum; j++) {
if (Blocks[i][j] == NULL) {
Blocks[i][j] = PageView[i];
PageCount[j] = i;
MissingPageNum++;
flag = i;
break;
}
}
if (j != BlockNum) {
continue;
}
//缺页时物理块没空余
temp = 0;
for (j = 0; j < BlockNum; j++) {
if (PageCount[temp] > PageCount[j]) {
temp = j;
}
}
Blocks[i][temp] = PageView[i];
PageCount[temp] = i;
MissingPageNum++;
flag = i;
}
output();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
while (true) {
input();
System.out.println("1.先进先出 2.最佳置换算法 3.最近最久未使用 4.退出");
int c = sc.nextInt();
switch (c) {
case 1:
FIFO();
break;
case 2:
OPT();
break;
case 3:
LRU();
break;
case 4:
System.exit(0);
default:
System.out.println("输入错误 重新输入");
break;
}
}
}
}
运行结果
总结
通过页面置换算法程序的编写,最后三次相同物理块与访问次序输出比较,最佳置换算法选择以后永不使用的页面,以便保证最低的缺页率。先进先出算法在分配更多的物理块时反而会造成更高的缺页率。最近最久未使用算法,性能更好。
2021.12.1 对OPT修改
感谢达不溜的橘子评论,OPT运行结果错误。我找了好久问题,原码就不修改了,方便看过的人对比。
根据橘子的数据:答案应该为这样
7 | 0 | 1 | 2 | 0 | 3 | 0 | 4 | 2 | 3 | 0 | 3 | 2 | 1 | 2 | 0 | 1 | 7 | 0 | 1 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
7 | 7 | 7 | 2 | 2 | 2 | 2 | 2 | 7 | |||||||||||
0 | 0 | 0 | 0 | 4 | 0 | 0 | 0 | ||||||||||||
1 | 1 | 3 | 3 | 3 | 1 | 1 | |||||||||||||
实际为这样: | |||||||||||||||||||
![]() | |||||||||||||||||||
可以发现第一、二个物理块一直都没有被置换,说明对权重赋值有问题。 |
问题:
1.原代码63行,比较权重的逻辑不对。权重永远是最后一个物理块权重最大,因此总是最后一个被修改(修改后,若存在两个页面A、B都不再访问,那么权重都为最大,这里默认置换后者,可以在63行修改逻辑,使用随机数对A、B重新赋权,这里不赘述了,晚上两点了我想睡觉哇)。
2.原代码53行,把最后一个物理块置换这段代码,上面一段for执行最后一次外循环后,内循环没有找到,此时最后一个物理块权重也为最大。所以应在上一行for的外循环内。
// 最佳置换
public static void OPT() {
original();
Blocks[0][0] = PageView[0];
int temp, flag = 0;
for (i = 0; i < PageNum; i++) {
// 不缺页
for (j = 0; j < BlockNum; j++) {
if (PageView[i] == Blocks[flag][j]) {
break;
}
}
if (j != BlockNum) { // 代表页面已经在内存块中不缺页
continue;
}
// 缺页
for (k = 0; k < BlockNum; k++) {
if (Blocks[flag][k] == NULL) {
break;
} else {
Blocks[i][k] = Blocks[flag][k];
}
}
// 页面选择,满和不满
// 缺页时物理块未满
for (j = 0; j < BlockNum; j++) {
if (Blocks[i][j] == NULL) {
Blocks[i][j] = PageView[i];
MissingPageNum++;
flag = i;
break;
}
}
if (j != BlockNum) {
continue;
}
// 缺页时内存块已满
temp = 0;
for (j = 0; j < BlockNum; j++) {
// 向后遍历,寻找最久不访问
for (k = i + 1; k < PageNum; k++) {
// 寻找最长时间内不被访问的页面 k是内存页面序列下标 存进PageCount数组内
if (Blocks[i][j] == PageView[k]) {
PageCount[j] = k;//找到,记录权重
break;
}
}
// 剩下的页面没有和当前物理块一样的,将其权重设为最大值
if (k == PageNum) {
PageCount[j] = PageNum;
}
}
// 找出权重最大的块
for(int a=1;a<BlockNum;a++) {
if(PageCount[a]>PageCount[temp])
temp=a;
}
Blocks[i][temp] = PageView[i];
MissingPageNum++;
flag = i;
}
output();
}
测试结果: