目录
前言
在上一篇简单介绍了5种算法,和代码总体的设计思路,与c++较c语言优势等。可以点击链接跳转:操作系统 实验9:页面置换算法模拟设计 (FIFO)(LRU)(OPT)(LFR)(NUR) --使用c++实现 (上) - CSDN App】
这篇博客将深入去看这几种算法的实现,将剩余部分结束。
核心部分
先进先出的算法(FIFO)
代码
这里有关链表的部分,不涉及增删,所以使用静态链表即可,通过索引进行访问。
先进先出底层就是一个循环单指针队列,通过earliest 指针取余实现循环。一切都非常简单,这是最简单的也是最早出现的页面置换算法。
class FIFO : public User
{
public:
FIFO(int memSize)
: User(memSize)
{
}
bool Once(int incId) override
{
int pageId = incId / _1K_;
for (int i = 0; i < memoryCapacity; i++)
{
if (pageId == memory[i])
{
haveRep = false;
return haveRep;
}
}
lastRep = memory[earliest];
memory[earliest] = pageId;
haveRep = true;
lastRepNum = earliest;
earliest = (earliest + 1) % memoryCapacity;
return haveRep;
}
private:
int earliest = 0;
protected:
std::string Subjoin() const override
{
return std::string("\n(FIFO) 现在最早进入的页是第") +
std::to_string(earliest + 1) + "个";
}
};
运行
主函数在上一篇博客中上过了,这里我直接放运行后的结果了。
最近最久未使用算法(LRU)
代码
故名思意,就是在内存当中距离现在-上一次访问在最早之前的页被替换掉,给每个节点定义了一个计时器,与先进先出的算法唯一的不一样就是,一个是选择进入时间最早,一个是选择等待时间最长,其他部分完全一样。
需要注意的是先进先出算法因为是循环队列,所以完全可以只输出当前指针位置就行了,但是LRU如果只输出指针会非常不直观,所以就以表格对其的方式输出,美观又大气,一目了然。
class LRU : public User // 最近最久未使用算法
{
public:
LRU(int memSize)
: User(memSize)
, time(new int[memSize])
{
}
~LRU()
{
delete[] time;
}
bool Once(int incId) override
{
int pageId = incId / _1K_;
haveRep = true;
for (int i = 0; i < memoryCapacity; i++)
{
time[i]++;
if (pageId == memory[i])
{
time[i] = 0;
haveRep = false;
}
}
if (haveRep)
{
int repId = 0;
for (int i = 1; i < memoryCapacity; i++)
{ // 找到访问时间间隔最大的进行替换
if (time[i] > time[repId])
{
repId = i;
}
}
lastRepNum = repId;
lastRep = memory[repId];
memory[repId] = pageId;
time[repId] = 0;
}
return haveRep;
}
private:
int* time; // 上次访问时间间隔
protected:
std::string Subjoin() const override
{
std::string str = "\n(LRU)";
int width = 3;
for (int i = 0; i < memoryCapacity; i++)
{
str += " | ";
std::string numberStr = std::to_string(time[i]);
int padding = 3 - numberStr.length();
str += std::string(padding, ' ') + numberStr;
}
return str + " | (离上次访问时间)";
}
};
运行
最近最少访问页面算法(LFR)
代码
LFR和LRU同样也是非常相似,唯一的差别就是一个是记录上一次访问距离现在的时间,一个是记录访问次数。
放在代码上看就是LRU是被访问的计数归0,其他都要+1,而LFR是被访问的次数+1,其他不变,最低的置换要置1 。
class LFU : public User // 最少使用置换算法
{
public:
LFU(int memSize)
: User(memSize)
, times(new int[memSize])
{
}
~LFU()
{
delete[] times;
}
bool Once(int incId) override
{
int pageId = incId / _1K_;
for (int i = 0; i < memoryCapacity; i++)
{
if (pageId == memory[i])
{
times[i]++;
haveRep = false;
return haveRep;
}
}
int repId = 0;
for (int i = 1; i < memoryCapacity; i++)
{ // 找到访问次数最少的进行替换
if (times[i] < times[repId])
{
repId = i;
}
}
lastRepNum = repId;
lastRep = memory[repId];
memory[repId] = pageId;
times[repId] = 1;
haveRep = true;
return haveRep;
}
private:
int* times; // 访问次数
protected:
std::string Subjoin() const override
{
std::string str = "\n(LFU)";
int width = 3;
for (int i = 0; i < memoryCapacity; i++)
{
str += " | ";
std::string numberStr = std::to_string(times[i]);
int padding = 3 - numberStr.length();
str += std::string(padding, ' ') + numberStr;
}
return str + " | (访问次数)";
}
};
运行
最近未用过算法(NUR)
代码
NUR算法是LFR的改版代码,LFR记录访问次数,而NUR是记录是否在前一个周期中访问过了。LFR的计数器是很多位,但是NUR的是标志位仅仅1位。NUR的一个周期是转一圈,和FIFO一样都是采用循环队列。
NUR会循环遍历标志位,如果为1则清0,如果为0则置换。
class NRU : public User // 最近未用算法
{
public:
NRU(int memSize)
: User(memSize)
, flag(new bool[memSize])
, last(0)
{
memset(flag, 0, sizeof(bool) * memSize);
}
~NRU()
{
delete[] flag;
}
bool Once(int incId) override
{
int pageId = incId / _1K_;
for (int i = 0; i < memoryCapacity; i++)
{
if (pageId == memory[i])
{
flag[i] = true;
haveRep = false;
return haveRep;
}
}
while (true)
{
last %= memoryCapacity;
for (; last < memoryCapacity; last++)
{ // 找到标志位为0的进行替换
if (!flag[last])
{
flag[last] = true;
lastRepNum = last;
lastRep = memory[last];
memory[last] = pageId;
haveRep = true;
last++;
return haveRep;
}
else
flag[last] = false;
}
}
}
private:
bool* flag; // 访问标志位
int last;
protected:
std::string Subjoin() const override
{
std::string str = "\n(NRU)";
int width = 3;
for (int i = 0; i < memoryCapacity; i++)
{
str += " | ";
std::string numberStr = std::to_string(flag[i]);
int padding = 3 - numberStr.length();
str += std::string(padding, ' ') + numberStr;
}
return str + " | (标志位)";
}
};
运行
最佳页面置换算法(OPT)
最佳页面置换算法是一种基于未来的算法,它不可实现,但是可以作为评估其他页面置换算法的标准。
选择在最久远未来才会访问的或者以后都不访问了的页进行置换,这样可以最大程度的命中,所以是”最佳“。
前面的四种算法都是基于过去的,但是这个算法是基于未来的,所以参数需要将未来的所有指令序列传送进来,实现方法页比较复杂,也有其他方法实现,
思路
首先需要一个count计数直到当前已经执行到哪一条指令了,从当前指令向后遍历,每一个指令映射的页地址与内存中比较,是否有相同的,有相同的则将标志位标记上已经发现未来需要几步会执行到,直到仅剩下一个未标记的内存中的页,就是最远未来才会遇到的,可以替换掉。如果遍历到结束也没有标记满,说明未被标记的内存页以后再也不会访问,可以替换掉。
代码
class OPT : public User // 最近未用算法
{
public:
OPT(int memSize, IncSeq& incSeq)
: User(memSize)
, incSeq(incSeq)
, nextVst(new int[memSize])
{
memset(nextVst, -1, sizeof(int) * memoryCapacity);
}
~OPT()
{
delete[] nextVst;
}
bool Once(int incId) override
{
ownCount++;
int pageId = incId / _1K_;
for (int i = 0; i < memoryCapacity; i++)
{
if (pageId == memory[i])
{
haveRep = false;
return haveRep;
}
}
NextVisit();
int repId = 0;
for (int i = 1; i < memoryCapacity; i++)
{ // 找到最久后才访问的进行替换
if (nextVst[i] < nextVst[repId])
{
repId = i;
}
}
lastRepNum = repId;
lastRep = memory[repId];
memory[repId] = pageId;
haveRep = true;
return haveRep;
}
private:
IncSeq& incSeq; // 指令序列
int* nextVst;
int ownCount = 0;
void NextVisit()
{
memset(nextVst, -1, sizeof(int) * memoryCapacity);
bool flag[memoryCapacity];
memset(flag, 0, sizeof(bool) * memoryCapacity);
int num = memoryCapacity, ct = ownCount;
while (num != 1 && ct < incSeq.Size())
{
int page = incSeq[ct++] / _1K_;
for (int i = 0; i < memoryCapacity; i++)
{
if (!flag[i] && page == memory[i])
{
flag[i] = true;
nextVst[i] = ct - ownCount + 1;
num--;
}
}
}
}
protected:
std::string Subjoin() const override
{
for (int i = 0; i < memoryCapacity; i++)
if (nextVst[i] > 0)
nextVst[i]--;
else if (nextVst[i] == 0)
{
nextVst[i] = -2;
for (int j = ownCount + 1; j < incSeq.Size(); j++)
{
if (memory[i] == incSeq[j] / _1K_)
{
nextVst[i] = j - ownCount + 1;
break;
}
}
}
if (haveRep)
{
nextVst[lastRepNum] = -1;
for (int i = ownCount + 1; i < incSeq.Size(); i++)
{
if (memory[lastRepNum] == incSeq[i] / _1K_)
{
nextVst[lastRepNum] = i - ownCount;
break;
}
}
}
std::string str = "\n(OPT)";
int width = 3;
for (int i = 0; i < memoryCapacity; i++)
{
str += " | ";
std::string numberStr = std::to_string(nextVst[i]);
int padding = 3 - numberStr.length();
str += std::string(padding, ' ') + numberStr;
}
return str + " | (到下次访问的距离)";
}
};