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

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

一、实验目的

  1. 存储管理的主要功能之一是合理地分配空间。请求页式管理是一种常用的虚拟存储管理技术。
  2. 本实验的目的是通过请求页式存储管理中页面置换算法模拟设计,了解虚拟存储技术的技术特点,掌握请求页式存储管理的页面置换算法。

二、实验环境

实验环境如下表所示

名称配置信息
操作系统Xubuntu(实验楼)
编程语言C语言
编译器GCC-12
fork.c所属内核Linux-6.8.7

三、实验问题

  1. 操作系统是如何管理虚拟地址与物理地址之间的关系?
  2. 什么是虚拟存储技术,为什么要使用虚拟内存技术?
  3. 什么是页地址流?
  4. 请求页式管理是一种什么技术?

四、实验内容

  1. 通过随机数产生一个指令序列,共320条指令。指令的地址按下述原则生成:

①50%的指令是顺序执行的;

②50%的指令是均匀分布在前地址部分;

③50%的指令是均匀分布在后地址部分。

具体的实施方法是:

①在 [0,319] 的指令之间随即选取一起点m;

②顺序执行一条指令,即执行地址为m+1的指令;

③在前地址[0,m+1]中随机选取一条指令并执行,该指令的地址为m′;

④顺序执行一条指令,其地址为 m′+ 1;

⑤在后地址[m′+ 2,319]中随机选取一条指令并执行;

⑥重复上述步骤①-⑤,直到执行320次指令。

  1. 将指令序列变换为页地址流

设:①页面大小为1k;

②用户内存容量为4页到32页;

③用户虚存容量为32k。

在用户虚存中,按每k存放10条指令排在虚存地址,即320条指令在虚存中的存放方式为:

第0条-第9条指令为第0页(对应虚存地址为[0,9]);

第10条-第19条指令为第一页(对应虚存地址为[10,19]);

​ … …

第310条~第319条指令为第31页(对应虚地址为[310,319])。

按以上方式,用户指令可组成32页。

  1. 计算并输出下述各种算法在不同内存容量下的命中率。

①先进先出的算法(FIFO);

②最近最少使用算法(LRU算法);

③最佳淘汰算法(OPT)先淘汰最不常用的页地址;

④第二次机会页面替换算法(SCR算法);

⑤CLCOK算法又称为最近未使用算法(NRU)。

​ 其中③、④、⑤为选择内容。

命中率=1-页面失效次数/页地址流长度

在本实验中,页地址流长度为320,页面失效次数为每次访问相应指令时,该指令所对应的页不在内存的次数。

五、实验步骤

5.1方案设计

5.1.1算法分析

(1)FIFO算法:这个算法的基本思想是总是先淘汰一些驻留在内存时间最长的页面,即先进入内存的页面先被置换掉。作业只要把进入内存的各个页面按进入的时间次序用链表链接起来,设定一个链表首指针指向进入内存最早的一个页面,新进入的页面放在链表的尾部,需要淘汰某一个页面时,总是淘汰替换指针所指向的页面即可。先进先出算法在程序按线性顺序访问逻辑地址空间时比较理想,否则效率不高。特别是在遇到循环执行的程序段时,往往会把频繁访问的页面,因其在内存驻留时间过长,而周期性地淘汰掉。

(2)OPT算法:这是最理想的页面置换算法,从内存中移出以后不再使用的页面;如无这样的页面,则选择以后最长时间内不需要访问的页面。本算法因为页面访问的顺序是很难预知的,所以不是一种实际的方法。

(3)LRU算法:本算法的基本思想是,如果某一页被访问了,那么它很可能马上又被访问;反之,如果某一页很长时间没有被访问,那么最近也不太可能会被访问。这种算法考虑了程序设计的局部性原理。其实质是:当需要置换一页时,选择最近一段时间内最久未使用的页面予以淘汰。

(4)SCR算法:此算法的本质就是设置一个访问位当淘汰一个页面时,要检查其访问位:若访问位是1,给它第二次机会,选择下一个FIFO页面,并将其访问位置为0;若访问位是0,则淘汰它。另外,访问到访问位为0的页面,将其访问位重新置为1

(5)Clock算法:又称最近未用算法(NRU)。该算法将内存中所有页面都通过链接指针链接成一个循环队列,设置访问位,1表示被访问过,0表示未被访问过;如果访问位是0,则选择该页换出,如果访问位是1,则将访问位置0,暂不换出,再按照FIFO算法检查下一个页面,重复上述步骤。

5.1.2算法设计

在本实验中,我们通过对五种算法进行了解,并通过C语言编程调用实现,并打印结果,了解操作系统在内存管理是如何实现的。

(1) FIFO算法

  • 每次选择淘汰的页面是最早进入内存的页面

  • 实现方法:把调入内存的页面根据调入的先后顺序排成一个队列,需要换出页面时选择队头页面即可。

  • 队列的最大长度取决于系统为进程分配了多少个内存块。

(2) OPT算法

  • 每次选择淘汰的页面将是以后永不使用,或者在最长时间内不再被访问的页面,这样可以保证最低的缺页率。
  • 实现方法: 输入页面号引用串,如果页框中的某个页面P以后永不使用,则该页面为淘汰页面Pt。如果每个P都会再次被访问,那么其中最长未来时间内不再被访问的页面为淘汰页面Pt。

(3) LRU算法

  • 每次淘汰的页面是最近最久未使用的页面
  • 实现方法:赋予每个页面对应的页表项中,用访问字段记录该页面自上次被访问以来所经历的时间t。
  • 当需要淘汰一个页面时,选择现有页面中 t 值最大的,即最近最久未使用的页面。

(4) SCR算法

  • 第二次机会算法的基本思想是与FIFO相同的,但是有所改进,避免把经常使用的页面置换出去。
  • 实现方式:设置一个访问位。当淘汰一个页面时,要检查其访问位:若访问位是1,给它第二次机会,选择下一个FIFO页面,并将其访问位置为0;若访问位是0,则淘汰它。另外,访问到访问位为0的页面,将其访问位重新置为1。

(5) Clock算法

  • 时钟置换算法是一种性能和开销较均衡的算法,又称CLOCK算法,或最近未用算法(NRU)
  • 简单的CLOCK 算法实现方法:为每个页面设置一个访问位,再将内存中的页面都通过链接指针链接成一个循环队列。

​ ①、当某页被访问时,其访问位置为1。当需要淘汰一个页面时,只需检查页的访问位。

​ ②、如果是0,就选择该页换出;如果是1,则将它置为0,暂不换出,继续检查下一个页面,若第一轮扫描中所有页面都是1,则将这些页面的访问位依次置为0后,再进行第二轮扫描(第二轮扫描中一定会有访问位为0的页面,因此简单的CLOCK 算法选择一个淘汰页面最多会经过两轮扫描)

5.2 所用函数说明

  1. 函数typedef struct:定义一个队列的顺序存储结构

  2. 函数void Init():初始化数据

  3. 函数void OPT(int n):实现OPT算法

  4. 函数void FIFO(int n):实现FIFO算法

  5. 函数void LRU(int n):实现LRU算法

  6. 函数void SCR(int n):实现SCR算法

  7. 函数void Clock(int n):实现Clock算法

  8. 函数int main():主函数,实现初始化和函数调用

5.3 模块代码及其相关注释

调用头函数,进行宏定义

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 320  //指令数
#define MAX 500
int instruction[N];     //指令序列
int page[N];            //记录指令所在页面

定义队列的顺序存储结构

typedef struct
{
	int* base;    //存储空间的基地址
	int front;
	int rear;
}SqQueue;

初始化数据

void Init()
{
	srand((unsigned int)time(NULL));//生成随机种子
	int index = 0;
	int x = 0;
	int y = 0;
	int z = 0;
	int i;
	while (index < N)
	{
		x = rand() % N; //在[0,319]的指令地址之间随机选取一起点m
		while (x > N - 2)
		{
			x = rand() % N;
		}
		instruction[index++] = x + 1;//顺序执行一条指令,即执行地址为m+1的指令(记录顺序)   
		y = rand() % (x + 1);  //在前地址[0,m+1]中随机选取一条指令并执行,该指令的地址为m’
		while (y > N - 3)
		{
			y = rand() % (x + 1);
		}
		instruction[index++] = y;
		instruction[index++] = y + 1;// 顺序执行一条指令,其地址为m’ + 1
		z = rand() % (N - (y + 2)) + y + 2;   //在后地址[m’+2,319]中随机选取一条指令
		instruction[index++] = z;
	}
	for (i = 0; i < N; i++)
	{
		page[i] = instruction[i] / 10; //每条指令对应的页面
	}
	printf("随机生成的每条指令所在页面为:\n");
	for (i = 0; i < N; i++)
	{
		if ((i % 10 == 0) && (i != 0))
			printf("\n");
		printf("%d\t", page[i]);
	}
}

OPT算法

void OPT(int n)
{
	int i, j, k;
	int test, test2; //test标志该页是否在内存中,test2标志内存是否还有空间 
	int* next_time = NULL; //页面下次出现时间
	next_time = (int*)malloc(sizeof(int) * n);
	int* memory = NULL;//内存空间
	memory = (int*)malloc(sizeof(int) * n);
	int index = 0;   //距离下次访问最远的页面的下标
	double count = 0; //定义命中次数
	for (i = 0; i < n; i++)
	{
		next_time[i] = -1;  //初始化数组
		memory[i] = -1;
	}
	for (i = 0; i < N; i++)
	{
		test = 0;
		for (j = 0; j < n; j++)
		{
			if (memory[j] == page[i]) //查找该页是否在内存中
			{
				test = 1;
				break;
			}
		}
		if (test == 1)  //test=1,该页在内存中
		{
			count += 1;  //命中数+1  
		}
		else//当发送缺页中断时
		{
			test2 = 0;
			for (j = 0; j < n; j++) //缺页中断
			{
				if (memory[j] == -1)  //检查是否还有内存空间   
				{
					test2 = 1;            //说明有多余空间,test2=1;
					next_time[j] = MAX;
					memory[j] = page[i];    //新的页面进入内存空间
					break;
				}
			}
			if (test2 == 0)//当内存空间已满时
			{
				for (int j = 0; j < n; j++)
				{
					next_time[j] = MAX;
					for (k = i + 1; k < N; k++)
					{
						if (memory[j] == page[k])
						{
							next_time[j] = k - i; //求出内存中的页面下一次出现的位置
							break;
						}
					}
				}
				for (j = 0; j < n; j++)
				{
					if (next_time[j] > next_time[index])
						index = j;   //找出距离下次访问最远的页面,求出下标index
				}
				memory[index] = page[i];//找到页面,替换该页面
			}
		}
	}
	printf("内存容量为%d页时,命中率为%lf", n, count / N);
	printf("\n");
	free(next_time);
	free(memory);
}

FIFO算法

void FIFO(int n)
{
	SqQueue Q;//生成队列
	//初始化队列
	Q.base = (int*)malloc(sizeof(int) * n);
	if (!Q.base)
		exit(1);
	Q.front = Q.rear = 0;
	double count = 0;   //定义命中次数
	int test;//test标志该页是否在内存中
	for (int i = 0; i < N; i++)
	{
		test = 0;
		int temp = Q.front;//标记队列的第一个
		while (temp != Q.rear)
		{
			if (Q.base[temp] == page[i])     //查找该页是否在内存中
			{
				test = 1;//
				break;
			}
			temp = (temp + 1) % n;
		}
		if (test == 1)//该页在内存中
		{
			count += 1;   //命中数量+1
		}
		else                //发生缺页中断
		{
			if ((Q.rear + 1) % n == Q.front)             //如果内存已满
			{
				Q.front = (Q.front + 1) % n;  //最先进去的页面淘汰,即出队操作
				Q.base[Q.rear] = page[i];     //最新的页面进入内存,即入队操作
				Q.rear = (Q.rear + 1) % n;
			}
			else                                        //当内存没满
			{
				Q.base[Q.rear] = page[i];               //新的页面进入内存,即入队操作
				Q.rear = (Q.rear + 1) % n;
			}
		}
	}
	printf("内存容量为%d页时命中率为%lf", n, count / N);
	printf("\n");
	free(Q.base);//释放资源

}

LRU算法

void LRU(int n)
{
	int i, j, k;
	int* memory = NULL;
	memory = (int*)malloc(sizeof(int) * n); //内存空间
	int* sign = NULL;
	sign = (int*)malloc(sizeof(int) * n);     //标志内存中的页上次访问到现在的时间
	double count = 0;    //定义命中次数
	int test, test2;    //test标志该页是否在内存中,test2标志内存是否还有空间 
	int index;         //设置为最久未访问页面的下标
	for (i = 0; i < n; i++)
	{
		memory[i] = -1; //初始化数组
		sign[i] = -1;
	}
	for (i = 0; i < N; i++)
	{
		//更新标记时间的值
		for (j = 0; j < n; j++)
		{
			if (sign[j] != -1)
			{
				sign[j] += 1;    //页面上次访问到现在时间+1
			}
		}
		test = 0;
		for (j = 0; j < n; j++)
		{
			if (memory[j] == page[i])           //查找该页是否在内存中
			{
				test = 1;
				sign[j] = 0;               //访问时间置0
				break;
			}
		}
		if (test == 1)          //test=1,该页在内存中
		{
			count += 1;     //命中+1
		}
		else                //引发缺页中断
		{
			test2 = 0;
			for (k = 0; k < n; k++)
			{
				if (memory[k] == -1)        //检查是否还有内存空间   
				{
					test2 = 1;          //有多余空间,test2=1;
					memory[k] = page[i];  //新的页面进入内存
					sign[k] = 0;     //访问时间置0
					break;
				}
			}
			if (test2 == 0)   //如果内存已满
			{
				index = 0;
				for (k = 0; k < n; k++)
				{
					if (sign[index] < sign[k])
						index = k;       //找出上次访问到现在最久的页面,找到下标index
				}
				memory[index] = page[i];       //替换该页面
			}

		}
	}
	printf("内存容量为%d页时命中率为%lf", n, count / N);
	printf("\n");
	free(memory);
	free(sign);

}

SCR算法

void SCR(int n) {
	int* memory = (int*)malloc(sizeof(int) * n); // 内存空间  
	int* refBit = (int*)malloc(sizeof(int) * n);
	int* counter = (int*)malloc(sizeof(int) * n);
	int hitCount = 0; // 命中次数  
	int initialHand = 0; // 初始hand位置  
	int found = 0; // 用于标记页面是否在内存中  
	int i, j;
	int hand = 0;

	// 初始化内存、引用位和访问时间  
	for (i = 0; i < n; i++) {
		memory[i] = -1; // 假设初始时没有页面在内存中  
		refBit[i] = 0; // 引用位初始化为0  
		counter[i] = 0; // 访问时间初始化为0  
	}

	// 遍历页面序列  
	for (i = 0; i < N; i++) {
		int pageFault = page[i]; // 当前页面  

		// 查找页面是否在内存中  
		for (j = 0; j < n; j++) {
			if (memory[j] == pageFault) {
				found = 1; // 页面在内存中  
				refBit[j] = 0; // 访问了该页面,将引用位设置为0  
				counter[j] = 0; // 重置访问时间  
				break;
			}
		}

		// 页面在内存中,增加命中次数  
		if (found) {
			hitCount++;
			found = 0; // 重置found标志,为下一个页面做准备  
		}
		else {
			// 页面不在内存中,发生缺页中断  
			found = 0; // 重置found标志,用于下面的循环  
			initialHand = hand = 0; // 初始化hand并设置initialHand  
			do {
				// 如果R位为0,则替换当前页面  
				if (refBit[hand] == 0) {
					memory[hand] = pageFault;
					refBit[hand] = 1; // 设置新页面的R位为1  
					counter[hand] = 0; // 重置新页面的访问时间  
					break;
				}

				// 如果R位为1,则增加访问时间并继续查找  
				counter[hand]++;

				// 继续查找下一个页面  
				hand = (hand + 1) % n;

				// 检查是否已经遍历了所有页面  
				if (hand == initialHand) {
					// 如果所有页面的R位都为1,则替换hand指向的页面(即最旧的页面)  
					int oldest = hand;
					for (j = (hand + 1) % n; j != hand; j = (j + 1) % n) {
						if (counter[j] > counter[oldest]) {
							oldest = j;
						}
					}
					memory[oldest] = pageFault;
					refBit[oldest] = 1; // 设置新页面的R位为1  
					counter[oldest] = 0; // 重置新页面的访问时间  
					break;
				}
			} while (1); // 循环直到找到可替换的页面  
		}
	}

	// 计算并输出命中率  
	printf("内存容量为%d页时命中率为%lf\n", n, (double)hitCount / N);


	// 释放内存  
	free(memory);
	free(refBit);
	free(counter);
}

Clock算法

void Clock(int n) {
	int hitCount = 0; // 命中次数  
	int hand = 0; // 模拟循环队列的指针(hand in hand)  
	int i;
	int* memory = NULL;
	memory = (int*)malloc(sizeof(int) * n); //内存空间
	int* refBit = NULL;
	refBit = (int*)malloc(sizeof(int) * n);

	// 初始化内存和引用位  
	for (i = 0; i < n; i++) {
		memory[i] = -1; // 假设初始时没有页面在内存中  
		refBit[i] = 0; // 引用位初始化为0  
	}

	// 遍历页面序列  
	for (i = 0; i < N; i++) {
		int pageFault = page[i]; // 当前页面  
		int found = 0; // 标记页面是否在内存中  

		// 查找页面是否在内存中  
		for (int j = 0; j < n; j++) {
			if (memory[j] == pageFault) {
				found = 1; // 页面在内存中  
				refBit[j] = 0; // 访问了该页面,将引用位设置为0  
				break;
			}
		}

		// 页面在内存中,增加命中次数  
		if (found) {
			hitCount++;
		}
		else {
			// 页面不在内存中,发生缺页中断  
			// 查找第一个R位为0的页面或者循环回到开始并重置所有R位  
			do {
				if (refBit[hand] == 0) {
					// 找到R位为0的页面,替换它  
					memory[hand] = pageFault;
					refBit[hand] = 1; // 设置新页面的R位为1  
					break;
				}
				else {
					// 如果没有找到,则继续查找下一个页面,或者重置所有R位  
					hand = (hand + 1) % n; // 循环队列  
					if (hand == 0) { // 如果回到开始,重置所有R位  
						for (int k = 0; k < n; k++) {
							refBit[k] = 0;
						}
					}
				}
			} while (hand != 0 || refBit[hand] == 1); // 循环直到找到可替换的页面或回到开始  
		}
	}
	// 计算并输出命中率  
	printf("内存容量为%d页时命中率为%lf\n", n, (double)hitCount / N);

	free(memory);
	free(refBit);
}

主函数,调用函数

void main()
{
	printf("----------------------------------初始化操作----------------------------------\n");
	Init(); //初始化
	printf("\n");
	printf("---------------------------------最佳淘汰算法(OPT)---------------------------------\n");
	for (int i = 4; i <= 32; i++)
	{
		OPT(i);         //最佳淘汰算法,用户的内存容量4页到32页
	}
	printf("\n");
	printf("--------------------------------先进先出算法(FIFO)---------------------------------\n");
	for (int j = 4; j <= 32; j++)
	{
		FIFO(j);         //先进先出算法,用户的内存容量4页到32页
	}
	printf("\n");
	printf("------------------------------最近最久未使用算法(LRU)------------------------------\n");
	for (int t = 4; t <= 32; t++)
	{
		LRU(t);          //最近最久未使用算法,用户的内存容量4页到32页
	}
	printf("\n");
	printf("------------------------------第二次机会页面替换算法(SCR)------------------------------\n");
	for (int b = 4; b <= 32; b++)
	{
		SCR(b);          //第二次机会页面替换算法,用户的内存容量4页到32页
	}
	printf("\n");
	printf("------------------------------时钟页面替换算法(Clock)------------------------------\n");
	for (int t = 4; t <= 32; t++)
	{
		Clock(t);          //时钟页面替换算法,用户的内存容量4页到32页
	}
	system("pause");
}

运行结果展示

初始化随机数生成:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

OPT算法运行结果展示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

FIFO算法运行结果展示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

LRU算法运行结果展示:
在这里插入图片描述

SCR算法运行结果展示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Clock算法运行结果展示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

六、实验结论

通过切实的实验与分析,我对于操作系统课程的分页存储管理了解大大的加深了,所有的东西都不再是纸上谈兵,从基础的五种算法的实现:OPT,FIFO,LUR,SCR,Clock,可以深入的了解操作系统中一些原理性的东西,他的运行,他的工作方式,不只是靠自己对着课本的文字去想想了。

实验五通过算法来对主存实现分配的过程中,更是深刻的体会到其中的微妙。五种算法分页管理中的运行结果出现之后一目了然,链表方式也是方便自如。是让书上不动的文字活了起来。

整个实验下来,无论是翻课本查原理,还是上网查阅资料,都是促进我们学习实践的一大助力,让课堂上一些死板的知识流转在手指之间,跃现在荧屏上面。

本实验全部代码及注释

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 320  //指令数
#define MAX 500
int instruction[N];     //指令序列
int page[N];            //记录指令所在页面
//队列的顺序存储结构
typedef struct
{
	int* base;    //存储空间的基地址
	int front;
	int rear;
}SqQueue;
//初始化数据
void Init()
{
	srand((unsigned int)time(NULL));//生成随机种子
	int index = 0;
	int x = 0;
	int y = 0;
	int z = 0;
	int i;
	while (index < N)
	{
		x = rand() % N; //在[0,319]的指令地址之间随机选取一起点m
		while (x > N - 2)
		{
			x = rand() % N;
		}
		instruction[index++] = x + 1;//顺序执行一条指令,即执行地址为m+1的指令(记录顺序)   
		y = rand() % (x + 1);  //在前地址[0,m+1]中随机选取一条指令并执行,该指令的地址为m’
		while (y > N - 3)
		{
			y = rand() % (x + 1);
		}
		instruction[index++] = y;
		instruction[index++] = y + 1;// 顺序执行一条指令,其地址为m’ + 1
		z = rand() % (N - (y + 2)) + y + 2;   //在后地址[m’+2,319]中随机选取一条指令
		instruction[index++] = z;
	}
	for (i = 0; i < N; i++)
	{
		page[i] = instruction[i] / 10; //每条指令对应的页面
	}
	printf("随机生成的每条指令所在页面为:\n");
	for (i = 0; i < N; i++)
	{
		if ((i % 10 == 0) && (i != 0))
			printf("\n");
		printf("%d\t", page[i]);
	}
}
//最佳淘汰算法(最佳页面替换算法)P235
void OPT(int n)
{
	int i, j, k;
	int test, test2; //test标志该页是否在内存中,test2标志内存是否还有空间 
	int* next_time = NULL; //页面下次出现时间
	next_time = (int*)malloc(sizeof(int) * n);
	int* memory = NULL;//内存空间
	memory = (int*)malloc(sizeof(int) * n);
	int index = 0;   //距离下次访问最远的页面的下标
	double count = 0; //定义命中次数
	for (i = 0; i < n; i++)
	{
		next_time[i] = -1;  //初始化数组
		memory[i] = -1;
	}
	for (i = 0; i < N; i++)
	{
		test = 0;
		for (j = 0; j < n; j++)
		{
			if (memory[j] == page[i]) //查找该页是否在内存中
			{
				test = 1;
				break;
			}
		}
		if (test == 1)  //test=1,该页在内存中
		{
			count += 1;  //命中数+1  
		}
		else//当发送缺页中断时
		{
			test2 = 0;
			for (j = 0; j < n; j++) //缺页中断
			{
				if (memory[j] == -1)  //检查是否还有内存空间   
				{
					test2 = 1;            //说明有多余空间,test2=1;
					next_time[j] = MAX;
					memory[j] = page[i];    //新的页面进入内存空间
					break;
				}
			}
			if (test2 == 0)//当内存空间已满时
			{
				for (int j = 0; j < n; j++)
				{
					next_time[j] = MAX;
					for (k = i + 1; k < N; k++)
					{
						if (memory[j] == page[k])
						{
							next_time[j] = k - i; //求出内存中的页面下一次出现的位置
							break;
						}
					}
				}
				for (j = 0; j < n; j++)
				{
					if (next_time[j] > next_time[index])
						index = j;   //找出距离下次访问最远的页面,求出下标index
				}
				memory[index] = page[i];//找到页面,替换该页面
			}
		}
	}
	printf("内存容量为%d页时,命中率为%lf", n, count / N);
	printf("\n");
	free(next_time);
	free(memory);
}
//先进先出法(先进先出页面替换算法)
void FIFO(int n)
{
	SqQueue Q;//生成队列
	//初始化队列
	Q.base = (int*)malloc(sizeof(int) * n);
	if (!Q.base)
		exit(1);
	Q.front = Q.rear = 0;
	double count = 0;   //定义命中次数
	int test;//test标志该页是否在内存中
	for (int i = 0; i < N; i++)
	{
		test = 0;
		int temp = Q.front;//标记队列的第一个
		while (temp != Q.rear)
		{
			if (Q.base[temp] == page[i])     //查找该页是否在内存中
			{
				test = 1;//
				break;
			}
			temp = (temp + 1) % n;
		}
		if (test == 1)//该页在内存中
		{
			count += 1;   //命中数量+1
		}
		else                //发生缺页中断
		{
			if ((Q.rear + 1) % n == Q.front)             //如果内存已满
			{
				Q.front = (Q.front + 1) % n;  //最先进去的页面淘汰,即出队操作
				Q.base[Q.rear] = page[i];     //最新的页面进入内存,即入队操作
				Q.rear = (Q.rear + 1) % n;
			}
			else                                        //当内存没满
			{
				Q.base[Q.rear] = page[i];               //新的页面进入内存,即入队操作
				Q.rear = (Q.rear + 1) % n;
			}
		}
	}
	printf("内存容量为%d页时命中率为%lf", n, count / N);
	printf("\n");
	free(Q.base);//释放队列

}
//最近最久未使用算法(最近最少使用页面替换算法)
void LRU(int n)
{
	int i, j, k;
	int* memory = NULL;
	memory = (int*)malloc(sizeof(int) * n); //内存空间
	int* sign = NULL;
	sign = (int*)malloc(sizeof(int) * n);     //标志内存中的页上次访问到现在的时间
	double count = 0;    //定义命中次数
	int test, test2;    //test标志该页是否在内存中,test2标志内存是否还有空间 
	int index;         //设置为最久未访问页面的下标
	for (i = 0; i < n; i++)
	{
		memory[i] = -1; //初始化数组
		sign[i] = -1;
	}
	for (i = 0; i < N; i++)
	{
		//更新标记时间的值
		for (j = 0; j < n; j++)
		{
			if (sign[j] != -1)
			{
				sign[j] += 1;    //页面上次访问到现在时间+1
			}
		}
		test = 0;
		for (j = 0; j < n; j++)
		{
			if (memory[j] == page[i])           //查找该页是否在内存中
			{
				test = 1;
				sign[j] = 0;               //访问时间置0
				break;
			}
		}
		if (test == 1)          //test=1,该页在内存中
		{
			count += 1;     //命中+1
		}
		else                //引发缺页中断
		{
			test2 = 0;
			for (k = 0; k < n; k++)
			{
				if (memory[k] == -1)        //检查是否还有内存空间   
				{
					test2 = 1;          //有多余空间,test2=1;
					memory[k] = page[i];  //新的页面进入内存
					sign[k] = 0;     //访问时间置0
					break;
				}
			}
			if (test2 == 0)   //如果内存已满
			{
				index = 0;
				for (k = 0; k < n; k++)
				{
					if (sign[index] < sign[k])
						index = k;       //找出上次访问到现在最久的页面,找到下标index
				}
				memory[index] = page[i];       //替换该页面
			}

		}
	}
	printf("内存容量为%d页时命中率为%lf", n, count / N);
	printf("\n");
	free(memory);
	free(sign);

}
//第二次机会页面替换算法(SCR)
void SCR(int n) {
	int* memory = (int*)malloc(sizeof(int) * n); // 内存空间  
	int* refBit = (int*)malloc(sizeof(int) * n);
	int* counter = (int*)malloc(sizeof(int) * n);
	int hitCount = 0; // 命中次数  
	int initialHand = 0; // 初始hand位置  
	int found = 0; // 用于标记页面是否在内存中  
	int i, j;
	int hand = 0;

	// 初始化内存、引用位和访问时间  
	for (i = 0; i < n; i++) {
		memory[i] = -1; // 假设初始时没有页面在内存中  
		refBit[i] = 0; // 引用位初始化为0  
		counter[i] = 0; // 访问时间初始化为0  
	}

	// 遍历页面序列  
	for (i = 0; i < N; i++) {
		int pageFault = page[i]; // 当前页面  

		// 查找页面是否在内存中  
		for (j = 0; j < n; j++) {
			if (memory[j] == pageFault) {
				found = 1; // 页面在内存中  
				refBit[j] = 0; // 访问了该页面,将引用位设置为0  
				counter[j] = 0; // 重置访问时间  
				break;
			}
		}

		// 页面在内存中,增加命中次数  
		if (found) {
			hitCount++;
			found = 0; // 重置found标志,为下一个页面做准备  
		}
		else {
			// 页面不在内存中,发生缺页中断  
			found = 0; // 重置found标志,用于下面的循环  
			initialHand = hand = 0; // 初始化hand并设置initialHand  
			do {
				// 如果R位为0,则替换当前页面  
				if (refBit[hand] == 0) {
					memory[hand] = pageFault;
					refBit[hand] = 1; // 设置新页面的R位为1  
					counter[hand] = 0; // 重置新页面的访问时间  
					break;
				}

				// 如果R位为1,则增加访问时间并继续查找  
				counter[hand]++;

				// 继续查找下一个页面  
				hand = (hand + 1) % n;

				// 检查是否已经遍历了所有页面  
				if (hand == initialHand) {
					// 如果所有页面的R位都为1,则替换hand指向的页面(即最旧的页面)  
					int oldest = hand;
					for (j = (hand + 1) % n; j != hand; j = (j + 1) % n) {
						if (counter[j] > counter[oldest]) {
							oldest = j;
						}
					}
					memory[oldest] = pageFault;
					refBit[oldest] = 1; // 设置新页面的R位为1  
					counter[oldest] = 0; // 重置新页面的访问时间  
					break;
				}
			} while (1); // 循环直到找到可替换的页面  
		}
	}

	// 计算并输出命中率  
	printf("内存容量为%d页时命中率为%lf\n", n, (double)hitCount / N);


	// 释放内存  
	free(memory);
	free(refBit);
	free(counter);
}


//时钟页面替换算法(Clock)
void Clock(int n) {
	int hitCount = 0; // 命中次数  
	int hand = 0; // 模拟循环队列的指针(hand in hand)  
	int i;
	int* memory = NULL;
	memory = (int*)malloc(sizeof(int) * n); //内存空间
	int* refBit = NULL;
	refBit = (int*)malloc(sizeof(int) * n);

	// 初始化内存和引用位  
	for (i = 0; i < n; i++) {
		memory[i] = -1; // 假设初始时没有页面在内存中  
		refBit[i] = 0; // 引用位初始化为0  
	}

	// 遍历页面序列  
	for (i = 0; i < N; i++) {
		int pageFault = page[i]; // 当前页面  
		int found = 0; // 标记页面是否在内存中  

		// 查找页面是否在内存中  
		for (int j = 0; j < n; j++) {
			if (memory[j] == pageFault) {
				found = 1; // 页面在内存中  
				refBit[j] = 0; // 访问了该页面,将引用位设置为0  
				break;
			}
		}

		// 页面在内存中,增加命中次数  
		if (found) {
			hitCount++;
		}
		else {
			// 页面不在内存中,发生缺页中断  
			// 查找第一个R位为0的页面或者循环回到开始并重置所有R位  
			do {
				if (refBit[hand] == 0) {
					// 找到R位为0的页面,替换它  
					memory[hand] = pageFault;
					refBit[hand] = 1; // 设置新页面的R位为1  
					break;
				}
				else {
					// 如果没有找到,则继续查找下一个页面,或者重置所有R位  
					hand = (hand + 1) % n; // 循环队列  
					if (hand == 0) { // 如果回到开始,重置所有R位  
						for (int k = 0; k < n; k++) {
							refBit[k] = 0;
						}
					}
				}
			} while (hand != 0 || refBit[hand] == 1); // 循环直到找到可替换的页面或回到开始  
		}
	}
	// 计算并输出命中率  
	printf("内存容量为%d页时命中率为%lf\n", n, (double)hitCount / N);

	free(memory);
	free(refBit);
}
//主函数
void main()
{
	printf("----------------------------------初始化操作----------------------------------\n");
	Init(); //初始化
	printf("\n");
	printf("---------------------------------最佳淘汰算法(OPT)---------------------------------\n");
	for (int i = 4; i <= 32; i++)
	{
		OPT(i);         //最佳淘汰算法,用户的内存容量4页到32页
	}
	printf("\n");
	printf("--------------------------------先进先出算法(FIFO)---------------------------------\n");
	for (int j = 4; j <= 32; j++)
	{
		FIFO(j);         //先进先出算法,用户的内存容量4页到32页
	}
	printf("\n");
	printf("------------------------------最近最久未使用算法(LRU)------------------------------\n");
	for (int t = 4; t <= 32; t++)
	{
		LRU(t);          //最近最久未使用算法,用户的内存容量4页到32页
	}
	printf("\n");
	printf("------------------------------第二次机会页面替换算法(SCR)------------------------------\n");
	for (int b = 4; b <= 32; b++)
	{
		SCR(b);          //第二次机会页面替换算法,用户的内存容量4页到32页
	}
	printf("\n");
	printf("------------------------------时钟页面替换算法(Clock)------------------------------\n");
	for (int t = 4; t <= 32; t++)
	{
		Clock(t);          //时钟页面替换算法,用户的内存容量4页到32页
	}
	system("pause");
}
一. 实验目的: 1.通过编写和调试存储管理的模拟程序以加深对存储管理方案的理解。熟悉虚存管理的各种页面淘汰算法 2.通过编写和调试地址转换过程的模拟程序以加强对地址转换过程的了解。 二.实验要求 实验程序由以下三大部分组成: (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页。 (3) 分别使用FIFO算法和LFU算法,计算给用户进程的这32页分配4,5,…,32个页面(内存块)时其缺页率。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值