[设计目的]
1. 理解虚拟内存和请求分页管理机制的基本概念;
2. 学习不同的页面替换算法,包括但不限于FIFO、LRU、OPT等;
3. 通过模拟实现页面替换算法,加深对操作系统内存管理的理解。
[设计要求]
在操作系统当中,设计并实现一个模拟环境,用于模拟请求分页虚拟存贮中的页面替换过程。
需要创建一个模拟的内存环境,包括内存页框、页面、进程等概念。
需要实现页面的加载、访问和替换过程,并能够追踪页面的访问情况。
在操作系统中,对FIFO、LRU、OPT等页面替换算法进行模拟,分析并比较它们的性能和效率。
编写代码实现每种算法,并确保它们能够在模拟环境中正常运行。
收集并分析不同算法在不同工作负载下的性能数据,比如页面错误率、CPU 时间等。
比较不同算法的优缺点,并探讨它们在实际系统中的应用场景
[设计思想及内容]
设计思想是创建一个模拟环境,其中包含一个固定大小的内存框架集合,以及一个页面访问序列。通过模拟页面访问过程,展示不同页面替换算法的工作方式及其对系统性能的影响。
[数据结构设计]
核心数据结构主要涉及内存管理和页面替换算法所需的元素。以下是核心数据结构的定义:
页面(_Page):包含页面ID等信息。
页面队列(_PageQueue):用于存储进程中的页面序列。
进程(_Process):包含页面队列和页面总数。
内存块(_Block):表示内存中的一个存储单元,包含页面指针、状态和最后访问时间。
内存块队列(_BlockQueue):用于表示内存中的块序列。
[程序源码]
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<sys/time.h>
#include<unistd.h>
#include<stdlib.h>
#define NULL ((void *)0)
#define BUSY 1
#define AVA 0
#define BLOCKNUMBER 3
//以下为数据结构的定义
long Time = 0;
//页面
typedef struct _Page{
int id;
}Page;
//页面队列
typedef struct _PageQueue{
Page page;
struct _PageQueue *next;
}PageQueue;
//进程
typedef struct _Process{
PageQueue pages;
int length;
}Process;
//内存块
typedef struct _Block{
Page *page;
int state;
long lasttime;
}Block;
//内存块队列
typedef struct _BlockQueue{
Block block;
struct _BlockQueue *next;
}BlockQueue;
//重要函数的实现
//初始化页面队列
PageQueue *InitPageQueue(int pagesize,int maxpageID){
PageQueue *head = NULL;
PageQueue *p = NULL;
PageQueue *q = NULL;
for (int i = 0; i < pagesize; i++)
{
p = (PageQueue *)malloc(sizeof(PageQueue));
p->page.id = rand() % (maxpageID + 1);
p->next = NULL;
printf("%d ",p->page.id);
if(head == NULL) head = p;
else q->next = p;
q = p ;
}
printf("\n");
return head;
}
//初始化进程
Process *InitProcess(Process *process,int pagesize,int maxpageID){
process = (Process *)malloc(sizeof(Process));
printf("进程初始化\n");
process->length = pagesize;
process->pages.next = InitPageQueue(pagesize,maxpageID);
return process;
}
//内存初始化
BlockQueue *InitBlockQueue(int size){
BlockQueue *head = NULL;
BlockQueue *p = NULL;
BlockQueue *q = NULL;
for (int i = 0; i < size; i++)
{
p = (BlockQueue *)malloc(sizeof(BlockQueue));
p->block.page = (Page *)malloc(sizeof(Page));
p->block.page->id = 0;
p->block.state = AVA;
p->block.lasttime = 0;
if(head == NULL) head = p;
else q->next = p;
q = p;
}
q->next = NULL;
return head;
}
//获取当前块队列的长度
int Get_BlockQueue_Length(BlockQueue *blockqueue){
BlockQueue *presentblock;
presentblock = blockqueue;
int queuelength = 0 ;
while (presentblock != NULL)
{
queuelength++;
presentblock = presentblock->next;
}
return queuelength;
}
//搜索空闲块
BlockQueue *Search_AVA_Block(BlockQueue *blockqueue){
BlockQueue *presntblock;
presntblock = blockqueue;
while (presntblock != NULL)
{
if(presntblock->block.state == AVA) return presntblock;
else presntblock = presntblock->next;
}
return NULL;
}
//清空块队列
void EmptyBlock(BlockQueue *blockqueue){
BlockQueue *presentblock;
presentblock = blockqueue;
while(presentblock != NULL){
presentblock->block.page = NULL;
presentblock->block.state = AVA;
presentblock->block.lasttime = 0;
presentblock = presentblock->next;
}
}
//搜索特定页面
BlockQueue *SearchPage(BlockQueue *blockqueue,Page page){
BlockQueue *p;
p = blockqueue;
while(p != NULL){
if(p->block.page->id == page.id){
return p;
}else{
p = p->next;
}
return NULL;
}
}
//搜索内存中停留最久的块
BlockQueue *SreachOldestBlock(BlockQueue *blockqueue){
BlockQueue *p,*oldestblock;
p = blockqueue;
oldestblock = p;
long oldest = p->block.lasttime;
while(p != NULL){
if(p->block.lasttime < oldest){
oldest = p->block.lasttime;
oldestblock = p;
}
p = p->next;
}
return oldestblock;
}
//搜索内存中持续最长时间不被访问的页面
BlockQueue *SearchLongestBlock(BlockQueue *blockqueue,PageQueue *page){
BlockQueue *p = blockqueue, *longestblock;
PageQueue *q = page->next;
if (p == NULL) return p;
longestblock = p;
int max_count = 0;
while (p != NULL){
int count = 0;
while(q != NULL){
count++;
if(p->block.page->id == q->page.id) break;
q = q->next;
}
if(count > max_count){
max_count = count;
longestblock = p;
}
q = page->next;
p = p->next;
}
return longestblock;
}
//返回块号
int GetBlockLable(BlockQueue *blockQueue, BlockQueue *goalBlock) {
BlockQueue *p = blockQueue;
int count = 1;
while (p != goalBlock) {
p = p->next;
count++;
}
return count;
}
//打印块信息
void PrintBlockList(BlockQueue *blockQueue, int pageID, int number) {
BlockQueue *presentBlock = blockQueue;
for (int i = 0; i < Get_BlockQueue_Length(blockQueue); i++) {
printf("页框%d ", i + 1);
if (presentBlock->block.state == AVA) {
printf("| |\n");
} else {
if (presentBlock->block.page->id != pageID) {
printf("| %d |\n", presentBlock->block.page->id);
} else {
switch (number) {
case 1:
printf("| %d | 命中! 主存已存在该页面,位于页框%d\n", pageID, GetBlockLable(blockQueue, presentBlock));
break;
case 2:
printf("| %d | 缺页! 主存不存在该页面,载入空闲页框%d\n", pageID, GetBlockLable(blockQueue, presentBlock));
break;
case 3:
printf("| %d | 缺页! 主存不存在该页面,替换页框%d\n", pageID, GetBlockLable(blockQueue, presentBlock));
break;
default:
break;
}
}
}
presentBlock = presentBlock->next;
}
printf("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n");
}
//替换算法的实现
//FIFO
void FIFO(BlockQueue *blockQueue, Process *process) {
PageQueue *currentPage = process->pages.next;
while (currentPage != NULL) {
if (SearchPage(blockQueue, currentPage->page) != NULL) {
PrintBlockList(blockQueue, currentPage->page.id, 1);
} else {
BlockQueue *avaBlock = Search_AVA_Block(blockQueue);
if (avaBlock != NULL) {
avaBlock->block.state = BUSY;
avaBlock->block.lasttime = Time++;
avaBlock->block.page = (Page *) malloc(sizeof(Page));
avaBlock->block.page->id = currentPage->page.id;
PrintBlockList(blockQueue, currentPage->page.id, 2);
} else {
avaBlock = SreachOldestBlock(blockQueue);
avaBlock->block.lasttime = Time++;
avaBlock->block.page->id = currentPage->page.id;
PrintBlockList(blockQueue, currentPage->page.id,3);
}
}
currentPage = currentPage->next;
}
}
//LRU
void LRU(BlockQueue *blockQueue, Process *process) {
PageQueue *currentPage = process->pages.next;
while (currentPage != NULL) {
BlockQueue *searchedBlock = SearchPage(blockQueue, currentPage->page);
if (searchedBlock != NULL) {
searchedBlock->block.lasttime = Time++;
PrintBlockList(blockQueue, currentPage->page.id, 1);
} else {
BlockQueue *avaBlock = Search_AVA_Block(blockQueue);
if (avaBlock != NULL) {
avaBlock->block.state = BUSY;
avaBlock->block.lasttime = Time++;
avaBlock->block.page = (Page *) malloc(sizeof(Page));
avaBlock->block.page->id = currentPage->page.id;
PrintBlockList(blockQueue, currentPage->page.id, 2);
} else {
avaBlock = SreachOldestBlock(blockQueue);
avaBlock->block.lasttime = Time++;
avaBlock->block.page->id = currentPage->page.id;
PrintBlockList(blockQueue, currentPage->page.id, 3);
}
}
currentPage = currentPage->next;
}
}
//OPT
void OPT(BlockQueue *blockQueue,Process *process){
PageQueue *currentPage = process->pages.next;
while (currentPage != NULL) {
if (SearchPage(blockQueue, currentPage->page) != NULL) {
PrintBlockList(blockQueue, currentPage->page.id,1);
} else {
BlockQueue *avaBlock = Search_AVA_Block(blockQueue);
if (avaBlock != NULL) {
avaBlock->block.state = BUSY;
avaBlock->block.lasttime = Time++;
avaBlock->block.page = (Page *) malloc(sizeof(Page));
avaBlock->block.page->id = currentPage->page.id;
PrintBlockList(blockQueue, currentPage->page.id, 2);
} else {
avaBlock = SearchLongestBlock(blockQueue,currentPage);
avaBlock->block.lasttime = Time++;
avaBlock->block.page->id = currentPage->page.id;
PrintBlockList(blockQueue, currentPage->page.id, 3);
}
}
currentPage = currentPage->next;
}
}
int main(){
Process *process,*p;
int pageNumber;
int casenum;
PageQueue *pages;
BlockQueue *blockqueue;
blockqueue = InitBlockQueue(BLOCKNUMBER);
printf("请输入进程页面数:");
scanf("%d",&pageNumber);
printf("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n");
printf("主存页框数: %d 进程页面数:%d\n",BLOCKNUMBER,pageNumber);
printf("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n");
p = InitProcess(process,pageNumber,pageNumber);
printf("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n");
printf("1.FIFO\t2.LRU\t3.OPT\t0.exit\n");
printf("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n");
printf("请选择要使用的替换算法:");
scanf("%d",&casenum);
switch (casenum)
{
case 1 :
FIFO(blockqueue,p);
break;
case 2 :
LRU(blockqueue,p);
break;
case 3 :
OPT(blockqueue,p);
break;
case 0 :
return 0;
default:
printf("输入错误!");
break;
}
}
简要的代码思路解释:
我们知道OPT算法在现实中是无法实现的,但是我们这里采用了比较投机的算法简单的模拟了一下
OPT算法:最佳选择算法
假设我们现在有三个内存块,而来了六个进程,我们很容易知道,当前面是不一样的三个进入内存块之后,势必会产生页面置换,我们遍历现在内存块中的内容,和后面的进程进行匹配,相同则记录两个进程的“距离”,不同的就即为无穷大或者-1,大家按自己的习惯来就好,起个区分作用就行
FIFO算法:先进先出算法
这里我们设置了一个变量lasttime,意思是持续时间,每当进程进入之后,我们就让在内存中的进程的lasttime的值全都+1,替换时替换掉 lasttime值最小的进程所在的内存块。
LRU算法:最近最久未使用算法
这里我们有一个十分巧妙的地方,就是和FIFO采用的一样的lasttime,这里的lasttime的值更新条件和FIFO中有所区别,我们不仅会全都+1,而且在进程被调用的时候还会给被调用的进程额外+1,怎么说呢,还是喜欢举例子,3个内存块,进程分别为1 2 3 1 2 3 ,当调用到第四个进程(这里是1),那么此时1 2 3 不仅同时增加一个时间单位,而且 1 还会再被增加一个时间单位,因为他被调用了,这样在置换页面的时候,我们还是优先置换lasttime值最小的内容
这么做的优点:统一置换函数,而且减少变量设计,编程实现更加高效
[设计实现展示(可附截图说明)]
OPT算法
FIFO算法
LRU算法
[课程设计出现问题及解决方法]
问题1:页面替换算法的性能比较不明显。 解决方法:增加页面访问序列的长度,确保有足够的数据来观察算法的性能差异。
问题2:内存框架大小固定,限制了模拟的灵活性。 解决方法:允许用户输入内存框架的大小,使模拟更加灵活。
问题3:置换算法在置换时出现和预期效果不匹配的情况。解决办法:通过请教老师,老师说search_oldest_block函数的if逻辑判断有问题,请过debug调试之后发现问题所在,使得程序达到了预期效果。
【参考资料】
https://blog.youkuaiyun.com/qq_32767041/article/details/85237988
模拟实现请求分页虚存页面替换算法