最近最久未使用(LRU)置换算法的C++实现

操作系统实验 最近最久未使用(LRU)置换算法

实验题目

  1. 最近最久未使用(LRU)置换算法原理就是:当需要淘汰某页面时,选择当前一段时间内最久未使用过的页先淘汰,即淘汰距当前最远的上次使用的页。

    • 例如: 分配给该进程的页块数为3,一个20位长的页面访问序列为:12560,36536,56042,70435,则缺页次数和缺页率按下图给出:

      image-20211207201735320

      注:原实验指导书上图示有误,该图为笔者制作。

  2. 假定分配给该进程的页块数为3,页面访问序列长度为20。本实验可以采用数组结构实现,首先随机产生页面序列,当发生请求调页时,若内存已满,则需要利用LRU算法,将当前一段时间内最久未使用过的页替换出去。

    • 模拟程序的算法如下图:

      image-20211207202046671

程序中使用的数据结构及符号说明

  1. 数组(数据结构)记录信息

    int memory[1001];      //记录进程内存中的页
    bool flag[1001];       //标记:访问的页面是否在内存中
    int l_time[1001];      //记录页面的最近使用时间
    int vis[1001];         //记录访问序列
    bool miss[1001];       //记录每次访问的缺页状态
    int state[1001][1001]; //记录每次访问后的进程的内存页面状态
    
  2. 整型变量

    int n;                 //进程内存的页块数
    int now_num = 0;       //进程内存当前的页个数
    int now_time = 0;      //当前时间(访问次数)
    int m;                 //页面访问序列的长度
    int miss_num = 0;      //缺页次数
    
  3. 缺页率

    double miss_rate;      //缺页率
    
  4. 自定义函数

    • void init():初始化
    • void LRU(int a):LRU算法的实现
    • void display():输出信息

源程序及注释

#include <iostream>
#include <queue>
#include <iomanip>
using namespace std;

int n;                 //进程内存的页块数
int now_num = 0;       //进程内存当前的页个数
int now_time = 0;      //当前时间(访问次数)
int m;                 //页面访问序列的长度
int miss_num = 0;      //缺页次数
double miss_rate;      //缺页率
int memory[1001];      //记录进程内存中的页
bool flag[1001];       //标记:访问的页面是否在内存中
int l_time[1001];      //记录页面的最近使用时间
int vis[1001];         //记录访问序列
bool miss[1001];       //记录每次访问的缺页状态
int state[1001][1001]; //记录每次访问后的进程的内存页面状态

//初始化
void init()
{
	cout << "请输入分配给该进程的页块数:";
	cin >> n;
	cout << "请输入页面访问序列的长度:";
	cin >> m;
	cout << "请输入访问序列:";
	for (int i = 0; i < m; i++)
	{
		cin >> vis[i];
	}
	//进程的内存中刚开始没有页,初始化为-1
	for (int i = 0; i < n; i++)
	{
		memory[i] = -1;
	}
	//在内存中的标记初始化为false
	for (int i = 0; i < 1000; i++)
	{
		flag[i] = false;
	}
	//页面的最近使用时间初始化为0
	for (int i = 0; i < 1000; i++)
	{
		l_time[i] = 0;
	}
	return;
}

//LRU算法
void LRU(int a)
{
	//检查请求访问的页是否在进程的内存中
	if (flag[a] == false)
	{
		miss[now_time] = 1; //此次标记:缺页
		miss_num++;
		if (now_num == n) //内存已满
		{
			int min_time = 0x3f3f3f, min_num = -1;
			//在内存中的页中,最早被访问时间和最久未使用过的页在内存中的编号
			for (int i = 0; i < now_num; i++)
			{
				if (l_time[memory[i]] < min_time)
				{
					min_time = l_time[memory[i]];
					min_num = i;
				}
			}
			flag[a] = true;            //将当前访问的页“是否在内存中”的标记设为1
			flag[memory[min_num]] = 0; //将被替换的页“是否在内存中”的标记设为0
			memory[min_num] = a;       //当前页替换最久未使用过的页
		}
		else //内存未满
		{
			flag[a] = true;
			memory[now_num] = a; //当前页进入内存的下一个位置
			now_num++;           //内存中页的个数加1
		}
	}
	else
	{
		miss[now_time] = 0; //标记:未缺页
	}
	//保存内存中页面的序列
	for (int i = 0; i < n; i++)
	{
		state[now_time][i] = memory[i];
	}
	l_time[a] = now_time; //更新当前页的被访问时间为当前时间
	now_time++;           //访问完一个页面,当前时间加1
	return;
}

void display()
{
	//输出表头
	cout << "|访问|";
	int left = n - n / 2 - 1;
	int right = n / 2;
	while (left--)
		cout << "    ";
	cout << "序列";
	while (right--)
		cout << "    ";
	cout << "|是否缺页|" << endl;
	for (int i = 0; i < m; i++)
	{
		cout << "|" << setw(4) << vis[i] << "|";
		for (int j = 0; j < n; j++)
		{
			if (state[i][j] != -1)
				cout << setw(4) << state[i][j];
			else
				cout << "    ";
		}
		if (miss[i] == 1) //输出“是”和“否”比较丑
			cout << "|    1   |" << endl;
		else
			cout << "|    0   |" << endl;
	}
	cout << "缺页次数:" << miss_num << endl;
	miss_rate = (double)miss_num / m;
	cout << "缺页率:" << miss_num << "/" << m << "=" << setprecision(2) << miss_rate << endl;
	return;
}

int main()
{
	init();
	for (int i = 0; i < m; i++)
	{
		LRU(vis[i]);
	}
	display();
	return 0;
}

程序运行时的初值及运行结果

初值

分配给该进程的页块数:3

页面访问序列的长度:20

访问序列:1 2 5 6 0 3 6 5 3 6 5 6 0 4 2 7 0 4 3 5

运行结果

image-20211221083746165

思考题

  • 比较LRU和其他置换算法各自的优缺点,能够实现其他置换算法模拟设计,分析内存页面数的变化对各种置换算法命中率。

FIFO算法

与前文LRU算法的实现不同的是,在FIFO算法中,l_time[1001]记录页面进入内存的时间。算法实现如下:

//FIFO算法
void FIFO(int a)
{
  //检查请求访问的页是否在进程的内存中
  if (flag[a] == false)
  {
  	miss[now_time] = 1; //此次标记:缺页
  	miss_num++;
  	if (now_num == n) //内存已满
  	{
  		int min_time = 0x3f3f3f, min_num = -1; //被替换的页的入内存时间最早的页和位置
  		//在内存中的页中,先进先出(将入内存时间最早的页移出内存)
  		for (int i = 0; i < now_num; i++)
  		{
  			if (l_time[memory[i]] < min_time)
  			{
  				min_time = l_time[memory[i]];
  				min_num = i;
  			}
  		}
  		flag[a] = true;            //将当前访问的页“是否在内存中”的标记设为1
  		flag[memory[min_num]] = 0; //将被替换的页“是否在内存中”的标记设为0
  		memory[min_num] = a;       //当前页替换最早入内存的页
  	}
  	else //内存未满
  	{
  		flag[a] = true;
  		memory[now_num] = a; //当前页进入内存的下一个位置
  		now_num++;           //内存中页的个数加1
  	}
  	l_time[a] = now_time; //更新当前页的入内存时间为当前时间
  }
  else
  {
  	miss[now_time] = 0; //标记:未缺页
  }
  //保存内存中页面的序列
  for (int i = 0; i < n; i++)
  {
  	state[now_time][i] = memory[i];
  }
  now_time++; //访问完一个页面,当前时间加1
  return;
}
运行结果

image-20211221084045884

NRU算法(CLOCK)

简单的NRU算法是给每一帧关联一个附加位,称为使用位。当某一页首次装入主存时,该帧的使用位设置为1;当该页随后再被访问到时,它的使用位置为0。

对于页替换算法,用于替换的候选帧集合看做一个循环缓冲区,并且有一个指针与之相关联。当某一页被替换时,该指针被设置成指向缓冲区中的下一帧。当需要替换一页时,操作系统扫描缓冲区,以查找使用位被置为0的一帧。每当遇到一个使用位为1的帧时,操作系统就将该位重新置为0;如果在这个过程开始时,缓冲区中所有帧的使用位均为0,则选择遇到的第一个帧替换;如果所有帧的使用位均为1,则指针在缓冲区中完整地循环一周,把所有使用位都置为0,并且停留在最初的位置上,替换该帧中的页。

int u[1001];      	   //记录页面的使用位
int point; 			   //指针

算法实现如下:

//NRU算法
void NRU(int a)
{
	//检查请求访问的页是否在进程的内存中
	if (flag[a] == false)
	{
		miss[now_time] = 1; //此次标记:缺页
		miss_num++;
		int num; //被替换的页的位置
		if (now_num == n) //内存已满
		{
			//指针遍历内存
			while (1)
			{
				if (u[memory[point]] == 1)
				{
					u[memory[point]] = 0; //将使用位为1的页面置为0
				}
				else //若使用位为0,被替换
				{
					num = point;
					flag[a] = true;        //将当前访问的页“是否在内存中”的标记设为1
					flag[memory[num]] = 0; //将被替换的页“是否在内存中”的标记设为0
					memory[num] = a;       //当前页替换最久未使用过的页
					break;
				}
				point++;
				if (point >= n - 1)
				{
					point %= (n - 1);
				}
			}
		}
		else //内存未满
		{
			flag[a] = true;
			memory[now_num] = a; //当前页进入内存的下一个位置
			u[now_num] = 1;	     //使用位:置为1
			now_num++;           //内存中页的个数加1
			point = now_num; 		 //指针指向下一位置
		}
	}
	else
	{
		miss[now_time] = 0; //标记:未缺页
		u[now_num] = 0;	//使用位:置为0
	}
	//保存内存中页面的序列
	for (int i = 0; i < n; i++)
	{
		state[now_time][i] = memory[i];
	}
	now_time++;           //访问完一个页面,当前时间加1
	return;
}
运行结果

image-20211221092549071

最佳置换算法(OPT)(理想置换算法)

无法预知一个进程中的若干页面哪一个最长时间不被访问,故无法实现。

各算法优缺点

最近最久未使用(LRU)

优点:由于考虑程序访问的时间局部性,一般能有较好的性能;实际应用多。
缺点:实现需要较多的硬件支持,会增加硬件成本。

先进先出(FIFO)

优点:先进先出算法实现简单,是最直观的一个算法。
缺点:先进先出的性能最差,因为与通常页面的使用规则不符合,所以实际应用少。

最近不用算法(NRU/CLOCK)

优点:近似于LRU算法,考虑了时间局部性,一般性能比LRU好。
缺点:同样需要较多的硬件支持,会增加硬件成本。

最佳置换算法(OPT)(理想置换算法)

缺点:最佳置换算法是一种理想化算法,具有较好的性能,但是实际上无法实现(无法预知一个进程中的若干页面哪一个最长时间不被访问);
优点:最佳置换算法可以保证获得最低的缺页率

  • 12
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yuhan_2001

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值