实验五:页面置换算法实验
1.实验目的
本实验旨在深化对存储管理的理解,并掌握虚拟存储器的实现原理。通过观察和了解重要的页面置换算法及其置换过程,实验旨在提升对算法的理解。同时,通过模拟算法的编程实践,本实验旨在锻炼编程技巧,并通过分析实验数据来增强数据分析能力。
2. 实验环境
- 操作系统:Linux
- 编程语言:C++
- 编译工具:g++
3.实验过程
实验总览
- 编写
vmrp.h
和vmrp.cc
文件,分别定义和实现虚拟存储页置换类。 - 编写
Makefile
文件以简化编译和链接流程。 - 使用
make
命令编译生成可执行文件vmpr
。 - 运行
vmpr
命令,输入引用页数、引用串和内存页帧数,观察LRU和FIFO算法的页面置换效果和性能。 - 补充实现增强二次机会(Enhanced Clock)置换算法及Clock算法,Lfu算法和Mfu算法,并进行相应测试。
- 修改程序使其能够随机生成内存页面引用串,以便动态观测各种置换算法的性能。
在本实验中,通过编写示例程序模拟了LRU算法和FIFO算法。实验中对两种算法使用了不同的页面引用序列和任意帧实内存块数的组合进行测试,以展示页面置换的过程。实验能够统计和报告不同置换算法情况下的页号淘汰顺序、缺页次数(页错误数)和缺页率,从而比较两种置换算法在给定条件下的优劣。为了便于未来扩充页面置换算法,并更好地描述置换过程,实验程序采用了C++语言,并使用了Replace类来描述置换算法及其属性。
通过本实验,不仅加深了对存储管理的理解,还提升了编程技巧和分析能力。对页面置换算法有了更深入的认识,这将有助于未来的学习和研究。
头文件vmrp.h
/*
Filename : vmrp.h
*copyright :(C) 2006 by zhonghonglie
*Function :声明虚拟内存页置换类
*/
#include<iostream>
#include<iomanip>
#include <malloc.h>
class Replace{
public:
Replace();
~Replace();
void InitSpace(char*MethodName);//初始化页号记录
void Report(void); //报告算法执行情况
void Fifo(void);//先进先出算法
void Lru(void);//最近最旧未用算法
void Clock(void);//时钟(二次机会)置换算法
void Eclock(void);//增强二次机会置换算法
void Lfu(void);//最不经常使用置换算法
void Mfu(void);//最经常使用置换算法
private:
int* ReferencePage;//存放要访问到的页号
int * EliminatePage;//存放淘汰页号
int * PageFrames ;//存放当前正在实存中的页号
int PageNumber;//访问页数
int FrameNumber;//实存帧数
int FaultNumber;//失败页数
}
vmrp.cc
/*
Filename : vmrp.cc
copyright : (C) 2006 by zhonghonglie
*Function :模拟虚拟内存页置换算法的程序
*/
#include "vmrp.h"
using namespace std;
Replace::Replace()
{
int i;
//设定总得访问页数,并分配相应的引用页号和淘汰页号记录数组空间
cout << "Please input page numbers :" ;
cin >> PageNumber;
ReferencePage = new int [sizeof(int) * PageNumber];
EliminatePage = new int [sizeof(int) * PageNumber];
//输入引用页号序列(页面走向),初始化引用页数组
cout << "Please input reference page string :";
for (i =0;i<PageNumber;i++)
{
cin >>ReferencePage[i];//引用页暂存引用数组
}
//设定内存实页数(帧数),并分配相应的实页号记录数组空间(页号栈)
cout << "Please input page frames :";
cin>>FrameNumber;
PageFrames = new int [sizeof(int) * FrameNumber];
}
Replace::~Replace() { }
void Replace::InitSpace(char* MethodName)
{
int i;
cout << endl <<MethodName <<endl;
FaultNumber=0;
//引用还未开始,-1表示无引用页
for (i= 0; i< PageNumber; i++)
EliminatePage[i]=-1;
for(i= 0; i< FrameNumber; i++)
PageFrames[i]=-1;
}
//分析统计选择的算法对于当前输入的页面走向的性能
void Replace::Report(void)
{
//报告淘汰页顺序
cout << endl <<"Eliminate page:";
for(int i=0; EliminatePage[i]!=-1; i++) cout<<EliminatePage[i]<<"";
//报告缺页数和缺页率
cout<< endl << "Number of page faults = "<< FaultNumber << endl;
cout << setw(6)<<setprecision(3) ;
cout << "Rate of page faults = " <<100*(float)FaultNumber/(float)PageNumber <<"%"<<endl;
}
//最近最旧未用置换算法
void Replace::Lru(void)
{
int i,j,k,l,next;
InitSpace("LRU");
//循环装入引用页
for(k=0,l=0;k <PageNumber; k++)
{
next=ReferencePage[k];
//检测引用页当前是否已在实存
for (i=0; i<FrameNumber; i++)
{
if(next == PageFrames[i])
{
//引用页已在实存将其调整到页记录栈顶
next=PageFrames[i];
for(j=i;j>0;j--) PageFrames[j] = PageFrames[j-1];
PageFrames[0]=next;
break;
}
}
if(PageFrames[0] == next)
{
//如果引用页已放栈顶,则为不缺页,报告当前内存页号
for(j=0; j<FrameNumber; j++)
if(PageFrames[j]>=0)
cout <<PageFrames[j]<<" ";
cout << endl;
continue;//继续装入下一页
}else
//如果引用页还未放栈顶,则为缺页,缺页数加1
FaultNumber++;
//栈底页号记入淘汰页数组中
EliminatePage[l]= PageFrames[FrameNumber-1];
//向下压栈
for(j=FrameNumber-1;j>0;j--) PageFrames[j]=PageFrames[j-1];
PageFrames[0] = next; //引用页放栈顶
//报告当前实存中页号
for(j=0;j<FrameNumber;j++)
if(PageFrames[j]>=0)
cout<<PageFrames[j]<<" ";
if(EliminatePage[l]>=0)
cout<<"->"<<EliminatePage[l++]<<endl;
else
cout<<endl;
}
//分析统计选择的算法对于当前引用过的页面走向的性能
Report();
}
void Replace::Fifo(void)
{
int i,j,k,l,next;
InitSpace("FIFO");
//循环装入引用页
for(k=0,j=l=0;k<PageNumber;k++)
{
next = ReferencePage[k];
//如果引用页已在实存中,报告实存页号
for(i=0;i<FrameNumber;i++)
if(next==PageFrames[i]) break;
if(i<FrameNumber)
{
for(i=0;i<FrameNumber;i++) cout<<PageFrames[i]<<" ";
cout<<endl;
continue;//继续引用下一页
}
//引用页不在实存中,缺页数+1
FaultNumber++;
EliminatePage[l]=PageFrames[j];//最先入页号记入淘汰页数组
PageFrames[j]=next;//引用页号放最先入页号处
j= (j+1)%FrameNumber;//最先入页号循环下移
//报告当前实存页号和淘汰页号
for(i=0;i<FrameNumber;i++)
if(PageFrames[i]>=0) cout<<PageFrames[i]<<" ";
if(EliminatePage[l]>=0)
cout<<"->"<<EliminatePage[l++]<<endl;
else cout<<endl;
}
//分析统计选择的算法对于当前引用过的页面走向的性能
Report();
}
//未实现的其他页置换算法入口
void Replace::Clock(void){ }
void Replace::Eclock (void){ }
void Replace::Lfu(void){ }
void Replace::Mfu(void){ }
int main(int argc,char *argv[])
{
Replace * vmpr = new Replace();
vmpr->Lru();
vmpr->Fifo();
return 0;
}
Makefile文件
head=vmrp.h
srcs=vmrp.cc
objs=vmrp.o
opts=-w -g -c
all: vmrp
vmrp: $(objs)
g++ $(objs) -o vmrp
vmrp.o: $(srcs) $(head)
g++ $(opts) $(srcs)
clean:
rm vmrp *.o
执行make命令编译连接,生成可执行文件vmpr
gmake
g++ -g -c vmrp.cc vmrp.h
g++ vmrp.o -o vmrp
实验结果:
下面输出报告了FIFO和LRU两种算法的页置换情况。其中每一行数字为当前实存中的页号,->右边的数字表示当前被淘汰的页号。每种算法最后3行输出为:依次淘汰页号,缺页数,页出错率。
输入数据1:
引用页数:12
引用页序列:1 2 3 4 1 2 5 1 2 3 4 5
内存页框数:3
LRU算法输出:
FIFO算法输出:
再次执行vmpr命令,仍然输入Belady串,仅将页帧数改为4
输入数据2:
引用页数:12
引用页序列:1 2 3 4 1 2 5 1 2 3 4 5
内存页框数:4
LRU算法输出:
FIFO算法输出:
从以上输出中可以看出FIFO置换算法的Belady异常现象,即当在相同的引用串下内存页帧数从3帧增加到4帧,页出错率反而从75%增加到了83.3%。而在相同的情况下LRU置换算法无此异常现象
其他算法代码实现(Clock,Eclock,LFU,MFU)
//Clock算法实现
void Replace::Clock(void)
{
int i, j, k, l, next;
char MethodName[20] = "Clock";
InitSpace(MethodName);
bool useBit[FrameNumber] = {false}; // 使用位数组,记录每一页的访问情况
// 循环装入引用页
for (k = 0, j = l = 0; k < PageNumber; k++)
{
next = ReferencePage[k];
// 如果引用页已在实存中,报告实存页号,并且置使用位为1
for (i = 0; i < FrameNumber; i++)
{
if (next == PageFrames[i])
{
useBit[i] = true; // 设置使用位为1
break;
}
}
// 如果引用页在实存中,输出实存页号并继续引用下一页
if (i < FrameNumber)
{
for (i = 0; i < FrameNumber; i++)
{
if (PageFrames[i] >= 0)
cout << PageFrames[i] << " ";
}
cout << endl;
continue;
}
// 引用页不在实存中,缺页数加1
FaultNumber++;
while (true)
{
if (!useBit[j]) // 如果当前帧的使用位为0,则替换该帧
{
EliminatePage[l] = PageFrames[j]; // 将被淘汰的页号记录到淘汰页数组
PageFrames[j] = next; // 将引用页放入该帧
useBit[j] = false; // 设置使用位为1
j = (j + 1) % FrameNumber; // 循环下移指针
break;
}
else // 如果当前帧的使用位为1,则将使用位设置为0,继续查找下一个帧
{
useBit[j] = false;
j = (j + 1) % FrameNumber;// j表示当前指针,每次都从上次地方继续
}
}
// 报告当前实存页号和淘汰页号
for (i = 0; i < FrameNumber; i++)
{
if (PageFrames[i] >= 0)
cout << PageFrames[i] << " ";
}
if (EliminatePage[l] >= 0)
cout << "->" << EliminatePage[l++] << endl;
else
cout << endl;
}
// 分析统计选择的算法对于当前引用的页面走向的性能
Report();
}
//Eclock算法实现
void Replace::Eclock(void)
{
int i, j, k, l, next;
char MethodName[20] = "Eclock";
InitSpace(MethodName);
bool useBit[FrameNumber] = {false}; // 使用位数组,记录每一页的访问情况
bool modifyBit[FrameNumber] = {false}; // 修改位数组,记录每一页的修改情况
// 循环装入引用页
for (k = 0, j = l = 0; k < PageNumber; k++)
{
next = ReferencePage[k];
// 如果引用页已在实存中,报告实存页号,并且置使用位为1
for (i = 0; i < FrameNumber; i++)
{
if (next == PageFrames[i])
{
useBit[i] = true; // 设置使用位为1
break;
}
}
// 如果引用页在实存中,输出实存页号并继续引用下一页
if (i < FrameNumber)
{
for (i = 0; i < FrameNumber; i++)
{
if (PageFrames[i] >= 0)
cout << PageFrames[i] << " ";
}
cout << endl;
continue;
}
// 引用页不在实存中,缺页数加1
FaultNumber++;
while(true)
{
if (!useBit[j] && !modifyBit[j])
{
EliminatePage[l] = PageFrames[j]; // 将被淘汰的页号记录到淘汰页数组
PageFrames[j] = next; // 将引用页放入该帧
useBit[j] = false; // 设置使用位为1
j = (j + 1) % FrameNumber; // 循环下移指针
break;
}
else if(!useBit[j] && modifyBit[j]){
EliminatePage[l] = PageFrames[j]; // 将被淘汰的页号记录到淘汰页数组
PageFrames[j] = next; // 将引用页放入该帧
useBit[j] = false; // 设置使用位为1
j = (j + 1) % FrameNumber; // 循环下移指针
break;
}
else{
useBit[j]=false;
j=(j+1)%FrameNumber;
}
}
// 报告当前实存页号和淘汰页号
for (i = 0; i < FrameNumber; i++)
{
if (PageFrames[i] >= 0)
cout << PageFrames[i] << " ";
}
if (EliminatePage[l] >= 0)
cout << "->" << EliminatePage[l++] << endl;
else
cout << endl;
}
// 分析统计选择的算法对于当前引用的页面走向的性能
Report();
}
//Lfu算法实现
void Replace::Lfu(void){
int i,j,k,l,next;
int * reference = new int[sizeof(int) * PageNumber];//引用页
int * freq = new int[FrameNumber]; // 记录每个页面的访问频率
int min_freq_index;
char MethodName[20] = "LFU";
InitSpace(MethodName);
// 初始化频率
for(i = 0; i < FrameNumber; i++){
freq[i] = 0;
}
//将引用页装入
for(k = 0, l = 0; k < PageNumber; k++){
next = ReferencePage[k];
// 检测当前引用页是否已经在实存
for(i = 0; i < FrameNumber; i++){
if(next == PageFrames[i]){
freq[i]++; // 更新访问频率
break;
}
}
if(i < FrameNumber){
// 如果引用页不缺页,报告当前内存页号
for(j = 0; j < FrameNumber; j++){
if(PageFrames[j] >= 0)
cout << PageFrames[j] << " ";
}
cout << endl;
continue; // 继续装下一页
}
else {
// 引用页不在实存中,缺页
FaultNumber++;
// 找到访问频率最小的页面
min_freq_index = 0;
for(i = 1; i < FrameNumber; i++){
if(freq[i] < freq[min_freq_index]){
min_freq_index = i;
}
}
// 栈底页号计入淘汰页数组
EliminatePage[l] = PageFrames[min_freq_index];
// 将新页面加入栈顶
PageFrames[min_freq_index] = next;
freq[min_freq_index] = 1; // 新页面的访问频率为1
// 报告现在在实存中存在的页号
for(j = 0; j < FrameNumber; j++){
if(PageFrames[j] >= 0)
cout << PageFrames[j] << ' ';
}
// 报告当前已经淘汰的页号
if(EliminatePage[l] >= 0){
cout << " -> " << EliminatePage[l++] << endl;
} else {
cout << endl;
}
}
}
Report();
}
//Mfu算法实现
void Replace::Mfu(void){
int i,j,k,l,next;
int * reference = new int[sizeof(int) * PageNumber];//引用页
int * freq = new int[FrameNumber]; // 记录每个页面的访问频率
int max_freq_index;
char MethodName[20] = "MFU";
InitSpace(MethodName);
// 初始化频率
for(i = 0; i < FrameNumber; i++){
freq[i] = 0;
}
//将引用页装入
for(k = 0, l = 0; k < PageNumber; k++){
next = ReferencePage[k];
// 检测当前引用页是否已经在实存
for(i = 0; i < FrameNumber; i++){
if(next == PageFrames[i]){
freq[i]++; // 更新访问频率
break;
}
}
if(i < FrameNumber){
// 如果引用页不缺页,报告当前内存页号
for(j = 0; j < FrameNumber; j++){
if(PageFrames[j] >= 0)
cout << PageFrames[j] << " ";
}
cout << endl;
continue; // 继续装下一页
}
else {
// 引用页不在实存中,缺页
FaultNumber++;
// 找到访问频率最小的页面
max_freq_index = 0;
for(i = 1; i < FrameNumber; i++){
if(freq[i] > freq[max_freq_index]){
max_freq_index = i;
}
}
//如果此时页框有剩,将max值替换
for(i=0;i<FaultNumber;i++){
if(PageFrames[i]==-1){
max_freq_index=i;
}
}
// 栈底页号计入淘汰页数组
EliminatePage[l] = PageFrames[max_freq_index];
// 将新页面加入栈顶
PageFrames[max_freq_index] = next;
freq[max_freq_index] = 1; // 新页面的访问频率为1
// 报告现在在实存中存在的页号
for(j = 0; j < FrameNumber; j++){
if(PageFrames[j] >= 0)
cout << PageFrames[j] << ' ';
}
// 报告当前已经淘汰的页号
if(EliminatePage[l] >= 0){
cout << " -> " << EliminatePage[l++] << endl;
} else {
cout << endl;
}
}
}
Report();
}
输入数据1:
引用页数:12
引用页序列:1 2 3 4 1 2 5 1 2 3 4 5
内存页框数:3
Clock算法输出:
Eclock算法输出:
LFU算法输出:
MFU算法输出:
输入数据2:
引用页数:12
引用页序列:1 2 3 4 1 2 5 1 2 3 4 5
内存页框数:4
Clock算法输出:
Eclock算法输出:
LFU算法输出:
MFU算法输出:
生成随机的字符序列
srand(time(0));
for (int i = 0; i < PageNumber; i++)
ReferencePage[i] = rand() % 10; // 假设页面号在0-9之间
cout << "Generated reference page string: ";
for (int i = 0; i < PageNumber; i++)
cout << ReferencePage[i] << " ";
cout<<endl;
并在加入头文件
#include <cstdlib>
#include <ctime>
执行效果
4.实验数据处理及结论
测试不同引用串在不同实存帧的结果
测试固定引用串
输入数据1:
引用页数:12
引用页序列:1 2 3 4 1 2 5 1 2 3 4 5
内存页框数:3
结果如下:
算法 | 缺页次数 | 缺页率 |
---|---|---|
LRU | 10 | 83.3% |
FIFO | 9 | 75% |
Clock | 9 | 75% |
Eclock | 10 | 83.3% |
LFU | 9 | 75% |
MFU | 8 | 66.7% |
输入数据2:
引用页数:12
引用页序列:1 2 3 4 1 2 5 1 2 3 4 5
内存页框数:4
结果如下:
算法 | 缺页次数 | 缺页率 |
---|---|---|
LRU | 8 | 66.7% |
FIFO | 10 | 83.3% |
Clock | 10 | 83.3% |
Eclock | 8 | 66.7% |
LFU | 7 | 58.3% |
MFU | 8 | 66.7% |
测试随机生成引用串
输入数据1:
随机生成长度为12,页号在[1, 8]范围内的页面引用串:2 1 1 2 7 2 5 0 7 1 7 9
内存页框数:4
结果如下:
算法 | 缺页次数 | 缺页率 |
---|---|---|
LRU | 7 | 58.3% |
FIFO | 6 | 50% |
Clock | 6 | 50% |
Eclock | 7 | 58.3% |
LFU | 7 | 58.3% |
MFU | 6 | 50% |
根据典型的现象作出不同算法中帧数与缺页数的曲线图
分别输入页框数3到10,并得到各算法的缺页数,并绘制图表
下面是随机子串生成的数据表
页框数 | LRU | FIFO | Clock | Eclock | LFU | MFU |
---|---|---|---|---|---|---|
3 | 9 | 9 | 10 | 8 | 7 | 9 |
4 | 8 | 10 | 8 | 7 | 6 | 8 |
5 | 7 | 8 | 7 | 6 | 5 | 6 |
6 | 5 | 7 | 6 | 5 | 4 | 5 |
7 | 5 | 5 | 5 | 5 | 5 | 5 |
绘图代码
import matplotlib.pyplot as plt
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei'] # 使用SimHei显示中文
plt.rcParams['axes.unicode_minus'] = False # 确保负号显示正常
# 数据
frames = [3, 4, 5, 6]
lru = [9, 8, 7, 5]
fifo = [9, 10, 8, 7]
clock = [10, 8, 7, 6]
eclock = [8, 7, 6, 5]
lfu = [7, 6, 5, 4]
mfu = [9, 8, 7, 6]
# 绘制曲线
plt.figure(figsize=(10, 6))
plt.plot(frames, lru, marker='o', label='LRU')
plt.plot(frames, fifo, marker='o', label='FIFO')
plt.plot(frames, clock, marker='o', label='Clock')
plt.plot(frames, eclock, marker='o', label='Eclock')
plt.plot(frames, lfu, marker='o', label='LFU')
plt.plot(frames, mfu, marker='o', label='MFU')
# 图例与标题
plt.xlabel("页框数")
plt.ylabel("缺页次数")
plt.title("不同页面置换算法中页框数与缺页次数的关系")
plt.legend()
plt.grid(True)
plt.show()
图像如下图所示
现象:
FIFO置换算法:出现Belady异常现象,页帧数增加页出错率反而增加。
LRU算法: 能较好地贴合实际访问特性,缺页率整体较低,不会出现Belady异常。
其他算法随着页框数增加,缺页次数呈下降趋势
其他各算法的实现思路
Clock算法实现
Clock算法使用一个循环指针和一个使用位数组来管理页面置换。
useBit
数组用于记录每个页面的访问情况。- 如果页面在内存中,设置相应的使用位为1。
- 如果页面不在内存中,指针循环查找
useBit
为0的页面进行替换,并将该页面的useBit
设置为1。 - 被替换的页面号记录到
EliminatePage
数组中。
核心逻辑:
- 检查页面是否在内存中,若在,设置使用位并继续下一个页面。
- 若不在,增加缺页数,并使用循环指针查找
useBit
为0的页面进行替换。
Eclock算法实现
Enhanced Clock算法在Clock算法的基础上增加了一个修改位数组,用于更精细地控制页面置换。
useBit
和modifyBit
分别记录每个页面的访问情况和修改情况。- 当页面在内存中时,设置相应的使用位为1。
- 当页面不在内存中时,优先查找
useBit
和modifyBit
都为0的页面进行替换;如果找不到,则查找useBit
为0但modifyBit
为1的页面进行替换。
核心逻辑:
- 检查页面是否在内存中,若在,设置使用位并继续下一个页面。
- 若不在,增加缺页数,并优先替换
useBit
和modifyBit
都为0的页面;若没有,则替换useBit
为0但modifyBit
为1的页面。
LFU算法实现
LFU算法使用一个频率数组来记录每个页面的访问频率,并根据最少访问频率进行页面置换。
freq
数组用于记录每个页面的访问频率。- 当页面在内存中时,增加其访问频率。
- 当页面不在内存中时,查找访问频率最小的页面进行替换。
核心逻辑:
- 检查页面是否在内存中,若在,增加其访问频率并继续下一个页面。
- 若不在,增加缺页数,并查找访问频率最小的页面进行替换。
MFU算法实现
MFU算法类似于LFU,但它根据最高访问频率进行页面置换。
freq
数组用于记录每个页面的访问频率。- 当页面在内存中时,增加其访问频率。
- 当页面不在内存中时,查找访问频率最高的页面进行替换。
核心逻辑:
- 检查页面是否在内存中,若在,增加其访问频率并继续下一个页面。
- 若不在,增加缺页数,并查找访问频率最高的页面进行替换。
各算法各适应于怎样的页面引用串和页面帧数
LRU (Least Recently Used)算法
- 适用场景:适用于页面访问存在时间局部性(temporal locality)的情况,即最近访问的页面在未来仍有较大概率被访问。
- 页面引用串:适合于具有明显局部性的页面引用串。
- 页面帧数:在中等和较多页面帧数的情况下效果较好,因为能有效利用局部性减少缺页中断。
FIFO (First-In-First-Out)算法
- 适用场景:适用于访问模式比较简单且没有明显局部性的情况。FIFO通过先入先出的方式替换页面,容易产生“Belady’s Anomaly”,即增加页面帧数反而增加缺页次数。
- 页面引用串:适合于页面访问模式简单、均匀的页面引用串。
- 页面帧数:在较少页面帧数的情况下效果较好,因为算法简单,开销小。
Clock算法
- 适用场景:适用于访问模式较为均匀的情况下,即页面访问频率比较均匀分布的情况。因为它通过循环指针和使用位来实现页面替换,避免了频繁的缺页中断。
- 页面引用串:适合于没有明显局部性(locality)特征的页面引用串。
- 页面帧数:在中等数量的页面帧数下表现较好,能够避免频繁的页面替换。
Enhanced Clock算法
- 适用场景:适用于访问模式较为复杂的情况,比如有的页面频繁访问且频繁修改。相比Clock算法,增加了修改位,使得页面置换更加精确。
- 页面引用串:适合于具有明显读写操作差异的页面引用串。
- 页面帧数:在中等数量的页面帧数下表现较好,能够进一步减少频繁的页面替换.
LFU算法
- 适用场景:适用于页面访问频率存在明显差异的情况。它通过记录每个页面的访问频率,选择访问频率最低的页面进行替换。
- 页面引用串:适合于有明显局部性(某些页面长期频繁访问)的页面引用串。
- 页面帧数:在较多页面帧数的情况下效果较好,因为更多的页面帧可以保留高频率访问的页面,减少缺页中断。
MFU算法
-
适用场景:适用于需要替换访问频率较高页面的情况,这种情况较为少见,但在某些特殊应用场景中可能有效。
-
页面引用串:适合于那些访问频率不断变化,没有明显局部性的页面引用串。
FIFO (First-In-First-Out)算法** -
适用场景:适用于访问模式比较简单且没有明显局部性的情况。FIFO通过先入先出的方式替换页面,容易产生“Belady’s Anomaly”,即增加页面帧数反而增加缺页次数。
-
页面引用串:适合于页面访问模式简单、均匀的页面引用串。
-
页面帧数:在较少页面帧数的情况下效果较好,因为算法简单,开销小。
Clock算法
- 适用场景:适用于访问模式较为均匀的情况下,即页面访问频率比较均匀分布的情况。因为它通过循环指针和使用位来实现页面替换,避免了频繁的缺页中断。
- 页面引用串:适合于没有明显局部性(locality)特征的页面引用串。
- 页面帧数:在中等数量的页面帧数下表现较好,能够避免频繁的页面替换。
Enhanced Clock算法
- 适用场景:适用于访问模式较为复杂的情况,比如有的页面频繁访问且频繁修改。相比Clock算法,增加了修改位,使得页面置换更加精确。
- 页面引用串:适合于具有明显读写操作差异的页面引用串。
- 页面帧数:在中等数量的页面帧数下表现较好,能够进一步减少频繁的页面替换.
LFU算法
- 适用场景:适用于页面访问频率存在明显差异的情况。它通过记录每个页面的访问频率,选择访问频率最低的页面进行替换。
- 页面引用串:适合于有明显局部性(某些页面长期频繁访问)的页面引用串。
- 页面帧数:在较多页面帧数的情况下效果较好,因为更多的页面帧可以保留高频率访问的页面,减少缺页中断。
MFU算法
- 适用场景:适用于需要替换访问频率较高页面的情况,这种情况较为少见,但在某些特殊应用场景中可能有效。
- 页面引用串:适合于那些访问频率不断变化,没有明显局部性的页面引用串。
- 页面帧数:在较少页面帧数的情况下表现较好,因为频繁访问的页面被替换,减少了低频页面的干扰。