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

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

  • 题目要求
      • 输入要求
      • 输出要求
  • 编程平台
  • 实验成果
      • 开始模拟
      • 错误输入
      • 退出程序
  • 代码实现
      • 抽象数据类型定义
      • 指令地址流生成
      • 指令地址流到页地址流的转换
      • 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”即可运行。

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