虚拟内存页面置换算法实验(OPT、FIFO、LRU)
进程运行时,若其访问的页面不再内存中而需将其调入,但内存已无空闲空间时,就需要从内存中调出一页程序或数据,送入磁盘的对换区。选择调出页面的算法就称为页面置换算法。
好的页面置换算法应有较低的缺页率,也就是说,应将以后不会再访问或以后较长时间内不会再访问的页面先调出。
实验目标
- 理解虚拟内存的基本工作原理;
- 掌握虚拟内存页面置换的相关概念;
- 掌握先进先出FIFO,最佳置换OPT和最近最久未使用LRU页面置换算法的实现方法。
实验内容
设计程序模拟先进先出FIFO、最佳置换OPT、最近最久未使用LRU页面置换算法的工作过程。假设内存中分配给每个进程的最小物理块数为m,在进程运行过程中要访问的页面个数为n,页面访问序列为P1, …, Pn,分别利用不同的页面置换算法调度进程的页面访问序列,给出页面访问序列的置换过程,计算每种算法缺页次数和缺页率。
输入:最小物理块数m,页面个数n,页面访问序列P1, …, Pn。
输出:每次页面访问时,物理块对应的页面序号;该方法下的缺页次数和缺页率。
最佳(OPT)置换算法
最佳(Optimal,OPT)置换算法选择的被淘汰的页面是以后永不使用的页面,或是在最长时间内不再被访问的页面,以便保证获得最低的缺页率。(然而,由于人们目前无法预知进程在内存下的若干页面中哪个是未来最长时间内不再被访问的,因而该算法无法实现)。
先进先出(FIFO)页面置换算法
先进先出页面置换算法优先淘汰最早进入内存的页面,即在内存中驻留时间最久的页面。该算法实现简单,只需把调入内存的页面根据先后次序链接成队列,设置一个指针总指向最早的页面。但该算法与进程实际运行时的规律不适应,因为在进程中,有的页面经常被访问。
FIFO算法还会产生所分配的物理块数增大而缺页数不减反增的异常现象,这由Belady于1969年发现,因此称为Belady异常。只有FIFO算法可能出现Belady异常,LRU和OPT算法永远不会出现Belady异常。
最近最久未使用(LRU)置换算法
最近最久未使用置换算法选择最近最长时间未访问过的页面予以淘汰,它认为过去一段时间内未访问过的页面,在最近的将来可能也不会被访问。该算法为每个页面设置一个访问字段,来记录页面自上次被访问以来所经历的时间,淘汰页面时选择页面中值最大的予以淘汰。
LRU算法本质上是“向前看”的,而最佳置换算法则是根据各页以后的使用情况“向后看”的。
程序设计
实验过程和结果分析
- 物理块数 m = 3 m=3 m=3
- 访问次数 n = 20 n=20 n=20
- 访问序列 P = [ 7 , 0 , 1 , 2 , 0 , 3 , 0 , 4 , 2 , 3 , 0 , 3 , 2 , 1 , 2 , 0 , 1 , 7 , 0 , 1 ] P=[7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2, 1, 2, 0, 1, 7, 0, 1] P=[7,0,1,2,0,3,0,4,2,3,0,3,2,1,2,0,1,7,0,1]
页面置换算法 | 缺页率 |
---|---|
OPT | 0.450 |
FIFO | 0.750 |
LRU | 0.600 |
不难发现,OPT的缺页率最低,性能最好,但是实际中的“前瞻”不能实现。LRU的缺页率次之,是比较理想的页面置换算法。FIFO的缺页率最高,效果最差,因为被换出的页面可以被频繁访问。
OPT
OPT:
7: 7 - - (miss)
0: 7 0 - (miss)
1: 7 0 1 (miss)
2: 2 0 1 (miss)
0: 2 0 1 (hit)
3: 2 0 3 (miss)
0: 2 0 3 (hit)
4: 2 4 3 (miss)
2: 2 4 3 (hit)
3: 2 4 3 (hit)
0: 2 0 3 (miss)
3: 2 0 3 (hit)
2: 2 0 3 (hit)
1: 2 0 1 (miss)
2: 2 0 1 (hit)
0: 2 0 1 (hit)
1: 2 0 1 (hit)
7: 7 0 1 (miss)
0: 7 0 1 (hit)
1: 7 0 1 (hit)
The rate of missing page is 0.450
FIFO
FIFO:
7: 7 - - (miss)
0: 7 0 - (miss)
1: 7 0 1 (miss)
2: 2 0 1 (miss)
0: 2 0 1 (hit)
3: 2 3 1 (miss)
0: 2 3 0 (miss)
4: 4 3 0 (miss)
2: 4 2 0 (miss)
3: 4 2 3 (miss)
0: 0 2 3 (miss)
3: 0 2 3 (hit)
2: 0 2 3 (hit)
1: 0 1 3 (miss)
2: 0 1 2 (miss)
0: 0 1 2 (hit)
1: 0 1 2 (hit)
7: 7 1 2 (miss)
0: 7 0 2 (miss)
1: 7 0 1 (miss)
The rate of missing page is 0.750
LRU
LRU:
7: 7 - - (miss)
0: 7 0 - (miss)
1: 7 0 1 (miss)
2: 2 0 1 (miss)
0: 2 0 1 (hit)
3: 2 0 3 (miss)
0: 2 0 3 (hit)
4: 4 0 3 (miss)
2: 4 0 2 (miss)
3: 4 3 2 (miss)
0: 0 3 2 (miss)
3: 0 3 2 (hit)
2: 0 3 2 (hit)
1: 1 3 2 (miss)
2: 1 3 2 (hit)
0: 1 0 2 (miss)
1: 1 0 2 (hit)
7: 1 0 7 (miss)
0: 1 0 7 (hit)
1: 1 0 7 (hit)
The rate of missing page is 0.600
输出截图
Belady 异常
- 物理块数 m 1 = 3 , m 2 = 4 m_1=3, m_2=4 m1=3,m2=4
- 访问次数 n = 12 n=12 n=12
- 访问序列 P = [ 3 , 2 , 1 , 0 , 3 , 2 , 4 , 3 , 2 , 1 , 0 , 4 ] P=[3, 2, 1, 0, 3, 2, 4, 3, 2, 1, 0, 4] P=[3,2,1,0,3,2,4,3,2,1,0,4]
在使用FIFO页面置换算法时,可能会出现Belady异常。
m | 缺页率 | 缺页次数 |
---|---|---|
3 | 0.750 | 9 |
4 | 0.833 | 10 |
附录:代码
#include <iostream>
using namespace std;
class Base {
public:
Base(int m, int n, int *p) :
m(m), n(n), p(p), missed(0),
field(new int[m]{}),
blocks(new int[m]) {
for (int j = 0; j < m; ++j)
blocks[j] = -1;
}
protected:
int m;
int n;
int *p; // 页面调用序列
int i{}; // 页面调用序列的指针
int missed; // 缺页次数
int *field; // 访问字段
int *blocks; // 物理块
void run(); // 运行函数
private:
float rateOfMissingPage{}; // 缺页率
bool isHit(); // 检查是否命中
virtual int blockSelect() = 0; // 换出页面选择函数
virtual void printAlgorithmName() const = 0;
void pageExchange(int blockInd, int page); // 页面换出函数
void updateField(); // 更新访问字段
void printBlocks();
void printRateOfMissingPage() const;
};
void Base::pageExchange(int blockInd, int page) {
blocks[blockInd] = page;
}
void Base::printBlocks() {
int t;
for (int j = 0; j < m; ++j) {
t = blocks[j];
if (t >= 0) printf("%d ", t);
else printf("- ");
}
}
bool Base::isHit() {
int page = p[i];
for (int j = 0; j < m; ++j)
if (page == blocks[j]) {
// 命中则修改访问字段
field[j] = 0;
return true;
}
return false;
}
void Base::run() {
int blockInd;
printAlgorithmName();
for (i = 0; i < n; ++i) {
updateField();
// 尚有空闲物理块
if (i < m) {
blocks[i] = p[i];
blockInd = i;
} else {
// 命中
if (isHit()) {
printf("\033[32m%d: ", p[i]);
printBlocks();
printf("(hit)\033[0m\n");
continue;
} else { // 未命中
blockInd = blockSelect();
pageExchange(blockInd, p[i]);
}
}
printf("\033[31m%d: ", p[i]);
printBlocks();
printf("(miss)\033[0m\n");
++missed;
field[blockInd] = 0;
}
rateOfMissingPage = (float) missed / (float) n;
printRateOfMissingPage();
}
void Base::printRateOfMissingPage() const {
printf("The rate of missing page is %.3f\n", rateOfMissingPage);
}
void Base::updateField() {
for (int j = 0; j < m; ++j)
++field[j];
}
class OPT : public Base {
public:
OPT(int m, int n, int *p) : Base(m, n, p) { run(); }
private:
int blockSelect() override {
int j, k;
int obj_page;
int look_back = 0;
for (j = 0; j < m; ++j) {
for (k = i + 1; k < n; ++k) {
if (blocks[j] == p[k]) {
if (k > look_back) {
obj_page = j;
look_back = k;
}
break;
}
}
if (k == n) {
obj_page = j;
break;
}
}
return obj_page;
}
void printAlgorithmName() const override {
cout << "OPT: " << endl;
}
};
class FIFO : public Base {
public:
FIFO(int m, int n, int *p) : Base(m, n, p) { run(); }
private:
int blockSelect() override {
return (missed) % m;
}
void printAlgorithmName() const override {
cout << "FIFO: " << endl;
}
};
class LRU : public Base {
public:
LRU(int m, int n, int *p) : Base(m, n, p) { run(); }
private:
int blockSelect() override {
int tag = 0;
int obj_page;
for (int j = 0; j < m; ++j) {
if (tag < field[j]) {
tag = field[j];
obj_page = j;
}
}
return obj_page;
}
void printAlgorithmName() const override {
cout << "LRU: " << endl;
}
};
int main() {
int m = 3;
int P[] = {3, 2, 1, 0, 3, 2, 4, 3, 2, 1, 0, 4};
int n = sizeof(P) / sizeof(int);
// OPT opt(m, n, P);
FIFO fifo1(3, n, P);
FIFO fifo2(4, n, P);
// LRU lru(m, n, P);
return 0;
}
参考文献
- 2023年操作系统考研复习指导/王道论坛考研组编.——北京:电子工业出版社,2021.12