![58a7a5590fb08e82ca0f395c239bd7b0.png](https://i-blog.csdnimg.cn/blog_migrate/4773854511aa9c54251f0cce77149a4d.jpeg)
人在美国,刚下飞机!
小白第一次写知乎文章,如果有不准确的地方,请各位大神多多指教。
本次我要写的文章是操作系统的页面置换算法(OPT、FIFO、LRU、CLOCK)。
页面置换算法不是很难,想必搜我这种文章的应该是都懂原理。所以在这里,简单概括算法特点,主要是展示代码。
- 最佳置换算法——OPT(Optimal)置换算法
OPT是一种理想的置换算法。当要调入一页面而必须淘汰一个旧页面时,所淘汰的页面应该是以后不再访问的或距现在最长时间后再访问的页面,该置换算法的缺页中断率最低。根据其原理,我们不难发现OPT是需要知道现有作业的页面访问序列的,而实际情况中,我们不可能知道页面的访问序列的,因此OPT在实际中不可能实现,但我们常把它作为衡量其他置换算法效率的标准。
- 先进先出置换算法——FIFO(First In First Out)置换算法
FIFO算法总是淘汰最先进入主存的页面,该算法比较简单,但算法效率不高。
- 最近最久未用置换算法——LRU(Least Recently Used)置换算法
LRU算法总是淘汰最后一次访问时间距当前时间间隔最长的页面。该算法是一种通用算法,因而广泛被采用。
- 时钟(CLOCK)置换算法
该算法是采用环形链表表示进程所访问的页面(模仿圆形时钟),引入一个指针指向最早进入主存的页面(就像时钟指针指向某个时间一样),这样只需移动指针来查找满足条件的页面。首先,给每个进入页表的页面设置一个“访问位”,对于刚刚进入页表的页面“访问位”设置为“1”。每次发生缺页中断时,若指针当前指向的页面访问位为“0”,就将当前页面置换出去,并且指针从当前指向的页面改为指向页表中的下一个页面。若指针当前指向的页面访问位为“1”,那么就将该页面访问位从“1”改为“0”。若不发生缺页中断,则指针不移动,并且将需要访问页面在页表中的“访问号”改为“1”。
![a7ad14ee3eb1f76323ac1ecea47bb005.png](https://i-blog.csdnimg.cn/blog_migrate/2dcc04e7939cac97afee73287d3fb870.jpeg)
- 代码
#include<iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
class PRA
{
public:
int row; //页表中共有row个物理块
int column; //页面访问序列长度
int *QString; //声明一个指针
vector<vector<int>> PageTable; // 存储历史上所有的出现的页表
public:
PRA(int row,int column,int a[]);// 构造函数
tuple<vector<vector<int>>,int> OPT(void);
tuple<vector<vector<int>>,int> FIFO(void);
tuple<vector<vector<int>>,int> LRU(void);
tuple<vector<vector<int>>,int> CLOCK(void);
void Print(vector<vector<int>> b,int BreakPage);
};
// 初始化类
PRA::PRA(int row,int column,int a[])
{
this->row = row;
this->column = column;
QString = a;
}
tuple<vector<vector<int>>,int> PRA::OPT(void)
{
int position;
int max_value=0,max_key=0;
map<int,int> distince; // 用来存储页表中页面距再次访问该页面的序列间隔
vector<int> block; // 用来表示当前页表中的页面页号
int i = 1,counter = 0; // i用来跟踪需访问的页号,counter记录缺页次数
PageTable.clear(); // PageTable用来存储历史上所有的页表
block.push_back(QString[i-1]); // 页表为空时,直接把待访问的页面直接装入就好啦
PageTable.push_back(block); // 把当前页表保存到存储历史上所有页表的容器中
counter++; // 由于页表装入新的页面,缺页次数+1
block.clear(); // 清除当前页表,以便下一次页表存放所有的页面
// 页表还没有放满的情况
while(PageTable[i-1].size()<3 && i<this->column)
{
// 查看是否发生缺页中断
vector<int>::iterator ret;
ret = find(PageTable[i-1].begin(), PageTable[i-1].end(), QString[i]);
if(ret != PageTable[i-1].end()) // 没有发生缺页中断
{
for(int j=0;j < PageTable[i-1].size();j++)
block.push_back(PageTable[i-1][j]);
PageTable.push_back(block);
block.clear();
i++;
}
else // 发生缺页中断
{
for(int j=0;j < PageTable[i-1].size();j++)
block.push_back(PageTable[i-1][j]);
block.push_back(QString[i]);
PageTable.push_back(block);
block.clear();
counter++;
i++;
}
}
while(i < this->column) // 页表已经放满
{
vector<int>::iterator ret;
ret = find(PageTable[i-1].begin(), PageTable[i-1].end(), QString[i]);
if(ret != PageTable[i-1].end()) // 没有发生缺页中断
{
for(int j=0;j < PageTable[i-1].size();j++)
block.push_back(PageTable[i-1][j]);
PageTable.push_back(block);
block.clear();
i++;
}
else // 发生了缺页中断
{
counter++;
max_value = 0;
for(int j=0;j < PageTable[i-1].size();j++)
{
for(position=i+1;position<this->column;position++) // 求前一次页表中所有页面与再次访问该页面的序列间隔,以便确定缺页时,置换出哪一个页面
{
if(PageTable[i-1][j] == QString[position])
{
distince[PageTable[i-1][j]] = position-i+1;
// 记录存储页表中页面距再次访问该页面的序列间隔
break;
}
else
{
distince[PageTable[i-1][j]] = this->column;
// 前一次页表中的页面后续不会再次访问,就将其序列间隔设置为整个页面访问序列的长度,这样使得不再被访问的页面必定能够置换出去
}
}
if(distince[PageTable[i-1][j]] > max_value) // 求出前一次页表中页面再次被访问的最大序列间隔值
max_value = distince[PageTable[i-1][j]];
}
//找出距现在最长时间后再访问的页面
auto find_item = find_if(distince.begin(), distince.end(),[max_value](const map<int, int>::value_type item)
{
return item.second == max_value;
});
if (find_item!= distince.end()) // 找出最大间隔值对应的页面
{
max_key = (*find_item).first;
}
distince.clear();
for(int j=0;j < PageTable[i-1].size();j++)
{
if(PageTable[i-1][j]==max_key)
block.push_back(QString[i]);
else
block.push_back(PageTable[i-1][j]);
}
PageTable.push_back(block);
block.clear();
i++;
}
}
return make_tuple(PageTable,counter);
}
// FIFO实现很简单,就像队列一样,若发生缺页中断每次把最先进入页表中的页面置换出去,若不发生缺页中断当前页表就复制前一次页表中的页面就可以了tuple<vector<vector<int>>,int> PRA::FIFO(void)
{
vector<int> block;
PageTable.clear();
int i = 1,counter = 0;
block.push_back(QString[i-1]);
PageTable.push_back(block);
counter++;
block.clear();
while(PageTable[i-1].size()<3 && i<this->column)
{
vector<int>::iterator ret;
ret = find(PageTable[i-1].begin(), PageTable[i-1].end(), QString[i]);
if(ret != PageTable[i-1].end())
{
for(int j=0;j < PageTable[i-1].size();j++)
block.push_back(PageTable[i-1][j]);
PageTable.push_back(block);
block.clear();
i++;
}
else
{
for(int j=0;j < PageTable[i-1].size();j++)
block.push_back(PageTable[i-1][j]);
block.push_back(QString[i]);
PageTable.push_back(block);
block.clear();
counter++;
i++;
}
}
while(i < this->column)
{
vector<int>::iterator ret;
ret = find(PageTable[i-1].begin(), PageTable[i-1].end(), QString[i]);
if(ret != PageTable[i-1].end())
{
for(int j=0;j < PageTable[i-1].size();j++)
block.push_back(PageTable[i-1][j]);
PageTable.push_back(block);
block.clear();
i++;
}
else
{
counter++;
for(int j=1;j < PageTable[i-1].size();j++)
block.push_back(PageTable[i-1][j]);
block.push_back(QString[i]);
PageTable.push_back(block);
block.clear();
i++;
}
}
return make_tuple(PageTable,counter);
}
// LRU也很简单,每次把最新访问的页面,放置队列尾部,这样就可以和FIFO一样,缺页中断就把队首的页面置换出去就行了
tuple<vector<vector<int>>,int> PRA::LRU(void)
{
vector<int> block;
PageTable.clear();
int i = 1,counter = 0;
block.push_back(QString[i-1]);
PageTable.push_back(block);
counter++;
block.clear();
while(PageTable[i-1].size()<3 && i<this->column)
{
vector<int>::iterator ret;
ret = find(PageTable[i-1].begin(), PageTable[i-1].end(), QString[i]);
if(ret != PageTable[i-1].end())
{
for(int j=0;j < PageTable[i-1].size();j++)
{
if(PageTable[i-1][j] == QString[i])
continue;
else
block.push_back(PageTable[i-1][j]);
}
block.push_back(QString[i]);
PageTable.push_back(block);
block.clear();
i++;
}
else
{
for(int j=0;j < PageTable[i-1].size();j++)
block.push_back(PageTable[i-1][j]);
block.push_back(QString[i]);
PageTable.push_back(block);
block.clear();
counter++;
i++;
}
}
while(i < this->column)
{
vector<int>::iterator ret;
ret = find(PageTable[i-1].begin(), PageTable[i-1].end(), QString[i]);
if(ret != PageTable[i-1].end())
{
for(int j=0;j < PageTable[i-1].size();j++)
{
if(PageTable[i-1][j] == QString[i])
continue;
else
block.push_back(PageTable[i-1][j]);
}
block.push_back(QString[i]);
PageTable.push_back(block);
block.clear();
i++;
}
else
{
counter++;
for(int j=1;j < PageTable[i-1].size();j++)
block.push_back(PageTable[i-1][j]);
block.push_back(QString[i]);
PageTable.push_back(block);
block.clear();
i++;
}
}
return make_tuple(PageTable,counter);
}
// 部分变量未做注释的,可以查看OPT函数,一样的变量含义
tuple<vector<vector<int>>,int> PRA::CLOCK(void)
{
int mark,*p;
// mark用来标记指针指向的页面,由于我设置的3个物理块,所以可用mark%3来实现循坏,当然也可以改成mark%this->row(this->row=3嘛)。上述代码有提到row代表有几个物理块
vector<int> block;
map<int,int> ClockCount; // map映射页表中页面的“访问位”
PageTable.clear();
int i = 1,counter = 0;
block.push_back(QString[i-1]);
PageTable.push_back(block);
counter++;
block.clear();
// 页表还没有放满的情况
while(PageTable[i-1].size()<3 && i<this->column)
{
// 查看是否发生缺页中断
vector<int>::iterator ret;
ret = find(PageTable[i-1].begin(), PageTable[i-1].end(), QString[i]);
if(ret != PageTable[i-1].end())
{
for(int j=0;j < PageTable[i-1].size();j++)
block.push_back(PageTable[i-1][j]);
PageTable.push_back(block);
block.clear();
i++;
}
else
{
for(int j=0;j < PageTable[i-1].size();j++)
block.push_back(PageTable[i-1][j]);
block.push_back(QString[i]);
PageTable.push_back(block);
block.clear();
counter++;
i++;
}
}
for(int j=0;j<PageTable[i-1].size();j++)
ClockCount[PageTable[i-1][j]]=1;
mark=0;
while(i < this->column)
{
p = &PageTable[i-1][0]; // 指针p每次u dou都指向页表历史记录中前一次页表的首位
// 判断是否发生缺页中断
vector<int>::iterator ret;
ret = find(PageTable[i-1].begin(), PageTable[i-1].end(), QString[i]);
if(ret != PageTable[i-1].end()) // 发生缺页中断
{
for(int j=0;j < PageTable[i-1].size();j++)
block.push_back(PageTable[i-1][j]);
PageTable.push_back(block);
block.clear();
for(int j=0;j<PageTable[i-1].size();j++)
if(PageTable[i-1][j]==QString[i])
ClockCount[PageTable[i-1][j]]=1; // 页面进入页表时,其“访问位”为“1”
i++;
}
else
{
counter++;
int m;
while(true)
{
if(ClockCount[p[mark%3]]==0)
{
m=mark%3; // 记录被置换出去页面
ClockCount.erase(p[mark%3]); // 已经被置换出去的页面,需要在ClockCount映射表中删除
ClockCount[QString[i]] = 1; // 新加入的页面其“访问位”要设置 为“1”
mark++; // 指针指向下一个页面
break;
}
else
{
ClockCount[p[mark%3]] = 0; // 指针指向的页表中的页面,其“访问位”要设置为“0”
mark++;
}
}
for(int j=0;j < PageTable[i-1].size();j++)
{
if(j != m)
block.push_back(p[j]);
else
block.push_back(QString[i]); // 在被置换出去的页面位置方法新的页面
}
PageTable.push_back(block);
block.clear();
i++;
}
}
return make_tuple(PageTable,counter);
}
void PRA::Print(vector<vector<int>> b,int BreakPage)
{
for(int i=0;i<b.size();i++)
{
string s = "";
for(int j=0;j<b[i].size();j++)
{
s += to_string(b[i][j])+" ";
}
cout << s << endl;
}
cout << "发生缺页中断次数:" << BreakPage << ",缺页中断率为:" << to_string((BreakPage*100.0)/this->column+0.05).substr(0, 4) << "%" << endl;
}
int main()
{
system("chcp 65001");
// 初始化
vector<vector<int>> opt_b,fifo_b,lru_b,clock_b;
tuple<vector<vector<int>>,int> OPT_Tuple,FIFO_Tuple,LRU_Tuple,CLOCK_Tuple;
int OPT_BreakPage,FIFO_BreakPage,LRU_BreakPage,CLOCK_BreakPage;
int a[] = {6,7,6,5,9,6,8,9,7,6,9,6};
const int r=3,c=sizeof(a)/sizeof(a[0]);;
PRA pra(r,c,a);
// 调用最佳置换算法
cout << "-----最佳置换算法-----" << endl;
OPT_Tuple = pra.OPT();
opt_b = get<0>(OPT_Tuple);
OPT_BreakPage = get<1>(OPT_Tuple);
pra.Print(opt_b,OPT_BreakPage);
// 调用先进先出置换算法
cout << "-----先进先出置换算法-----" << endl;
FIFO_Tuple = pra.FIFO();
fifo_b = get<0>(FIFO_Tuple);
FIFO_BreakPage = get<1>(FIFO_Tuple);
pra.Print(fifo_b,FIFO_BreakPage);
// 调用最近最久未用置换算法
cout << "-----最近最久未用置换算法-----" << endl;
LRU_Tuple = pra.LRU();
lru_b = get<0>(LRU_Tuple);
LRU_BreakPage = get<1>(LRU_Tuple);
pra.Print(lru_b,LRU_BreakPage);
// 调用时钟置换算法
cout << "-----时钟置换算法-----" << endl;
CLOCK_Tuple = pra.CLOCK();
clock_b = get<0>(CLOCK_Tuple);
CLOCK_BreakPage = get<1>(CLOCK_Tuple);
pra.Print(clock_b,CLOCK_BreakPage);
}
- 结果
![ed5d4acda5717c821826426d30443a6f.png](https://i-blog.csdnimg.cn/blog_migrate/263c5af2c6c16b6f0c5436e28d2870f8.jpeg)
![e2951ae87f74321b783ce446728004c4.png](https://i-blog.csdnimg.cn/blog_migrate/ed9c30861194f89528229838db345ecc.jpeg)
背景图片来自舍友
部分理论来自我学校的教科书