【操作系统 - 5】虚拟内存页面置换算法

操作系统系列

  学习至此,发现很多学了但很久没用的知识,久而久之,慢慢遗忘。等哪天还需要的话,却发现已经忘得差不多了,即使整理了文档(word等),还是得从头再学一遍。读研第一学期,发现很多东西都可以从博客上学习到,也有不少博主呕心沥血整理了挺多有用的博文。于是,本人借此契机,也慢慢开始整理一些博文,不断改进完善中。整理博文(IT)有如下目的:

  • 首要目的:记录“求学生涯”的所学所悟,不断修改,不断更新!(有读者的互动)
  • 其次目的:在这“开源”的时代,整理并分享所学所悟是一种互利的行为!

博文系列:操作系统课程所学相关算法

6个实验相关代码的下载地址:http://download.csdn.net/detail/houchaoqun_xmu/9865648

-------------------------------

虚拟内存页面置换算法

一、概念介绍和案例解析 - 页面置换算法

  • 最佳(Optimal)置换算法:
  最佳置换算法是由Belady于1966年提出的一种理论上的算法。其所选择的被淘汰页面,将是以后永不使用的,或是在最长(未来)时间内不再被访问的页面。采用最佳置换算法,通常可保证获得最低的缺页率。
  但由于人们目前还无法预知一个进程在内存的若干个页面中,哪一个页面是未来最长时间内不再被访问的,因而该算法是无法实现的,但可以利用该算法去评价其它算法。现举例说明如下。 

  假定系统为某进程分配了三个物理块,并考虑有以下的页面号引用串:

7,0,1,2,0,3,0,4,2,3,0,3,2,1,2,0,1,7,0,1
  进程运行时,先将7,0,1三个页面装入内存。以后,当进程要访问页面2时,将会产生缺页中断。此时OS根据最佳置换算法,将选择页面7予以淘汰。这是因为页面0将作为第5个被访问的页面,页面1是第14个被访问的页面,而页面7则要在第18次页面访问时才需调入。下次访问页面0时,因它已在内存而不必产生缺页中断。当进程访问页面3时,又将引起页面1被淘汰;因为,它在现有的1,2,0三个页面中,将是以后最晚才被访问的。图4-26示出了采用最佳置换算法时的置换图。由图可看出,采用最佳置换算法发生了6次页面置换。

  • 先进先出(FIFO)页面置换算法:
  这是最早出现的置换算法。该算法总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面予以淘汰。
  该算法实现简单,只需把一个进程已调入内存的页面,按先后次序链接成一个队列,并设置一个指针,称为替换指针,使它总是指向最老的页面。
  但该算法与进程实际运行的规律不相适应,因为在进程中,有些页面经常被访问,比如,含有全局变量、常用函数、例程等的页面,FIFO算法并不能保证这些页面不被淘汰。
  这里,我们仍用上面的例子,但采用FIFO算法进行页面置换(图4-27)。当进程第一次访问页面2时,将把第7页换出,因为它是最先被调入内存的;在第一次访问页面3时,又将把第0页换出,因为它在现有的2,0,1 三个页面中是最老的页。由图4-27 可以看出,利用FIFO算法时进行了12次页面置换,比最佳置换算法正好多一倍。 
Belady(抖动)现象:采用FIFO算法时,如果对一个进程未分配它所要求的全部页面,有时就会出现分配的页面数增多,缺页率反而提高的异常现象。
Belady现象的描述:一个进程P要访问M个页,OS分配N个内存页面给进程P;对一个访问序列S,发生缺页次数为PE(S,N)。当N增大时,PE(S, N)时而增大,时而减小。
Belady现象的原因:FIFO算法的置换特征与进程访问内存的动态特征是矛盾的,即被置换的页面并不是进程不会访问的。

  • 最近最久未使用(LRU)置换算法:
LRU(Least Recently Used)置换算法的描述:
  FIFO置换算法性能之所以较差,是因为它所依据的条件是各个页面调入内存的时间,而页面调入的先后并不能反映页面的使用情况。最近最久未使用(LRU)的页面置换算法,是根据页面调入内存后的使用情况进行决策的。由于无法预测各页面将来的使用情况,只能利用“最近的过去”作为“最近的将来”的近似,因此,LRU置换算法是选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间t,当须淘汰一个页面时,选择现有页面中其t值最大的,即最近最久未使用的页面予以淘汰。 

利用LRU算法对上例进行页面置换的结果如上图所示
当进程第一次对页面2进行访问时,由于页面7是最近最久未被访问的,故将它置换出去。
当进程第一次对页面3进行访问时,第1页成为最近最久未使用的页,将它换出。
由图可以看出,前5个时间的图像与最佳置换算法时的相同,但这并非是必然的结果。
因为,最佳置换算法是从“向后看”的观点出发的,即它是依据以后各页的使用情况;
而LRU算法则是“向前看”的,即根据各页以前的使用情况来判断,而页面过去和未来的走向之间并无必然的联系。 

LRU置换算法的硬件支持
  LRU置换算法虽然是一种比较好的算法,但要求系统有较多的支持硬件。
  为了了解一个进程在内存中的各个页面各有多少时间未被进程访问,以及如何快速地知道哪一页是最近最久未使用的页面,须有两类硬件之一的支持:寄存器或栈。

  【寄存器】为了记录某进程在内存中各页的使用情况,须为每个在内存中的页面配置一个移位寄存器,可表示为

R = Rn-1Rn-2Rn-3 … R2R1R0

  当进程访问某物理块时,要将相应寄存器的Rn-1位置成1。此时,定时信号将每隔一定时间(例如100 ms)将寄存器右移一位。如果我们把n位寄存器的数看做是一个整数,那么,具有最小数值的寄存器所对应的页面,就是最近最久未使用的页面

  下图示出了某进程在内存中具有8个页面,为每个内存页面配置一个8位寄存器时的LRU访问情况。这里,把8个内存页面的序号分别定为1~8。由图可以看出,第3个内存页面的R值最小,当发生缺页时,首先将它置换出去。


  【栈】可利用一个特殊的栈来保存当前使用的各个页面的页面号。每当进程访问某页面时,便将该页面的页面号从栈中移出,将它压入栈顶。因此,栈顶始终是最新被访问页面的编号,而栈底则是最近最久未使用页面的页面号。假定现有一进程所访问的页面的页面号序列为:
4,7,0,7,1,0,1,2,1,2,6
  随着进程的访问,栈中页面号的变化情况如下图所示。在访问页面6时发生了缺页,此时页面4是最近最久未被访问的页,应将它置换出去。 

Linux LRU算法

  最初分配某个页时, 页的寿命为3, 每次页被访问, 其寿命增加3, 直到20为止。当内核的交换进程运行时(kswapd周期运行), 在内存的所有页面寿命减1。如果某个页的寿命为0,则该页作为交换候选页。

二、实验介绍

  • 问题描述:

  设计程序模拟先进先出FIFO、最佳置换OPI和最近最久未使用LRU页面置换算法的工作过程。假设内存中分配给每个进程的最小物理块数为m,在进程运行过程中要访问的页面个数为n,页面访问序列为P1, … ,Pn,分别利用不同的页面置换算法调度进程的页面访问序列,给出页面访问序列的置换过程,计算每种算法缺页次数和缺页率。

  • 程序要求:

1)利用先进先出FIFO、最佳置换OPI和最近最久未使用LRU三种页面置换算法模拟页面访问过程。

2)模拟三种算法的页面置换过程,给出每个页面访问时的内存分配情况。

3)输入:最小物理块数m,页面个数n,页面访问序列P1, …,Pn,算法选择1-FIFO,2-OPI,3-LRU。

4)输出:每种算法的缺页次数和缺页率。


三、程序设计和程序开发

const int MaxNumber=100;
int PageOrder[MaxNumber];  //页面序列
int PageNum,LackNum,MinBlockNum; //页面个数,缺页次数,最小物理块数
int PageDisCount[MaxNumber]; //当前内存距离下一次出现的距离
int LRUtime[MaxNumber];   //存储队列中各个页面最近使用情况
double  LackPageRate;   //缺页率
int LackPageNum;   //缺页数
int VirtualQueue[MaxNumber];   //虚拟队列
页面置换的实现过程如下:
  • 1.变量初始化;
  • 2.接收用户输入最小物理块数m,页面个数n,页面序列P1, … ,Pn,选择算法1-FIFO,2-OPI,3-LRU;
  • 3.根据用户选择的算法进行页面的分配和置换,输出页面置换算法的模拟过程;
  • 4.计算选择算法的缺页次数和缺页率;
  • 5.输出选择算法的缺页次数和缺页率。
FIFO过程:
void FIFO()
{
	cout<<"********* 你选择了FIFO算法:********* "<<endl;
	cout<<"页面置换情况如下:"<<endl;
	initial();
	bool isInQueue;
	int point = 0;  //指向最老的页面
	for (int i = MinBlockNum;i<PageNum;i++)
	{
		isInQueue = false;
		for (int k = 0;k<MinBlockNum;k++)
		{
			if (VirtualQueue[k] == PageOrder[i])   //如果当前页面在队列中
			{
				isInQueue = true;
			}
		}

		if (!isInQueue)   //如果当前页面不在队列中,则进行相应的处理
		{
			LackPageNum++;  //缺页数加1

			VirtualQueue[point] = PageOrder[i];
			display();
			point++;
			if (point == MinBlockNum) 
			{
				point = 0;  //当point指向队尾后一位的时候,将point重新指向队首
			}
		}
	}

	LackPageRate = (LackPageNum * 1.0)/PageNum;
	cout<<"缺页数LackPageNum = "<<LackPageNum<<endl;
	cout<<"缺页率LackPageRate = "<<LackPageRate<<endl;
	
}

OPI过程:

void OPI()
{
	cout<<"********* 你选择了OPI算法:********* "<<endl;
	cout<<"页面置换情况如下:"<<endl;
	initial();

	bool isInQueue;
	int dis;   //表示队列每个值距离下一次访问的距离
	int point;  //指向最长时间未被访问的下标

	for(int i = MinBlockNum;i<PageNum;i++)
	{
		isInQueue = false;	
		for (int k = 0;k<MinBlockNum;k++)
		{
			if (VirtualQueue[k] == PageOrder[i])   //如果当前页面在队列中
			{
				isInQueue = true;
			}
		}

		if (!isInQueue)
		{
			LackPageNum++;
			//计算当前队列每一页对应的下一次出现的距离
			for (int s = 0;s < MinBlockNum;s++)
			{
				dis = 1;
				for (int t = i;t<PageNum;t++)   //从页面序列的第i个位置开始找起
				{
					if (VirtualQueue[s] != PageOrder[t])
					{
						dis++;
					}
					else
					{						
						break;
					}
				}
				PageDisCount[s] = dis;
				
			}
			point = 0;
			for (int m = 1;m < MinBlockNum;m++)
			{
				if (PageDisCount[point] < PageDisCount[m])
				{
					point = m;
				}
			}


			VirtualQueue[point] = PageOrder[i];
			display();

		}//if
	}//for

	LackPageRate = (LackPageNum*1.0)/PageNum;
	cout<<"缺页数LackPageNum = "<<LackPageNum<<endl;
	cout<<"缺页率LackPageRate = "<<LackPageRate<<endl;
}

LRU过程:

void LRU()
{
	cout<<"********* 你选择了LRU算法:********* "<<endl;
	cout<<"页面置换情况如下:"<<endl;

	initial();
	bool isInQueue;
	int point,k;  //指向最长时间未被访问的下标
	
	for(int i = MinBlockNum;i<PageNum;i++)
	{
		isInQueue = false;	
		for (k = 0;k<MinBlockNum;k++)
		{
			if (VirtualQueue[k] == PageOrder[i])   //如果当前页面在队列中
			{
				isInQueue = true;
			}
		}

		if (!isInQueue)
		{
			LackPageNum++;
			point = 0;
			for (int j = 1;j<MinBlockNum;j++)
			{
				if (LRUtime[point]<LRUtime[j])
				{
					point = j;
				}
			}

			for (int s = 0;s<MinBlockNum;s++)//其余页面对应的时间要+1
			{
				if (VirtualQueue[s] != VirtualQueue[point])
				{
					LRUtime[s]++;
				}
			}

			VirtualQueue[point] = PageOrder[i];
			LRUtime[point] = 0;

			display();
		}//if
		else   //负责更新当前对应页面的时间
		{
			for (int s = 0;s<MinBlockNum;s++)//其余页面对应的时间要+1
			{
				if (VirtualQueue[s] != PageOrder[i])
				{
					LRUtime[s]++;
				}
				else
					LRUtime[s] = 0;
			}
		}
	}//for

	LackPageRate = (LackPageNum*1.0)/PageNum;
	cout<<"缺页数LackPageNum = "<<LackPageNum<<endl;
	cout<<"缺页率LackPageRate = "<<LackPageRate<<endl;
}

四、实验结果分析

  • 实验数据:
3
20
7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1
  • 实验结果:

五、实验源码

// 操作系统_实验五(虚拟内存页面置换算法).cpp : 定义控制台应用程序的入口点。
//

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

const int MaxNumber=100;
int PageOrder[MaxNumber];  //页面序列
int PageNum,LackNum,MinBlockNum;  //页面个数,缺页次数,最小物理块数
int PageDisCount[MaxNumber]; //当前内存距离下一次出现的距离
int LRUtime[MaxNumber];   //存储队列中各个页面最近使用情况

double  LackPageRate;   //缺页率
int LackPageNum;   //缺页数
int VirtualQueue[MaxNumber];   //虚拟队列

void input();
void initial();
void FIFO();    //先进先出
void OPI();     //最佳置换
void LRU();    //最近最久未使用LRU页面置换算法
void display();

void input()
{
	ifstream readData;
	readData.open("data.txt");
	readData>>MinBlockNum;
	readData>>PageNum;
	for (int i=0;i<PageNum;i++)
	{
		readData>>PageOrder[i];
	}

	cout<<"读取数据结果如下:"<<endl;
	cout<<"最小物理块数 = "<<MinBlockNum<<endl;
	cout<<"页面个数 = "<<PageNum<<endl;
	cout<<"页面序列如下:"<<endl;
	for (int i = 0;i<PageNum;i++)
	{
		cout<<PageOrder[i]<<" ";
	}
	cout<<endl;
}

void initial()
{
	LackPageNum = MinBlockNum;
	LackPageRate = 0.0;

	for(int i = 0;i<PageNum;i++)
	{
		PageDisCount[i] = 0;  //初始化距离都为0
		VirtualQueue[i] = -1;  //初始化队列的值都为负数
	}

	for (int i = 0;i<MinBlockNum;i++)
	{
		bool isInQueue2 = false;
		int dis = 0;
		LRUtime[i] = 0;
		for (int j = 0;j<MinBlockNum;j++)
		{
			if (VirtualQueue[j] == PageOrder[i])
			{
				isInQueue2 = true;
			}
		}
		if (!isInQueue2)  //当有新的进程进入到队列时,便计算其对应的距离
		{
			VirtualQueue[i] = PageOrder[i];
			for (int k = 0;k<i;k++)
			{ 
				LRUtime[k]++;   //之前的页面对应的时间+1
			}
			display();
		}
		else
		{
			LRUtime[i] = 0;  //重新更新为0,表示最近刚刚使用
		}
		
	}
}

void FIFO()
{
	cout<<"********* 你选择了FIFO算法:********* "<<endl;
	cout<<"页面置换情况如下:"<<endl;
	initial();
	bool isInQueue;
	int point = 0;  //指向最老的页面
	for (int i = MinBlockNum;i<PageNum;i++)
	{
		isInQueue = false;
		for (int k = 0;k<MinBlockNum;k++)
		{
			if (VirtualQueue[k] == PageOrder[i])   //如果当前页面在队列中
			{
				isInQueue = true;
			}
		}

		if (!isInQueue)   //如果当前页面不在队列中,则进行相应的处理
		{
			LackPageNum++;  //缺页数加1

			VirtualQueue[point] = PageOrder[i];
			display();
			point++;
			if (point == MinBlockNum) 
			{
				point = 0;  //当point指向队尾后一位的时候,将point重新指向队首
			}
		}
	}

	LackPageRate = (LackPageNum * 1.0)/PageNum;
	cout<<"缺页数LackPageNum = "<<LackPageNum<<endl;
	cout<<"缺页率LackPageRate = "<<LackPageRate<<endl;
	
}

void OPI()
{
	cout<<"********* 你选择了OPI算法:********* "<<endl;
	cout<<"页面置换情况如下:"<<endl;
	initial();

	bool isInQueue;
	int dis;   //表示队列每个值距离下一次访问的距离
	int point;  //指向最长时间未被访问的下标

	for(int i = MinBlockNum;i<PageNum;i++)
	{
		isInQueue = false;	
		for (int k = 0;k<MinBlockNum;k++)
		{
			if (VirtualQueue[k] == PageOrder[i])   //如果当前页面在队列中
			{
				isInQueue = true;
			}
		}

		if (!isInQueue)
		{
			LackPageNum++;
			//计算当前队列每一页对应的下一次出现的距离
			for (int s = 0;s < MinBlockNum;s++)
			{
				dis = 1;
				for (int t = i;t<PageNum;t++)   //从页面序列的第i个位置开始找起
				{
					if (VirtualQueue[s] != PageOrder[t])
					{
						dis++;
					}
					else
					{						
						break;
					}
				}
				PageDisCount[s] = dis;
				
			}
			point = 0;
			for (int m = 1;m < MinBlockNum;m++)
			{
				if (PageDisCount[point] < PageDisCount[m])
				{
					point = m;
				}
			}


			VirtualQueue[point] = PageOrder[i];
			display();

		}//if
	}//for

	LackPageRate = (LackPageNum*1.0)/PageNum;
	cout<<"缺页数LackPageNum = "<<LackPageNum<<endl;
	cout<<"缺页率LackPageRate = "<<LackPageRate<<endl;
}

void LRU()
{
	cout<<"********* 你选择了LRU算法:********* "<<endl;
	cout<<"页面置换情况如下:"<<endl;

	initial();
	bool isInQueue;
	int point,k;  //指向最长时间未被访问的下标
	
	for(int i = MinBlockNum;i<PageNum;i++)
	{
		isInQueue = false;	
		for (k = 0;k<MinBlockNum;k++)
		{
			if (VirtualQueue[k] == PageOrder[i])   //如果当前页面在队列中
			{
				isInQueue = true;
			}
		}

		if (!isInQueue)
		{
			LackPageNum++;
			point = 0;
			for (int j = 1;j<MinBlockNum;j++)
			{
				if (LRUtime[point]<LRUtime[j])
				{
					point = j;
				}
			}

			for (int s = 0;s<MinBlockNum;s++)//其余页面对应的时间要+1
			{
				if (VirtualQueue[s] != VirtualQueue[point])
				{
					LRUtime[s]++;
				}
			}

			VirtualQueue[point] = PageOrder[i];
			LRUtime[point] = 0;

			display();
		}//if
		else   //负责更新当前对应页面的时间
		{
			for (int s = 0;s<MinBlockNum;s++)//其余页面对应的时间要+1
			{
				if (VirtualQueue[s] != PageOrder[i])
				{
					LRUtime[s]++;
				}
				else
					LRUtime[s] = 0;
			}
		}
	}//for

	LackPageRate = (LackPageNum*1.0)/PageNum;
	cout<<"缺页数LackPageNum = "<<LackPageNum<<endl;
	cout<<"缺页率LackPageRate = "<<LackPageRate<<endl;
}

void display()
{
	for (int i = 0;i<MinBlockNum && VirtualQueue[i]>=0;i++)
	{
		cout<<VirtualQueue[i]<<" ";
	}
	cout<<endl;
}

int main()
{
	input();

	int isContinue = 1;
	int chooseAlgorithm;

	while(isContinue)
	{
		cout<<"******************************************************"<<endl;
		cout<<"********* 请选择算法    **********"<<endl;
		cout<<"********* 1代表FIFO算法 **********"<<endl;
		cout<<"********* 2代表OPI算法  **********"<<endl;
		cout<<"********* 3代表LRU算法  **********"<<endl;
		cin>>chooseAlgorithm;
		switch(chooseAlgorithm)
		{
		case 1:
			FIFO();
			break;
		case 2:
			OPI();
			break;
		case 3:
			LRU();
			break;
		default:
			cout<<"请输入正确的序号进行选择:"<<endl;break;
		}
		cout<<"********** 是否继续选择算法?              **********"<<endl;
		cout<<"********** 输入1代表继续,输入0代表退出! **********"<<endl;
		cin>>isContinue;
	}

	cout<<"***************************结束***************************"<<endl;

	system("pause");

	return 0;
}


  • 34
    点赞
  • 183
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
虚拟内存是一种计算机内存管理技术,它允许程序使用比实际可用物理内存更大的内存空间。虚拟内存通过将内存分为固定大小的页面来实现,每个页面可以被映射到物理内存中的一个页面帧或者磁盘上的一个页面。当程序需要访问一个不在物理内存中的页面时,操作系统会将该页面从磁盘中读取到物理内存中,如果物理内存中没有足够的空间,则需要使用页面置换算法将一个已经在物理内存中的页面替换出去。 常见的页面置换算法有以下几种: 1.最优页面置换算法(OPT):选择在未来最长时间内不再被访问的页面进行置换,但是由于无法预测未来的访问模式,因此该算法无法实现。 2.先进先出页面置换算法(FIFO):选择最早进入物理内存的页面进行置换,但是该算法可能会将最常用的页面替换出去,导致缺页率增加。 3.最近最少使用页面置换算法(LRU):选择最近最少被访问的页面进行置换,该算法可以较好地模拟人类的访问模式,但是实现起来比较复杂。 4.时钟页面置换算法(Clock):将物理内存中的页面组织成一个环形链表,每个页面都有一个访问位,当页面被访问时,访问位被设置为1,当需要置换页面时,从当前位置开始扫描环形链表,如果访问位为0,则选择该页面进行置换,否则将访问位设置为0并继续扫描。 5.最不经常使用页面置换算法(NFU):选择最不经常被访问的页面进行置换,每个页面有一个计数器,每次被访问时计数器加1,当需要置换页面时,选择计数器最小的页面进行置换。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值