一、实验目的
1、通过软件模拟页面置换过程,加深对请求页式存储管理实现原理的理解
2、理解和掌握OPT、FIFO和LRU三种页面置换算法,深入分析三者之间的优缺点。
二、实验环境
硬件环境:计算机一台,局域网环境;
软件环境: Windows或Linux操作系统, C语言编程环境。
三、实验内容和步骤
参考设计思路
(1)重要数据结构
① 页表数据结构
typedef struct
{
int vmn;
int pmn;
int exist;
int time;
}vpage_item;
vpage_item page_table[VM_PAGE];
页表是虚地址向物理地址转换的依据,包含虚页号所对应的实页号,是否在物理内存中。
页表中增加了一个time项,用于替换算法选择淘汰页面,在不同的替换算法中,time含义不一样。
在LRU算法中,time为最近访问的时间。该虚页每访问一次,time置为当前访问时刻,淘汰页面时,淘汰time值最小的,即最久没有被使用的。
在FIFO算法中,time为该虚页进入内存的时间。只有当该虚页从外存进入内存时,才置该标志。淘汰页面时,淘汰time值最小的,即最早进入内存的虚页。
在OPT算法中,time没有任何意义。
② 物理页位图数据结构
vpage_item * ppage_bitmap[PM_PAGE];
物理页位图是用于记录物理页是否被使用,用于物理页内存的分配。正常情况下是一个数组,元素值为0时,代表相应物理页没有装入任何虚页,值为1时,代表该物理页装入虚页。但为方便替换算法检索要替换出去的虚页,数组的每个元素值为当前放在该物理页的页表项的指针。若值为NULL,则表示该物理页没有被占用,当值不为NULL时,表示正在占用该物理页的虚页。
③指令相关数据结构
//每条指令信息
typedef struct{
int num;
int vpage;
int offset;
int inflow;
}instr_item;
//指令数组
instr_item instr_array[TOTAL_INSTR];
//指令流数据结构
struct instr_flow{
instr_item *instr;
struct instr_flow *next;
};
//指令流头数据结构
struct instr_flow_head{
int num;
struct instr_flow *next;
};
struct instr_flow_head iflow_head;
每条指令包括指令号、该指令所属虚页及页内偏移(这两项可以根据指令号计算出来,增加这两项是为了方便编程)。inflow是一个辅助项,用于构建指令流。
本题要求,按照规则生成的指令流中,应包含所有的共320条指令。但每次随机生成的指令号,可能已在指令流中,因此最终指令流中的指令数可能远远超过320条指令。
设置inflow的目的是为了便于统计是否320条指令均已加入到指令流中。在该条指令加入到指令流中时,如果inflow为0,表示该指令尚未在指令流中,则统计数加1;如果inflow为1,表示该指令已经加入过指令流,该指令虽然再次加入指令流,但统计数不增加。这样,当统计计数为320时,表示所有的指令均已加入到指令流中。
struct instr_flow为指令流数据结构,struct instr_flow_head始终指向指令流的头,其中num用于指令流中指令数量计数,用于计算缺页率。
(2)主程序,如下图所示。

(3)指令流生成流程
指令流的生成按照实验要求生成,其算法流程如下图所示。

(4)物理内存分配流程
物理内存分配时,需要根据当前置换算法选择淘汰页面。其算法流程下图所示。

(5)运行流程图,如下图所示。

(6)三种置换算法
①OPT算法:在当前指令的后续指令流中,寻找已在内存中的虚页,哪个最远才被使用,反过来,如果先找到最近三个(物理页面总数为4)也在内存中的虚页,则剩下的那个虚页肯定就是最远才被使用的虚页,该虚页被淘汰,其物理内存分配给当前指令所在的虚页。
②FIFO算法:在已在物理内存中的虚页中,寻找time最小的虚页(最早进入物理内存的虚页),该虚页即是被淘汰的虚页。
③LRU算法:思想同FIFO算法,但time最小的虚页含义是最久没有被使用的虚页。
在这三种置换算法中,OPT的算法稍微复杂一些,下图给了该算法的程序流程图。

程序代码
#include <iostream>
#define VM_PAGE 32 /*假设每个页面可以存放10条指令,则共有32个虚页*/
#define PM_PAGE 4 /*分配给作业的内存块数为4*/
#define TOTAL_INSTR 320 /*320条指令*/
using namespace std;
int instr_count = 0;
typedef struct
{
int vmn; //虚页号
int pmn;//实页号
int exist;//是否存在内存
int time;//时间,作为置换依据
}vpage_item;
vpage_item page_table[VM_PAGE];//虚页表
vpage_item* ppage_bitmap[PM_PAGE]; //实页表
//每条指令信息
typedef struct {
int num;//指令号
int vpage;//虚页号
int offset;//页内偏移
int inflow;//是否在指令流中
}instr_item;
//指令数组
instr_item instr_array[TOTAL_INSTR];
//指令流数据结构
struct instr_flow {
instr_item* instr;
struct instr_flow* next;
};
//指令流头数据结构
struct instr_flow_head {
int num = 0;//用于指令流中指令数量计数
struct instr_flow* next;
};
struct instr_flow_head iflow_head;
void Init_PTable() //页表初始化
{
for (int i = 0; i < VM_PAGE; i++)
{
page_table[i].vmn = i + 1; //虚页号
page_table[i].pmn = -1; //实页号
page_table[i].exist = 0;
page_table[i].time = -1;
}
for (int i = 0; i < PM_PAGE; i++)
{
ppage_bitmap[i] = NULL;
}
}
void Init_Instr()
{
int n, count = 1, i = 0,m;
for (i = 0; i < TOTAL_INSTR; i++)
{
instr_array[i].num = i;
if (count % 10 != 0)
m = count / 10 + 1;
else
m = count / 10;
instr_array[i].vpage = m;
instr_array[i].offset = count - (m - 1) * 10 - 1;
instr_array[i].inflow = 0;
count++;
}
srand((unsigned)time(NULL));
instr_flow* p_instr_flow = new instr_flow;
iflow_head.next = p_instr_flow;
while(true){
n = rand() % TOTAL_INSTR;
if (instr_array[n].inflow == 0) {
instr_array[n].inflow = 1;
iflow_head.num++;
}
instr_count++;
p_instr_flow->instr = new instr_item;
p_instr_flow->instr = instr_array + n;
p_instr_flow->next = new instr_flow;
printf("%d\t%d\t%d\n", p_instr_flow->instr->num, p_instr_flow->instr->vpage, p_instr_flow->instr->offset);
if (iflow_head.num == TOTAL_INSTR) {//所有指令均位于指令流中
p_instr_flow->next = NULL;
break;
}
p_instr_flow = p_instr_flow->next;
}
}
void FIFO()/*FIFO页面置换算法*/
{
int k = 0;
int i;
int missing_page_count = 0;
int current_time = 0;
instr_flow* p_instr_flow = new instr_flow;
p_instr_flow = iflow_head.next;
while (p_instr_flow!=NULL)
{
if (page_table[p_instr_flow->instr->vpage - 1].exist == 0)
{
missing_page_count++;
if (k < PM_PAGE)
{
if (ppage_bitmap[k] == NULL) /*找到一个空闲物理块*/
{
ppage_bitmap[k] = &page_table[p_instr_flow->instr->vpage - 1];
ppage_bitmap[k]->exist = 1;
ppage_bitmap[k]->pmn = k;
ppage_bitmap[k]->time = current_time;
k++;
}
}
else
{
int temp = ppage_bitmap[0]->time; /*记录物理块中作业最早到达时间*/
int j = 0; /*记录应当被替换的物理块号*/
for (i = 0; i < PM_PAGE; i++) /*寻找最早到达的作业*/
{
if (ppage_bitmap[i]->time < temp)
{
temp = ppage_bitmap[i]->time;
j = i;
}
}
ppage_bitmap[j]->exist = 0;
ppage_bitmap[j] = &page_table[p_instr_flow->instr->vpage - 1]; /*更新页表项*/
ppage_bitmap[j]->exist = 1;
ppage_bitmap[j]->pmn = j;
ppage_bitmap[j]->time = current_time;
}
}
current_time++;
p_instr_flow = p_instr_flow->next;
}
printf("FIFO算法缺页次数为:%d\t缺页率为:%f\t置换次数为:%d\t置换率为:%f\n", missing_page_count, missing_page_count / (float)instr_count, missing_page_count - 4, (missing_page_count - 4) / (float)instr_count);
}
void LRU()
{
int k = 0;
int i;
int missing_page_count = 0;
int current_time = 0;
instr_flow* p_instr_flow = new instr_flow;
//bool isleft = true; /*当前物理块中是否有剩余*/
p_instr_flow = iflow_head.next;
while (p_instr_flow != NULL)
{
if (page_table[p_instr_flow->instr->vpage - 1].exist == 0)/*当前指令不存在物理块中*/
{
missing_page_count++;
if (k < PM_PAGE)
{
if (ppage_bitmap[k] == NULL) /*找到一个空闲物理块*/
{
ppage_bitmap[k] = &page_table[p_instr_flow->instr->vpage - 1];
ppage_bitmap[k]->exist = 1;
ppage_bitmap[k]->pmn = k;
ppage_bitmap[k]->time = current_time;
k++;
}
}
else
{
int temp = ppage_bitmap[0]->time; /*记录物理块中作业最早到达时间*/
int j = 0; /*记录应当被替换的物理块号*/
for (i = 0; i < PM_PAGE; i++)/*找到时间最小的物理块,也就是最久未使用的*/
{
if (ppage_bitmap[i]->time < temp)
{
temp = ppage_bitmap[i]->time;
j = i;
}
}
ppage_bitmap[j]->exist = 0;
ppage_bitmap[j] = &page_table[p_instr_flow->instr->vpage - 1]; /*更新页表项*/
ppage_bitmap[j]->exist = 1;
ppage_bitmap[j]->pmn = j;
ppage_bitmap[j]->time = current_time;
}
}
else/*当前指令在物理块中,时间更新*/
{
page_table[p_instr_flow->instr->vpage - 1].time = current_time;
}
current_time++;
p_instr_flow = p_instr_flow->next;
}
printf("LRU算法缺页次数为:%d\t缺页率为:%f\t置换次数为:%d\t置换率为:%f\n", missing_page_count, missing_page_count / (float)instr_count, missing_page_count - 4, (missing_page_count - 4) / (float)instr_count);
}
void OPT()
{
int k = 0;
int missing_page_count = 0;
int current_time = 0;
instr_flow* p_instr_flow = new instr_flow;
p_instr_flow = iflow_head.next;
while (p_instr_flow != NULL) {
if (page_table[p_instr_flow->instr->vpage - 1].exist == 0) {
missing_page_count++;
if (k < PM_PAGE)
{
if (ppage_bitmap[k] == NULL) /*找到一个空闲物理块*/
{
ppage_bitmap[k] = &page_table[p_instr_flow->instr->vpage - 1];
ppage_bitmap[k]->exist = 1;
ppage_bitmap[k]->pmn = k;
ppage_bitmap[k]->time = current_time;
k++;
}
}
else {
int used[VM_PAGE] = { 0 };/*记录哪些虚页已经使用*/
int i;
instr_flow* pp_instr_flow = p_instr_flow->next;
int count = 0;
/*便利剩余指令*/
for (pp_instr_flow; pp_instr_flow != NULL; pp_instr_flow = pp_instr_flow->next)
{
if (page_table[pp_instr_flow->instr->vpage - 1].exist == 1)
{
used[page_table[pp_instr_flow->instr->vpage - 1].vmn - 1] = 1;
}
for (i = 0; i < VM_PAGE; i++) {
if (used[i] == 1) {
count++;
}
}
if (count == 3) {
break;
}
}
for (i = 0; i < PM_PAGE; i++) {
if (used[ppage_bitmap[i]->vmn - 1] == 0) {
ppage_bitmap[i]->exist = 0;
ppage_bitmap[i] = &page_table[p_instr_flow->instr->vpage - 1];
ppage_bitmap[i]->exist = 1;
ppage_bitmap[i]->pmn = i;
ppage_bitmap[i]->time = current_time;
}
}
}
}
current_time++;
p_instr_flow = p_instr_flow->next;
}
printf("OPT算法缺页次数为:%d\t缺页率为:%f\t置换次数为:%d\t置换率为:%f", missing_page_count, missing_page_count / (float)instr_count, missing_page_count - 4, (missing_page_count - 4) / (float)instr_count);
}
int main()
{
Init_PTable();
cout << "指令流如下:" << endl;
printf("-----------------------------------------------\n");
printf("指令号\t所在页\t页内位移\n");
printf("-----------------------------------------------\n");
Init_Instr();
printf("-----------------------------------------------\n");
cout << "共计" << instr_count << "条指令" << endl;
cout << endl << endl;
FIFO();
Init_PTable();
LRU();
Init_PTable();
OPT();
return 0;
}
实验结果


实验分析
①分析实验结果产生的原因,总结从实验观察到的结果,分析三种置换算法的缺页率的差异。
FIFO算法每次先替换最先进入的页面,LRU算法每次先替换最近最久未使用的页面,OPT算法每次先替换将来最长时间内不再被访问的页面。所以FIFO算法的置换率相对较大,而LRU算法考虑到了程序的局部性原理,相对较小,OPT算法也相对较小。
②结合操作系统课程中讲授的原理,写出本次实验的心得体会。
FIFO算法置换最先调入内存的页面,即置换在内存中驻留时间最久的页面。按照进入内存的先后次序排列成队列,从队尾进入,从队首删除。但是该算法会淘汰经常访问的页面,不适应进程实际运行的规律。
LRU算法置换最近一段时间以来最长时间未访问过的页面。根据程序局部性原理,刚被访问的页面,可能马上又要被访问;而较长时间内没有被访问的页面,可能最近不会被访问。普偏地适用于各种类型的程序,但是系统要时时刻刻对各页的访问历史情况加以记录和更新,开销太大,因此LRU算法必须要有硬件的支持。
OPT算法置换以后不再被访问,或者在将来最迟才回被访问的页面,缺页中断率最低。但是该算法需要依据以后各业的使用情况。所以OPT算法不可能实现。
本实验通过软件模拟请求页式存储管理中的页面置换过程,重点研究了OPT、FIFO和LRU三种页面置换算法,并对比分析了它们的优缺点。实验采用C语言编程环境,详细介绍了数据结构设计、主程序流程、指令流生成等关键环节。

被折叠的 条评论
为什么被折叠?



