【实验目的】
通过模拟实现请求页式存储管理的几种基本页面置换算法,了解虚拟存储技术的特点,掌握虚拟存储请求页式存储管理中几种基本页面置换算法的基本思想和实现过程,并比较它们的效率。
实验要求:了解虚拟存储技术的特点,掌握请求页式存储管理的主要页面置换算法原理。
【实验内容】
1、设计一个虚拟存储区和内存工作区,并使用下述算法计算访问命中率。
1)最佳淘汰算法(OPT)
2)先进先出的算法(FIFO)
3)最近最久未使用算法(LRU)
4)最不经常使用算法(LFU)
5)最近未使用算法(NUR)
命中率=1-页面失效次数/页地址流长度
【实验环境】(含主要设计设备、器材、软件等)
Pc电脑一台,虚拟机下的linux环境
【实验步骤、过程】(含原理图、流程图、关键代码,或实验过程中的记录、数据等)
main 函数:初始化伪随机数生成器,并生成包含320条指令的地址序列。将地址序列划分为页号和页内偏移。对不同的页面帧数(从4到32),分别调用五种页面置换算法的函数进行模拟比较。
initialize 函数:初始化页面表、页面帧链表,以及空闲和占用页面帧链表。
FIFO 函数:优先淘汰最早进入内存的页面,即先入先出,它的实现很简单,使用一个队列来维护页面进入内存的顺序,当需要淘汰页面时,选择队列头部的页面进行淘汰。当进程分配到的页面数增加时,缺页中断的次数可能增加也可能减少。代码中使用一个先进先出队列 freepf_head 和busypf_head/busypf_tail 来管理页面帧的空闲和占用状态。遍历指令序列,对于每个页面请求,检查页面是否在内存中。如果不在内存中,则发生缺页,选择一个空闲的页面帧进行置换。统计命中率,并输出结果。
OPT 函数:使用最佳置换算法(OPT)模拟页面置换过程。对于每个缺页,预测未来的指令中哪个将最晚访问到,然后选择该页面进行替换。通过计算未来指令中每个页面的距离,选择距离最远的页面帧进行置换。统计命中率,并输出结果。
LRU 函数:淘汰最近没有使用的页面,选择最近最长时间未访问过的页面予以淘汰,它认为过去一段时间内未访问过的页面,在最近的将来可能也不会被访问。代码中使用一个时间戳 present_time 记录每个页面最近一次被访问的时间。遍历指令序列,对于每个页面请求,检查页面是否在内存中。如果不在内存中,则发生缺页,选择最久未使用的页面帧进行置换。更新页面的时间戳,统计命中率,并输出结果。
NUR 函数:基于LRU算法的改进版本,它不仅考虑了页面最近的访问情况,还考虑了页面的修改情况。代码中使用一个访问位 pl[counter] 记录每个页面的访问状态,每隔一定周期清零。遍历指令序列,对于每个页面请求,检查页面是否在内存中。如果不在内存中,则发生缺页,选择最近未使用的页面帧进行置换。模拟定期清零访问位,统计命中率,并输出结果。
LFU 函数:选择最不经常被访问的页面进行淘汰,通过维护页面被访问的频率来实现。使用一个计数器 pl[counter] 记录每个页面被访问的次数。遍历指令序列,对于每个页面请求,检查页面是否在内存中。如果不在内存中,则发生缺页,选择访问次数最少的页面帧进行置换。统计命中率,并输出结果。
#include<stdio.h>
#include <unistd.h>
#include<time.h>
#include<stdlib.h>
#define TRUE 1
#define FALSE 0
#define INVALID -1
#define total_instruction 320
#define total_vp 32
#define clear_period 50
typedef struct
{
int pn,pfn,counter,time;
}pl_type;
pl_type pl[total_vp];
struct pfc_struct{
int pn,pfn;
struct pfc_struct *next;
};
typedef struct pfc_struct pfc_type;
pfc_type pfc[total_vp],*freepf_head,*busypf_head,*busypf_tail;
int diseffect, a[total_instruction];
int page[total_instruction], offset[total_instruction];
int initialize(int);
int FIFO(int);
int LRU(int);
int LFU(int);
int NUR(int);
int OPT(int);
int main( )
{
int s,i,j;
srand(10*getpid());
s=(float)319*rand( )/32767/32767/2+1;
for(i=0;i<total_instruction;i+=4)
{
if(s<0||s>319)
{
printf("When i==%d,Error,s==%d\n",i,s);
exit(0);
}
a[i]=s;
a[i+1]=a[i]+1;
a[i+2]=(float)a[i]*rand( )/32767/32767/2;
a[i+3]=a[i+2]+1;
s=(float)(318-a[i+2])*rand( )/32767/32767/2+a[i+2]+2;
if((a[i+2]>318)||(s>319))
printf("a[%d+2],a number which is :%d and s==%d\n",i,a[i+2],s);
}
for (i=0;i<total_instruction;i++)
{
page[i]=a[i]/10;
offset[i]=a[i]%10;
}
for(i=4;i<=32;i++)
{
printf("---%2d page frames---\n",i);
FIFO(i);
LRU(i);
LFU(i);
NUR(i);
OPT(i);
}
return 0;
}
int initialize(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;
}
for(i=0;i<total_pf-1;i++)
{
pfc[i].next=&pfc[i+1];
pfc[i].pfn=i;
}
pfc[total_pf-1].next=NULL;
pfc[total_pf-1].pfn=total_pf-1;
freepf_head=&pfc[0];
return 0;
}
/*先进先出算法*/
int FIFO(int total_pf)
{
int i,j;
pfc_type *p;
initialize(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;
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;
busypf_tail=freepf_head;
}
freepf_head=p;
}
}
printf("FIFO:%6.4f\n",1-(float)diseffect/320);
return 0;
}
/*最近最久未使用算法*/
int LRU (int total_pf)
{
int min,minj,i,j,present_time;
initialize(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++)
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;
}
else
pl[page[i]].time=present_time;
present_time++;
}
printf("LRU:%6.4f\n",1-(float)diseffect/320);
return 0;
}
int NUR(int total_pf) /*最近未使用算法*/
{
int i,j,dp,cont_flag,old_dp;
pfc_type *t;
initialize(total_pf);
dp=0;
for(i=0;i<total_instruction;i++)
{
if (pl[page[i]].pfn==INVALID)
{
diseffect++;
if(freepf_head==NULL)
{
cont_flag=TRUE;
old_dp=dp;
while(cont_flag)
if(pl[dp].counter==0&&pl[dp].pfn!=INVALID)
cont_flag=FALSE;
else
{
dp++;
if(dp==total_vp)
dp=0;
if(dp==old_dp)
for(j=0;j<total_vp;j++)
pl[j].counter=0;
}
freepf_head=&pfc[pl[dp].pfn];
pl[dp].pfn=INVALID;
freepf_head->next=NULL;
}
pl[page[i]].pfn=freepf_head->pfn;
freepf_head=freepf_head->next;
}
else
pl[page[i]].counter=1;
if(i%clear_period==0)
for(j=0;j<total_vp;j++)
pl[j].counter=0;
}
printf("NUR:%6.4f\n",1-(float)diseffect/320);
return 0;
}
/*最佳置换算法*/
int OPT(int total_pf)
{
int i,j, max,maxpage,d,dist[total_vp];
pfc_type *t;
initialize(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;
}
}
printf("OPT:%6.4f\n",1-(float)diseffect/320);
return 0;
}
/*最不经常使用置换法*/
int LFU(int total_pf)
{
int i,j,min,minpage;
pfc_type *t;
initialize(total_pf);
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++)
{
if(min>pl[j].counter&&pl[j].pfn!=INVALID)
{
min=pl[j].counter;
minpage=j;
}
pl[j].counter=0;
}
freepf_head=&pfc[pl[minpage].pfn];
pl[minpage].pfn=INVALID;
freepf_head->next=NULL;
}
pl[page[i]].pfn=freepf_head->pfn;
pl[page[i]].counter++;
freepf_head=freepf_head->next;
}
else
pl[page[i]].counter++;
}
printf("LFU:%6.4f\n",1-(float)diseffect/320);
return 0;
}
2.代码包括了初始化内存、分配内存(使用First Fit和Best Fit算法)、释放内存等操作。
initializeMemory 函数:初始化内存,将整个内存看作一个空闲的内存块。
printMemory 函数:打印当前内存的状态,包括每个空闲块的起始地址和大小。
firstFit函数:当需要分配内存空间给新的作业时,首次适应算法会从头开始搜索空闲区队列,找到第一个大小大于等于所需空间的空闲区,然后将该空闲区分配给作业。代码中对于每个作业,它在当前空闲块中找到第一个大小足够的块进行分配,并更新空闲块列表。
bestFit函数:BF算法是在FF算法的基础上做了改进。当需要分配内存空间给新的作业时,它会遍历整个空闲区队列,找到大小最接近所需空间的空闲区,然后将该空闲区分配给作业。代码中对于每个作业,它在当前空闲块中找到最适合的块(大小最接近作业大小的块)进行分配,并更新空闲块列表。
deallocateMemory 函数:释放内存,将释放的内存块添加到空闲块列表中。
main函数:初始化内存,生成一些随机大小的作业。使用FF算法分配内存给每个作业,打印内存状态。释放部分内存。使用BF再次分配内存给每个作业,打印内存状态。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <limits.h>
typedef struct Block {
int start;
int size;
} Block;
typedef struct Job {
int size;
} Job;
Block* freeBlocks = NULL;
int numFreeBlocks = 0;
void initializeMemory(int totalSize) {
freeBlocks = (Block*)malloc(sizeof(Block));
freeBlocks[0].start = 0;
freeBlocks[0].size = totalSize;
numFreeBlocks = 1;
}
void printMemory()
{
printf("\nMemory Status:\n");
for (int i = 0; i < numFreeBlocks; ++i)
printf("Block %d: Start = %d, Size = %d\n", i + 1, freeBlocks[i].start, freeBlocks[i].size);
}
// First Fit算法
void firstFit(Job* job)
{
int jobSize = job->size;
for (int i = 0; i < numFreeBlocks; ++i)
{
if (freeBlocks[i].size >= jobSize)
{
printf("Allocated Job of Size %d at Block %d\n", jobSize, i + 1);
freeBlocks[i].start += jobSize;
freeBlocks[i].size -= jobSize;
return;
}
}
printf("No suitable block found for Job of Size %d\n", jobSize);
}
// Best Fit算法
void bestFit(Job* job)
{
int jobSize = job->size;
int bestFitIndex = -1;
int minSizeDiff = INT_MAX;
for (int i = 0; i < numFreeBlocks; ++i)
{
if (freeBlocks[i].size >= jobSize && freeBlocks[i].size - jobSize < minSizeDiff) {
bestFitIndex = i;
minSizeDiff = freeBlocks[i].size - jobSize;
}
}
if (bestFitIndex != -1)
{
printf("Allocated Job of Size %d at Block %d\n", jobSize, bestFitIndex + 1);
freeBlocks[bestFitIndex].start += jobSize;
freeBlocks[bestFitIndex].size -= jobSize;
} else {
printf("No suitable block found for Job of Size %d\n", jobSize);
}
}
// 释放内存
void deallocateMemory(int start, int size)
{
freeBlocks = (Block*)realloc(freeBlocks, (numFreeBlocks + 1) * sizeof(Block));
freeBlocks[numFreeBlocks].start = start;
freeBlocks[numFreeBlocks].size = size;
numFreeBlocks++;
printf("Deallocated Memory: Start = %d, Size = %d\n", start, size);
}
int main()
{
srand(time(NULL));
int totalMemorySize = 100;
int numJobs = 10;
initializeMemory(totalMemorySize);
Job jobs[numJobs];
for (int i = 0; i < numJobs; ++i) {
jobs[i].size = rand() % 20 + 1;
}
for (int i = 0; i < numJobs; ++i) {
printf("\nAllocating Job %d using First Fit...\n", i + 1);
firstFit(&jobs[i]);
printMemory();
}
deallocateMemory(20, 10);
deallocateMemory(60, 15);
for (int i = 0; i < numJobs; ++i) {
printf("\nAllocating Job %d using Best Fit...\n", i + 1);
bestFit(&jobs[i]);
printMemory();
}
free(freeBlocks);
return 0;
}
【实验结果或总结】(对实验结果进行相应分析,或总结实验的心得体会,并提出实验的改进意见)
1.
…………
五个算法比较:由图可知,在相同页面访问序列下,OPT算法具有最高的命中率。这是因为OPT算法是理想化的页面置换算法,它可以提前知道未来的页面访问情况,并选择最有可能被再次访问的页面进行置换。LFU算法在所有页面置换算法中具有较高的命中率。这是因为LFU 算法根据页面的历史访问频率来选择待置换页面,因此它可以较好地预测未来页面访问情况。FIFO算法具有较低的命中率。这是因为FIFO算法是简单的先进先出算法,它并不考虑页面访问情况,因此它很容易将经常被访问的页面置换出去。同时随着页面数的增加,所有页面置换算法的命中率都会有所提高。这是因为物理页面数增加后,内存中可以容纳更多的页面,因此缺页中断发生的概率就会降低。
2.总内存大小为100,使用First Fit算法进行10个作业的内存分配,每个作业的大小随机生成。打印每次分配后的内存状态。如果作业还未分配完,内存块不再满足作业需求,释放两个内存块,使用Best Fit算法再次进行10个作业的内存分配,打印每次分配后的内存状态,释放内存块。
…………
通过本次实验加深了我对页面置换算法的理解。页面置换算法是在内存已满且需要访问的页面不在内存中时,选择一个内存中的页面换出的方法。不同的算法有不同的选择标准和效率。最佳淘汰算法理论上是可以保证最低的缺页率和置换率,但是它是不可能实现的,因为我们无法预知未来的页面访问情况。先进先出置换算法实现简单,但是效率不高,可能会导致经常被访问的页面被频繁地换出。其他三个算法通过记录历史信息或者访问频率来预测未来的访问情况,但是这种预测并不一定准确,因此它的效率不高,但要比先进先出算法高。