操作系统--内存管理

实验目的

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

实验内容

  1. 模拟实现请求页式存储管理的几种基本页面置换算法
    (1)最佳淘汰算法(OPT)
    (2)先进先出的算法(FIFO)
    (3)最近最久未使用算法(LRU))

实验原理

  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、 画出每个页面置换算法流程图;
操作系统--内存管理_第1张图片
操作系统--内存管理_第2张图片
操作系统--内存管理_第3张图片
2、 对算法所用的数据结构进行说明;

#define INVALID -1
#define total_instruction 320  // 指令流长度
#define total_vp 32            // 虚页长
#define clear_period 50        // 清0周期

typedef struct         // 页面结构
{
	int pn;        // 页号
	int pfn;       // 页面
	int counter;   // 一个周期内访问该页面的次数
	int time;      // 访问时间
}pl_type;

pl_type pl[total_vp];  // 页面结构数组

struct pfc_struct  // 页面控制结构
{
	int pn;   // 页号
	int pfn;  // 页面
	struct pfc_struct *next; // 指向下一个页面的指针
};

typedef struct pfc_struct pfc_type;

pfc_type pfc[total_vp];  // 定义用户进程虚页控制结构
pfc_type *freepf_head;   // 空页面的头指针
pfc_type *busypf_head;   // 忙页面的头指针
pfc_type *busypf_tail;   // 忙页面的尾指针

int diseffect;                 // 页面实效次数
int a[total_instruction];      // 指令流数据组
int page[total_instruction];   // 每条指令所属页号
int offset[total_instruction]; // 每页装入10条指令后取模运算页号偏移值
float diseffect_vag[3];        // 三种算法的平均命中率

3、 测试数据随机产生。不可手工输入;

4、 编写程序并调试;

#include 
#include   
#include 

#define INVALID -1
#define total_instruction 320  // 指令流长度
#define total_vp 32            // 虚页长
#define clear_period 50        // 清0周期

typedef struct         // 页面结构
{
	int pn;        // 页号
	int pfn;       // 页面
	int counter;   // 一个周期内访问该页面的次数
	int time;      // 访问时间
}pl_type;

pl_type pl[total_vp];  // 页面结构数组

struct pfc_struct  // 页面控制结构
{
	int pn;   // 页号
	int pfn;  // 页面
	struct pfc_struct *next; // 指向下一个页面的指针
};

typedef struct pfc_struct pfc_type;

pfc_type pfc[total_vp];  // 定义用户进程虚页控制结构
pfc_type *freepf_head;   // 空页面的头指针
pfc_type *busypf_head;   // 忙页面的头指针
pfc_type *busypf_tail;   // 忙页面的尾指针

int diseffect;                 // 页面实效次数
int a[total_instruction];      // 指令流数据组
int page[total_instruction];   // 每条指令所属页号
int offset[total_instruction]; // 每页装入10条指令后取模运算页号偏移值
float diseffect_vag[3];        // 三种算法的平均命中率 
 
int init(int);   // 初始化,为每个页面赋初值
int OPT(int);    // 最佳淘汰置换算法
int FIFO(int);   // 先进先出置换算法算法
int LRU(int);    // 最近最久未使用置换算法


int main()
{
	int s, i, j;
	srand(time(0));
	//在[0,319]的指令地址之间随机选取一起点m
	s = (float)319 * rand() / 32767 / 32767 / 2 + 1;

	for (i = 0; i < total_instruction; i += 4) //产生指令队列
	{
		a[i] = s;                            //任选一指令访问点m
		a[i + 1] = a[i] + 1;                     //顺序执行一条指令
		a[i + 2] = (float)a[i] * rand() / 32767 / 32767 / 2; //执行前地址指令m'
		a[i + 3] = a[i + 2] + 1;                         //m'+1
		s = (float)(318 - a[i + 2]) * rand() / 32767 / 32767 / 2 + a[i + 2] + 2; //在后地址[m’+2,319]中随机选取一条指令并执行
		//printf("i=%d----%d\n", i, s);
	}

	for (i = 0; i < total_instruction; i++) //将指令序列变换成页地址流
	{
		page[i] = a[i] / 10;
		offset[i] = a[i] % 10;
	}

	printf("页号\\命中率:    OPT\tFIFO\tLRU\n");
	for (i = 4; i <= 32; i++)   //用户内存工作区从4个页面到32个页面
	{
		printf(" %2d \t\t", i);

		OPT(i);
		FIFO(i);
		LRU(i);
		printf("\n");
	}

	printf("平均命中率:\t");
	for (i = 0; i < 3; i++)
	{
		printf("%6.4f\t", diseffect_vag[i] / 29);
	}
	printf("\n");
	return 0;

}
		
		
int init(total_pf)  //初始化相关数据结构
int total_pf;      //用户进程的内存页面数
{
	int i;
	diseffect = 0;
	for (i = 0; i < total_vp; i++)
	{
		pl[i].pn = i;
		pl[i].pfn = INVALID;   //置页面控制结构中的页号,页面为空
		pl[i].counter = 0;
		pl[i].time = -1;  //页面控制结构中的访问次数为0,时间为-1
	}

	for (i = 0; i < total_pf - 1; i++)
	{
		pfc[i].next = &pfc[i + 1];
		pfc[i].pfn = i;
	}   //建立pfc[i-1]和pfc[i]之间的链接

	pfc[total_pf - 1].next = NULL;
	pfc[total_pf - 1].pfn = total_pf - 1;
	freepf_head = &pfc[0];  //空页面队列的头指针为pfc[0]
	return 0;
}



int OPT(total_pf)  //最佳置换算法
int total_pf;
{
	int i, j, max, maxpage, d, dist[total_vp];
	pfc_type* t;
	init(total_pf);
	for (i = 0; i < total_instruction; i++)
	{
		if (pl[page[i]].pfn == INVALID) //页面失效
		{
			diseffect++;
			if (freepf_head == NULL)    //无空闲页面
			{
				for (j = 0; j < total_vp; j++)
					if (pl[j].pfn != INVALID)
						dist[j] = 32767;  //最大"距离"
					else
						dist[j] = 0;

				d = 1;

				for (j = i + 1; j < total_instruction; j++)
				{
					if (pl[page[j]].pfn != INVALID)
						dist[page[j]] = d;
					d++;
				}

				max = -1;
				for (j = 0; j < total_vp; j++)
					if (max < dist[j])
					{
						max = dist[j];
						maxpage = j;
					}
				freepf_head = &pfc[pl[maxpage].pfn];
				freepf_head->next = NULL;
				pl[maxpage].pfn = INVALID;
			}
			pl[page[i]].pfn = freepf_head->pfn;
			freepf_head = freepf_head->next;
		}
	}
	diseffect_vag[0] += 1 - (float)diseffect / 320;
	printf("%6.4f\t", 1 - (float)diseffect / 320);
	return 0;
}

int FIFO(total_pf)    //先进先出算法
int total_pf;   //用户进程的内存页面数
{
	int i, j;
	pfc_type* p;
	init(total_pf);  //初始化相关页面控制用数据结构
	busypf_head = busypf_tail = NULL; //忙页面队列头,队列尾链接
	for (i = 0; i < total_instruction; i++)
	{
		if (pl[page[i]].pfn == INVALID)   //页面失效
		{
			diseffect += 1;  //失效次数
			if (freepf_head == NULL)         //无空闲页面
			{
				p = busypf_head->next;
				pl[busypf_head->pn].pfn = INVALID;
				freepf_head = busypf_head;  //释放忙页面队列的第一个页面
				freepf_head->next = NULL;
				busypf_head = p;
			}
			p = freepf_head->next; //按FIFO方式调新页面入内存页面
			freepf_head->next = NULL;
			freepf_head->pn = page[i];
			pl[page[i]].pfn = freepf_head->pfn;

			if (busypf_tail == NULL)
				busypf_head = busypf_tail = freepf_head;
			else
			{
				busypf_tail->next = freepf_head;  //free页面减少一个
				busypf_tail = freepf_head;
			}
			freepf_head = p;
		}
	}
	diseffect_vag[1] += 1 - (float)diseffect / 320;
	printf("%6.4f\t", 1 - (float)diseffect / 320);
	return 0;
}


int LRU (total_pf)   //最近最久未使用算法
int total_pf;
{
	int min, minj, i, j, present_time;
	init(total_pf);
	present_time = 0;
	for (i = 0; i < total_instruction; i++)
	{
		if (pl[page[i]].pfn == INVALID) //页面失效
		{
			diseffect++;
			if (freepf_head == NULL)  //无空闲页面
			{
				min = 32767;
				for (j = 0; j < total_vp; j++)   //找出time的最小值
					if (min > pl[j].time && pl[j].pfn != INVALID)
					{
						min = pl[j].time;
						minj = j;
					}
				freepf_head = &pfc[pl[minj].pfn]; //腾出一个单元
				pl[minj].pfn = INVALID;
				pl[minj].time = -1;
				freepf_head->next = NULL;
			}
			pl[page[i]].pfn = freepf_head->pfn;   //有空闲页面,改为有效
			pl[page[i]].time = present_time;
			freepf_head = freepf_head->next;   //减少一个free 页面
		}
		else
			pl[page[i]].time = present_time;  //命中则增加该单元的访问次数       
		present_time++;
	}
	diseffect_vag[2] += 1 - (float)diseffect / 320;
	printf("%6.4f\t", 1 - (float)diseffect / 320);
	return 0;
}

操作系统--内存管理_第4张图片
操作系统--内存管理_第5张图片
操作系统--内存管理_第6张图片
操作系统--内存管理_第7张图片
6、 根据实验结果与理论课讲述的原理进行实验分析。

从大量的实验结果中得出,最佳置换算法(OPT)拥有最高的命中率,而先进先出置换算法(FIFO)和最近最久未使用(LRU)算法的命中率相差不大,先进先出置换算法(FIFO)在某种情况下命中率比最近最久未使用(LRU)算法命中率高,但是在某种情况下最近最久未使用(LRU)算法命中率又比先进先出置换算法(FIFO)命中率高。

(1)、 最佳置换算法(OPT)

最优置换(Optimal Replacement)是在理论上提出的一种算法。其实质是:当调入新的一页而必须预先置换某个老页时,所选择的老页应是将来不再被使用,或者是在最远的将来才被访问。采用这种页面置换算法,保证有最少的缺页率。

但是最优页面置换算法的实现是困难的,因为它需要人们预先就知道一个进程整个运行过程中页面走向的全部情况。

(2)、 先进先出置换算法(FIFO)

最简单的页面置换算法是先入先出(FIFO)法。这种算法的实质是,总是选择在主存中停留时间最长(即最老)的一页置换,即先进入内存的页,先退出内存。理由是:最早调入内存的页,其不再被使用的可能性比刚调入内存的可能性大。建立一个FIFO队列,收容所有在内存中的页。被置换页面总是在队列头上进行。当一个页面被放入内存时,就把它插在队尾上。

这种算法只是在按线性顺序访问地址空间时才是理想的,否则效率不高。因为那些常被访问的页,往往在主存中也停留得最久,结果它们因变“老”而不得不被置换出去。

FIFO的另一个缺点是,它有一种异常现象,即在增加存储块的情况下,反而使缺页中断率增加了。

(3)、 最近最久未使用(LRU)算法

FIFO算法和OPT算法之间的主要差别是,FIFO算法利用页面进入内存后的时间长短作为置换依据,而OPT算法的依据是将来使用页面的时间。如果以最近的过去作为不久将来的近似,那么就可以把过去最长一段时间里不曾被使用的页面置换掉。它的实质是,当需要置换一页时,选择在最近一段时间里最久没有使用过的页面予以置换。这种算法就称为最久未使用算法(Least Recently Used,LRU)。

LRU算法是与每个页面最后使用的时间有关的。当必须置换一个页面时,LRU算法选择过去一段时间里最久未被使用的页面。LRU算法是经常采用的页面置换算法,并被认为是相当好的,但是存在如何实现它的问题。LRU算法需要实际硬件的支持。

思考题

1、从几种算法的命中率看,哪个算法最高?哪个算法最低?对每个页面的执行结果进行分析。

答:由实验结果截图,从几种算法的命中率看,OPT最高,有时FIFO最低,有时与LRU最低,每个页面执行结果会有所不同,但是FIFO与LRU相差不大。

最佳置换算法(Optimal):它所选择的被淘汰页面,将是在最长(未来)时间内不再被访问的或则是以后永不使用的页面。所以使用该置换算法,通常可以使得缺页率最低。

先进先出(FIFO)页面置换算法:该算法总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面予以淘汰。

最近最少使用(LRU)置换算法:算法根据数据的历史访问记录来进行淘汰数据,把历史访问记录的最久没有被访问到的数据淘汰。

因为先进先出(FIFO)页面置换算法和最近最少使用(LRU)置换算法都是根据历史数据选择页面置换,是利用“最近的过去”作为“最近的将来”的近似,所以具有一定的不确定性,因而缺页率通常也较大。而最佳置换算法(Optimal)是根据未来的数据选择页面置换,因此该算法的缺页率最低。

2、OPT算法在执行过程中可能会发生错误,为什么?

最佳置换算法(Optimal是选择以后永不使用的或许是在最长(未来)时间内不再被访问的页面淘汰。但由于计算机目前还无法预知一个进程在内存的若干个页面中,哪一个页面是未来最长时间内不再被访问的,因而该算法是无法实现的。

你可能感兴趣的:(操作系统)