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

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

一、实验目的

  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");
}
  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值