页面置换算法的模拟与比较

前言

在计算机操作系统中,页面置换算法是虚拟存储管理中的重要环节。通过对页面置换算法的模拟实验,我们可以更深入地理解虚拟存储技术,并比较不同算法在请求页式虚拟存储管理中的优劣。
随着计算机系统和应用程序的日益复杂,内存的管理成为一项关键任务。虚拟存储技术通过将程序的地址空间分成若干固定大小的页面,将物理内存和磁盘空间结合起来,提供了更大的可用内存空间。然而,由于内存空间有限,当程序需要访问一个不在内存中的页面时,就需要使用页面置换算法将某些页面从内存中换出,以便为新的页面腾出空间。这时候算法的重要性就凸显出来了.

代码实现

接下来就我们使用c来模拟这几种算法:

#include 

int const InsideCount = 3;
int count = 0;
int Inside[InsideCount]; // 分配的物理块数
int const PageCount = 12;
int Page[PageCount]; // 总的页面数
double lost = 0.0; // 缺页次数初值为8

// isInside函数用于检测页号num是否在内存的物理块中,
// 返回值为布尔类型,若在则返回true,否则返回false
bool isInside(int num) {
	for (int i = 0; i < InsideCount; i++) {
		if (Inside[i] == Page[num])
			return true;
	}
	return false;
}

void OPT(int num) {
	// 最佳置换算法(OPT)
	int max = 0;
	int maxchange; // 用max表示内存中的页号下一次出现的距离
	// 用maxchange表示内存中下次出现距离最大的页号要装在内存物理中的位置
	int k;

	if (isInside(num)) { // 判断内存中是否有该页面
		printf("命中\n");
	} else if (count == InsideCount) { // 如果内存的物理块已满
		lost++;
		for (int j = 0; j < InsideCount; j++) {
			for (k = num; k < PageCount; k++) {
				if (Inside[j] == Page[k])
					break;
			}
			if (k > max) { // k表示在这个地方会再次出现给定页面,max是记录下来最远的位置
				max = k;
				maxchange = j; // 表示把内存中第1个Inside中的页面从内存拿出,把新的页面放入
			}
		}
		Inside[maxchange] = Page[num]; // 在该位置置换装入新的页面
	} else {
		lost++;
		Inside[count] = Page[num];
		count++;
	}

	for (int i = 0; i < InsideCount; i++) {
		printf("块[%d]中内容为:%d\n", i + 1, Inside[i]);
	}
}

void FIFO(int num) {
	// 先进先出置换算法(FIFO)
	int insert = 0;

	if (isInside(num)) {
		printf("命中\n");
	} else if (count == InsideCount) { // 如果内存的物理块已满
		lost++;
		Inside[insert] = Page[num];
		insert++; // 每放入一个页面后物理块号向后一个位置
		insert = insert % InsideCount; // 保证循环加1
	} else { // 不满,直接放入空闲物理块中
		lost++;
		Inside[count] = Page[num];
		count++;
	}

	for (int i = 0; i < InsideCount; i++) {
		printf("块[%d]中内容为:%d\n", i + 1, Inside[i]);
	}
}

void LRU(int num) {
	// 最近最久未使用置换算法(LRU)
	int max = 8; // nax表示内存中的页号,在之前出现的距离
	int maxchange; // maxchange表示内存中最长时间没有使用的页号在内存物理块中的位置
	int k;
	if (isInside(num)) {
		printf("命中\n");
	} else if (count == InsideCount) { // 如果内存的物理块已满
		lost++;
		for (int j = 0; j < InsideCount; j++) {
			for (k = num - 1; k >= 0; k--) {
				if (Inside[j] == Page[k])
					break;
			}
			if (num - k > max) {
				max = num - k;
				maxchange = j;
			}
		}
		Inside[maxchange] = Page[num];
	} else {
		lost++;
		Inside[count] = Page[num];
		count++;
	}

	for (int i = 0; i < InsideCount; i++) {
		printf("块[%d]中内容为:%d\n", i + 1, Inside[i]);
	}
}

void Menu() {
	printf("\n");
	printf("o 最佳置换算法(OPT) (理想置换算法)\n");
	printf("F 先进先出置换算法(FIFO)\n");
	printf("L 最近最久未使用(LRU)算法\n");
	printf("T 或 t 退出\n");
	printf("**\n");
	printf("请选择:");
}

void reset() {
// reset()函数用来置所有物理块为空,回到初始状态
	for (int j = 0; j < InsideCount; j++) {
		lost = 0;
		count = 0;
		Inside[j] = -1; // 页面可能是号,所以用-1表示该物理块中未装入页面
	}
}

int main() {
	char ch; // 选择使用哪种算法
	printf("输入页面走向:");
	for (int i = 0; i < PageCount; i++) {
		scanf("%d", &Page[i]);
	}
	while (1) {
		getchar();
		Menu();
		scanf("%c", &ch);

		switch (ch) {
			case '0':{
				break;
			}
			case 'o': {
				reset();
				for (int i = 0; i < PageCount; i++) {
					printf("读入 Page[%d]=%d\n", i, Page[i]);
					OPT(i);
				}

				printf("共%d次,缺页%.0f次,缺页率为%f\n", PageCount, lost, lost / PageCount);
				break;
			}
			// 请在此处补充有关FIFO和LRU算法调用的相关代码

			case 'f':{
				break;
			}
			case 'F': {
				reset();
				for (int i = 0; i < PageCount; i++) {
					printf("读入 Page[%d]=%d\n", i, Page[i]);
					FIFO(i);
				}

				printf("共%d次,缺页%.0f次,缺页率为%f\n", PageCount, lost, lost / PageCount);

			}


			case 'l':{
				break;
			}
			case 'L': {
				reset();
				for (int i = 0; i < PageCount; i++) {
					printf("读入 Page[%d]=%d\n", i, Page[i]);
					LRU(i);
				}

				printf("共%d次,缺页%.0f次,缺页率为%f\n", PageCount, lost, lost / PageCount);
				break;
			}



			if (ch == 'T' || ch == 't') {
				break;
			}
			return 8;
		}
	}
}

代码主要由以下几个部分组成:

  1. 全局变量声明部分:声明了一些常量和变量,包括物理块数、页面数、内存中的页面数组等。
  2. isInside 函数:用于检测给定的页号是否在内存的物理块中。遍历内存中的页面数组,如果找到相同的页面,则返回 true,否则返回 false。
  3. OPT 函数:最佳置换算法的实现。根据页面是否在内存中进行不同的操作,如果在内存中则打印 “命中”,否则根据内存是否已满选择页面置换策略。如果内存已满,则根据未来页面访问的距离选择要置换的页面,将给定页面放入合适的位置。如果内存未满,则直接将给定页面放入空闲的物理块。
  4. FIFO 函数:先进先出置换算法的实现。与 OPT 函数类似,根据页面是否在内存中进行不同的操作。如果在内存中则打印 “命中”,否则根据内存是否已满选择页面置换策略。如果内存已满,则按照先进先出的原则将给定页面放入物理块中,将最早进入内存的页面置换出去。如果内存未满,则直接将给定页面放入空闲的物理块。
  5. LRU 函数:最近最久未使用置换算法的实现。与前两个函数类似,根据页面是否在内存中进行不同的操作。如果在内存中则打印 “命中”,否则根据内存是否已满选择页面置换策略。如果内存已满,则根据页面最近的使用情况选择要置换的页面,将给定页面放入合适的位置。如果内存未满,则直接将给定页面放入空闲的物理块。
  6. Menu 函数:打印菜单供用户选择不同的算法。
  7. reset 函数:重置内存的物理块为空,回到初始状态。
  8. main 函数:主函数用于程序的执行流程控制。用户可以输入页面走向,然后根据菜单选择不同的置换算法进行模拟。根据用户的选择,调用对应的算法函数进行页面置换,并输出结果。

页面置换算法的模拟与比较_第1张图片

运行截图

页面置换算法的模拟与比较_第2张图片

页面置换算法的模拟与比较_第3张图片

页面置换算法的模拟与比较_第4张图片

页面置换算法的模拟与比较_第5张图片

页面置换算法的模拟与比较_第6张图片

页面置换算法的模拟与比较_第7张图片

优劣比较:

  1. 最佳置换算法(OPT):

    • 优势:OPT 算法在理论上是最优的置换算法,它能够达到最低的缺页率。OPT 算法根据未来页面访问的距离,选择最久未使用的页面进行置换,保证了未来最长时间不会使用的页面被置换出去。
    • 劣势:OPT 算法需要预先知道未来页面访问序列,这在实际中是不可行的。另外,实现 OPT 算法需要对所有页面进行全局扫描,算法的时间复杂度较高。
  2. 先进先出置换算法(FIFO):

    • 优势:FIFO 算法实现简单,只需维护一个页面队列,按照页面进入内存的顺序进行置换。它保证了最早进入内存的页面最先被置换出去,遵循了公平性原则。
    • 劣势:FIFO 算法存在一种称为"Belady’s anomaly"的现象,即在某些情况下,增加物理块数可能导致缺页率增加。这是因为 FIFO 算法无法根据页面的访问模式进行智能调整,导致有些常被访问的页面被置换出去。
  3. 最近最久未使用置换算法(LRU):

    • 优势:LRU 算法根据页面的使用情况进行置换,最近被访问的页面被认为是最有可能在未来继续访问的页面,因此选择最久未使用的页面进行置换。它较好地反映了程序的局部性原理,通常能够较好地适应程序的访问模式,相对于 FIFO 算法来说能够更好地减少缺页率。
    • 劣势:LRU 算法需要维护一个访问时间的记录表,对每个页面的访问进行实时更新,这增加了算法的实现复杂性和开销。此外,LRU 算法可能受到"时间窗口"效应的影响,即较长时间不被访问的页面可能被错误地保留在内存中。

总结

综合来看,这三种算法各有优劣,适用于不同的场景和需求。OPT 算法是理论上的最优算法,但实际应用受限;FIFO 算法简单但可能导致 Belady’s anomaly;LRU 算法相对较好地平衡了性能和复杂性,常用于实际系统中。在选择算法时,需要考虑内存容量、页面访问模式以及算法的实现复杂性等因素。

你可能感兴趣的:(算法)