操作系统实验——内存管理

实验三 内存管理

一、实验目的

1、了解虚拟存储技术的特点,掌握虚拟存储请求页式存储管理中几种基本页面置换算法的基本思想和实现过程,并比较它们的效率。
2、了解程序设计技术和内存泄露的原因

二、实验环境

LINUX OS

三、实验内容

模拟实现请求页式存储管理的几种基本页面置换算法
(1)最佳淘汰算法(OPT)
(2)先进先出的算法(FIFO)
(3)最近最久未使用算法(LRU))
实验要求
1、 画出每个页面置换算法流程图;
2、 对算法所用的数据结构进行说明;
3、 测试数据随机产生。不可手工输入;
4、 编写程序并调试;
5、 多次测试程序,截屏输出实验结果;
6、 根据实验结果与理论课讲述的原理进行实验分析。

四、实验原理 实验中用到的系统调用函数(包括实验原理中介绍的和自己采用的),实验步骤

1、虚拟存储系统
UNIX中,为了提高内存利用率,提供了内外存进程对换机制;内存空间的分配和回收均以页为单位进行;一个进程只需将其一部分(段或页)调入内存便可运行;还支持请求调页的存储管理方式。
当进程在运行中需要访问某部分程序和数据时,发现其所在页面不在内存,就立即提出请求(向CPU发出缺中断),由系统将其所需页面调入内存。这种页面调入方式叫请求调页。
为实现请求调页,核心配置了四种数据结构:页表、页框号、访问位、修改位、有效位、保护位等。
2、页面置换算法
当CPU接收到缺页中断信号,中断处理程序先保存现场,分析中断原因,转入缺页中断处理程序。该程序通过查找页表,得到该页所在外存的物理块号。如果此时内存未满,能容纳新页,则启动磁盘I/O将所缺之页调入内存,然后修改页表。如果内存已满,则须按某种置换算法从内存中选出一页准备换出,是否重新写盘由页表的修改位决定,然后将缺页调入,修改页表。利用修改后的页表,去形成所要访问数据的物理地址,再去访问内存数据。整个页面的调入过程对用户是透明的。
(1)最佳淘汰算法(OPT):选择永不使用或在未来最长时间内不再被访问的页面予以替换。
(2)先进先出的算法(FIFO):选择在内存中驻留时间最久的页面予以替换。
(3)最近最久未使用算法(LRU):选择过去最长时间未被访问的页面予以替换。
3、首先用srand( )和rand( )函数定义和产生指令序列,然后将指令序列变换成相应的页地址流,并针对不同的算法计算出相应的命中率。
(1)通过随机数产生一个指令序列,共320条指令。指令的地址按下述原则生成:
A:50%的指令是顺序执行的
B:25%的指令是均匀分布在前地址部分
C:25%的指令是均匀分布在后地址部分
具体的实施方法是:
A:在[0,319]的指令地址之间随机选取一起点m
B:顺序执行一条指令,即执行地址为m+1的指令
C:在前地址[0,m+1]中随机选取一条指令并执行,该指令的地址为m’
D:顺序执行一条指令,其地址为m’+1
E:在后地址[m’+2,319]中随机选取一条指令并执行
F:重复步骤A-E,直到320次指令
(2)将指令序列变换为页地址流
设:页面大小为1K;
用户内存容量4页到32页;
用户虚存容量为32K。
在用户虚存中,按每K存放10条指令排列虚存地址,即320条指令在虚存中的存放方式为:
第 0 条-第 9 条指令为第0页(对应虚存地址为[0,9])
第10条-第19条指令为第1页(对应虚存地址为[10,19])
………………………………
第310条-第319条指令为第31页(对应虚存地址为[310,319])
按以上方式,用户指令可组成32页。

实验中用到的系统调用函数
因为是模拟程序,可以不使用系统调用函数。

五、实验结果分析(截屏的实验结果,与实验结果对应的实验分析)

1、实验结果与实验程序、实验步骤、实验原理、操作系统原理的对应分析;
2、不同条件下的实验结果反应的问题及原因;
3、实验结果的算法时间、效率、鲁棒性等性能分析。

流程图
在这里插入图片描述

OPT
在这里插入图片描述

LRU
在这里插入图片描述

FIFO
在这里插入图片描述

数据结构

struct pageInformation
{
int ID; //页面号
int visit; //被访问标记
};
private:
int count; //页面中断次数
int blockNum; //系统分配的内存块

三种算法的实现思路

For循环合并后的页面序列,首先查找在内存中是否存在当前要调度的页面,如果存在就不用进行页面缺页中断处理;否则,进行缺页中断。
缺页中断分成两种情况:
1、在内存中存在空闲的位置,直接将当前调度的页面调入内存(一般出现在调度的开始时候)
2、内存中没有空闲位置,就需要选择内存的某一个页面调出,以让当前的调度页面进入内存。注意这里淘汰的依据是引用位visit的大者。
//查找应予置换的页面
int findReplace()
{
int pos = 0;
for(int i=0; i<blockNum; i++)
if(block[i].visit >= block[pos].visit)
pos = i;//找到应予置换页面,返回BLOCK中位置
return pos;
}

FIFO:淘汰的是主存中驻留时间最长的页面,所以每一次调度都需要计算(增加)内存中页面的的驻留时间。那么在进行缺页中断的时候根据这个时间就可以选择要淘汰的页面了。
实现的代码如下:
for(int j=0; j<blockNum; j++)
block[j].visit++;
LRU:淘汰最近一段时间内最长时间没有被访问过的页面,注意到,每一次调度后都有将引用位复位成0,重新计时。(这个要注意与FIFO的区别)。那么处理的方法是:在页面调度过程中,如果访问的页面在内存中,就不用进行缺页中断,但是要注意将访问引用位置为0;其次在每一次调度过程中也是要增加驻留时间。
实现的代码如下:
if(exist != -1)
{
cout<<"----------------------------"<<endl;
cout<<“即将访问的是页面”<<page[i].ID<<"----内存中已存在该页"<<endl;;
cout<<"----------------------------"<<endl;
//每一次页面被访问后,重新计时
block[exist].visit = 0;//恢复存在的并刚访问过的BLOCK中页面visit为0
}
for(int j=0; j<blockNum; j++)
block[j].visit++;

OPT:淘汰的页面将是以后永远不再使用,或者是在将来最长时间内不再被访问的页面,那么就要对后续的页面进行预读,并将他们的访问引用位置为最早遇到的页面号,如果在后续的页面中没有出现,那么他的访问引用位就置为最大,就是肯定要被淘汰的。
实现的代码如下:(下面的代码预读所有的后续页面,同时也可以进行设置相关的预读页面数,修改下面代码中的pageNum就可以了。)
for(int k=0; k<blockNum; k++)
for(int j=i; j<pageNum; j++) //修改这里的j<pageNum 就可以改变预读后续页面的页数
{
if(block[k].ID != page[j].ID)
{
block[k].visit = 1000;
}
else
{
block[k].visit = j;
break;
}
}

实验截图

产生随机指令地址
在这里插入图片描述

OPT算法
在这里插入图片描述
LRU算法
在这里插入图片描述
FIFO算法
在这里插入图片描述

六、实验总结

FIFO算法:总是淘汰在内存中停留时间最长(年龄最老)的一页,即先进入内存的页,先被换出。
优点:FIFO页面置换算法实现简单,要求的硬件支持较少。
缺点:(1)性能并不很好,效率不高;
存在Belady异常现象,即缺页率随内存块增加而增加。
OPT算法:最佳置换算法(Optimal Replacement, OPT)其实质是:为调入新页面而必须预先淘汰某个老页面时,所选择的老页面应在将来不被使用,或者是在最远的将来才被访问。
优点:保证有最小缺页率。
缺点:实现上很困难。
LRU算法:当需要置换一页时,选择在最近一段时间里最久没有使用过的页面予以淘汰。
优点:效率高,实现简单
缺点:偶发性的、周期性的批量操作会导致LRU的命中率急剧下降,缓存五日内情况比较严重

具体根据实验结果分析可以很容易的发现,三种算法当中,OPT的缺页率是最低的,当内存块较大时,有可能出现三种算法缺页率相同的情况,但若是三种算法的缺页率不同,OPT的缺页率一定是最低的。

七、实验数据及源代码(学生必须提交自己设计的程序源代码,并有注释,源代码电子版也一并提交),包括思考题的程序。

PageReplacementAlgorithms
```cpp
include <iostream>
#include <stdlib.h>
#include <fstream>
#include <ctime>
using namespace std;
struct pageInformation
{
	int ID;     //页面号
	int visit;  //被访问标记
};
#define total_instruction 320
pageInformation* block;    //物理块
pageInformation* page;     //合并后的页面号串
pageInformation* oriPage;  //原生的320条指令所对应的320页
int instructions[320];      //原生的320条指令
int pageNum; //合并后的页面数

class Pager
{
private:
	int count; //页面中断次数
	int blockNum; //系统分配的内存块
public:
	//初始化生成320条指令
	void creatInstructions()
	{
		//生成320 个指令地址 Instruction address
		int insAdress[total_instruction];
		int random = 0;
		srand((unsigned)time(NULL)); //随机数种子
		for (int i = 0; i < total_instruction; i+=4)
		{
			int random = rand() % 320;

			insAdress[i] = random;

			int random_ = 0;
			random_ = rand() % (random + 1);
			insAdress[i + 1] = random_;
			insAdress[i + 2] = random_ + 1;

			int random__ = 0;
			random__ = (random_ + 2) + rand() % (319 - (random_ + 2) + 1);
			insAdress[i + 3] = random__;
		}

		//生成320条指令
		for (int j = 0; j < 320; j++)
		{
			instructions[j] = insAdress[j];
		}
	}
	//将指令转换成页号
	void convertToPage(int pageSize)
	{
		oriPage = new pageInformation[320]; //一条指令对于一页
		int num = pageSize * 10; //每一页存放的指令条数
		//int num = pageSize * 1024; //每一页存放的指令条数
		//pageSize = 1
		//对第一条指令做特殊处理
		oriPage[0].ID = 0;
		for (int i = 1; i < 320; i++)
		{
			oriPage[i].visit = 0;
			int a = instructions[i] / num;
			int b = instructions[i] % num;
			if (b)
				oriPage[i].ID = a;
			else
				oriPage[i].ID = a + 1;
		}
	}
	//对于相邻相同的页号,合并为一个。
	void mergence()
	{
		pageNum = 0;
		page = new pageInformation[320];
		//解决思路:依次扫描oriPage数组,和page中的最后一个数组比较,如果不同就写入page,相同就不用写入。
		page[0] = oriPage[0];
		for (int i = 1; i < 320; i++)
		{
			//如果不同就写入mergePage
			if (page[pageNum].ID != oriPage[i].ID)
			{
				pageNum++;
				page[pageNum] = oriPage[i];
			}
		}
	}
	//初始化物理块
	void creatBlock(int n)
	{
		blockNum = n;
		initalPhysicalBlock();
	}
	void initalPhysicalBlock()
	{
		block = new pageInformation[blockNum];
		BlockClear(blockNum);
	}
	//置分配的物理块的初值
	void BlockClear(int blockNum)
	{
		for (int i = 0; i < blockNum; i++)
		{
			block[i].ID = -1;
			block[i].visit = 0;
		}
	}
	//查找是否有空闲内存块
	int findSpace()
	{
		for (int i = 0; i < blockNum; i++)
			if (block[i].ID == -1)
				return i;//找到空闲内存,返回BLOCK中位置
		return -1;
	}
	//查找内存中是否有该页面
	int findExist(int curpage)
	{
		for (int i = 0; i < blockNum; i++)
			if (block[i].ID == page[curpage].ID)
				return i;//找到内存中有该页面,返回BLOCK中位置
		return -1;
	}
	//查找应予置换的页面
	int findReplace()
	{
		int pos = 0;
		for (int i = 0; i < blockNum; i++)
			if (block[i].visit >= block[pos].visit)
				pos = i;//找到应予置换页面,返回BLOCK中位置
		return pos;
	}
	//显示
	void display()
	{
		cout << "----------------------------" << endl;
		for (int i = 0; i < blockNum; i++)
			if (block[i].ID != -1)
				cout << block[i].ID << " ";
		cout << endl;
		cout << "----------------------------" << endl;
	}
	//FIFO算法
	void FIFO()
	{
		count = 0;
		int exist, space, position;
		double FIFO_missRate = 0;
		for (int i = 0; i < pageNum; i++)
		{
			//查找内存中是否存在该页
			exist = findExist(i);
			if (exist != -1)
			{
				cout << "----------------------------" << endl;
				cout << "即将访问的是页面" << page[i].ID << ",内存中已存在该页" << endl;
				cout << "----------------------------" << endl;
			}
			//内存块中不存在,进行缺页中断的调度
			else
			{
				count++;
				space = findSpace();
				//在内存块中找到空闲的位置,这个时候也是要中断的
				if (space != -1)
				{
					block[space] = page[i];
					display();
				}
				else
				{
					position = findReplace();
					cout << "----------------------------" << endl;
					cout << "即将访问的是页面" << page[i].ID << ",将被置换出的是页面" << block[position].ID << endl;
					cout << "----------------------------" << endl;
					block[position] = page[i];
					display();
				}
			}
			//计算页面在内存块中驻留的时间,每一次都+1,如果越大,说明驻留时间最长,就淘汰他
			for (int j = 0; j < blockNum; j++)
				block[j].visit++;//BLOCK中所有页面visit++
		}
		FIFO_missRate = (float)count / pageNum;
		cout << "缺页次数:" << count << endl;
		cout << "FIFO算法的缺页率是:" << FIFO_missRate*100 << " %"<<endl;
	}
	//LRU算法
	void LRU()
	{
		int exist, space, position;
		double LRU_missRate = 0;
		count = 0;
		for (int i = 0; i < pageNum; i++)
		{
			exist = findExist(i);
			if (exist != -1)
			{
				cout << "----------------------------" << endl;
				cout << "即将访问的是页面" << page[i].ID << ",内存中已存在该页" << endl;;
				cout << "----------------------------" << endl;
				//每一次页面被访问后,重新计时
				block[exist].visit = 0;//恢复存在的并刚访问过的BLOCK中页面visit为0
			}
			else
			{
				count++;
				space = findSpace();
				if (space != -1)
				{
					block[space] = page[i];
					display();
				}
				else
				{
					position = findReplace();
					cout << "----------------------------" << endl;
					cout << "即将访问的是页面" << page[i].ID << ",将被置换出的是页面" << block[position].ID << endl;
					cout << "----------------------------" << endl;
					block[position] = page[i];
					display();
				}
			}
			//计时
			for (int j = 0; j < blockNum; j++)
			{
				block[j].visit++;
			}
		}
		LRU_missRate = (float)count / pageNum;
		cout << "缺页次数:" << count << endl;
		cout << "LRU算法的缺页率是:" << LRU_missRate*100 << " %" << endl;

	}
	//OPT算法
	void OPT()
	{
		int exist, space, position;
		double OPT_missRate = 0;//缺页率
		count = 0;
		for (int i = 0; i < pageNum; i++)
		{
			exist = findExist(i);
			//页已经在内存块中
			if (exist != -1)
			{
				cout << "----------------------------" << endl;
				cout << "即将访问的是页面" << page[i].ID << ",内存中已存在该页" << endl;
				cout << "----------------------------" << endl;
			}
			else
			{
				count++;
				space = findSpace();
				if (space != -1)
				{
					block[space] = page[i];
					display();
				}
				else
				{
					for (int k = 0; k < blockNum; k++)
						for (int j = i; j < pageNum; j++) //修改这里的j<pageNum 就可以改变预读后续页面的页数
						{
							if (block[k].ID != page[j].ID)
							{
								block[k].visit = 1000;
							}
							else
							{
								block[k].visit = j;
								break;
							}
						}
					position = findReplace();
					cout << "----------------------------" << endl;
					cout << "即将访问的是页面" << page[i].ID << ",将被置换出的是页面" << block[position].ID << endl;
					cout << "----------------------------" << endl;
					block[position] = page[i];
					display();
				}
			}
		}
		OPT_missRate = (float)count / pageNum;
		cout << "缺页次数:" << count << endl;
		cout << "OPT算法的缺页率是:" << OPT_missRate*100 << " %" << endl;
	}

};
int main()
{

	while (1)
	{
		int selection;
		cout << "----请选择一种页面置换算法:1:OPT 2:LRU 3:FIFO----"<<endl;
		cin >> selection;
		int pSize, num;
		pSize = 1;
		num = 10;
		Pager test;
		test.creatInstructions();
		test.convertToPage(pSize);
		test.mergence();
		test.creatBlock(num);
		cout << "\t————————产生的随机指令地址————————\t" << endl;
		for (int m = 0;m < total_instruction;m+=4)
		{
			
			cout  <<"\t"<< instructions[m] << "\t" << instructions[m+1]<< "\t" << instructions[m+2] << "\t" <<instructions[m+3] <<endl;
		}
		if (selection == 1)
		{
			cout << "\t————————OPT————————\t" << endl;
			test.OPT();
		}
		else if (selection == 2)
		{
			cout << "\t————————LRU————————\t" << endl;
			test.LRU();
		}
		else if (selection == 3)
		{
			cout << "\t————————FIFO————————\t" << endl;
			test.FIFO();
		}
		delete page;
		delete oriPage;
		delete block;
		cout << endl;
	}
	return 0;
}

思考题

1、从几种算法的命中率看,哪个算法最高?哪个算法最低?对每个页面的执行结果进行分析。
A:
OPT算法的命中率最高,LRU与FIFO命中率差距相近;
2、OPT算法在执行过程中可能会发生错误,为什么?
A:
OPT算法是对将来出现最晚的进行淘汰,可以看出是对未来的假设进行判断,但是在实际操作过程中,进程是动态执行的,可能会发生改变,无法对未来进行判断,故当判断与实际不符时便会出现错误。可以看出,OPT算法是一种理想算法,现实中很难实现,本此实验时对OPT算法进行模拟,对未来的判断是固定不变的,不存在动态变化,故在模拟实验中并没有发生变化。

  • 3
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值