一、实验目的
- 通过编写和调试存储管理的模拟程序以加深对存储管理方案的理解。
- 促进对虚拟存储管理的页面淘汰算法的理解。
二、实验内容
设计一个请求页式存储管理方案:编写模拟程序实现,产生一个需要访问的指令地址流,它是一系列需要访问的指令的虚拟地址。页面淘汰算法采用时钟页面置换算法,并且在淘汰一页时,只将该页在页表中抹去,不再判断它是否被改写过,也不将它写回到辅存。
- 指定合适的页面尺寸。
- 指定内存页表的最大长度,并对页表进行初始化。
- 输入一个需要访问的指令虚拟地址。
- 每访问一个地址时,首先要计算该地址所在的页的页号,然后查页表,判断该页是否在主存如果该页已在主存,则打印页表情况。
- 如果该页不在主存且页表未满,则调入一页并打印页表情况。
- 如果该页不在主存且主存已满,则按时钟页面淘汰算法淘汰一页后调入所需的页,打印页表情况。
- 逐个地址访问,直到所有地址访问完毕。
三、编程环境及工具
Dev-C++ 5.11
四、具体设计及有关说明
设计思想:
- 创建循环链表,进行初始化;
- 创建页表结构体数组,进行初始化;
- 开始进行循环:输入虚拟地址,运算出虚拟地址对应的页数,①输入虚拟地址不存在或者超出页数的情况,结束循环;②通过页数与循环链表的一一对比判断该页在主存中:更新循环链表对应物理页帧的最近访问状态,更新页表结构体数组的信息;③页不在主存中:若主存已满,通过循环链表里指针的移动替换出对比其他最先进入的虚拟地址页(若此时虚拟地址页对应的主存刚被访问,则指针向下遍历,直到找到不是最近访问到的物理页帧),更新链表与数组的信息;若主存未满,直接进行信息的更新即可;④一次循环的最后,进行页表信息的输出;
数据结构:
运用了循环链表来表示物理页帧,让指针可以在链表里循环移动,以此来表示最先进入的虚拟地址页;
流程图:
五、代码:
#include<bits/stdc++.h>
#include<conio.h>
using namespace std;
#define P_area 4096 //定义固定数值
#define P_page 4
#define V_page 8
//定义页表的结构体,结构体包含虚拟页对应的物理页帧号、页帧号对应页的近期访问状况、存在状况
typedef struct V_Address
{
int Pa_num;
int R;
int p;
}vad;
//定义物理页帧号的结构体,结构体包含对应的虚拟页号、页帧号对应页的近期访问状况、页帧号、 物理地址结构体指针
typedef struct node
{
int page;
int R;
int Pa_num;
struct node *next;
}Node;
int main()
{
int i,m_empty;
m_empty = P_page; //empty用于表示剩余的物理地址页帧数
//创建循环链表,表长为物理页帧数 P_page
Node *pre = new Node; //定义结构体指针pre为指向循环链表表头的指针
Node *r = new Node;
pre->page = -1; //对应的虚拟页号暂时未知,所以赋值为-1
pre->R = 0; //初始状态为未访问状态
pre->Pa_num = 0; //表头的物理页帧数从零开始
pre->next = NULL;
r = pre;
for(i = 1;i < P_page;i++)
{
Node *p = new Node;
p->page = -1;
p->R = 0;
p->Pa_num = i; //物理页帧数为3时,循环结束
r->next = p;
r = p;
}
r->next = pre; //令表尾指针指向表头,构成循环链表
//将虚拟地址初始化
vad* pa = new vad[V_page];
for(i = 0;i < V_page;i++)
{
pa[i].p = 0;
pa[i].Pa_num = 0;
pa[i].R = 0;
}
printf("此示例程序定义页面大小为%dB,",P_area);
printf("定义分配给进程的物理页帧数为%d页,定义进程的虚拟地址空间大小为%d页。(^_^)\n",P_page,V_page);
//循环过程,不断循环
while(true)
{
int address,a_page,in_RAM;
in_RAM = 0; //作为标记,0时表示不在主存,1表示在主存
printf("(^_^)please input the virtual address<end of -1>:");
scanf("%d",&address); //输入虚拟地址
a_page = address / P_area; //算出虚拟地址所在的页数,页数从零开始
//讨论输入虚拟地址不存在或者超出页数的情况,结束循环
if(a_page > V_page || address < 0)
{
printf("fatal error,illegal address!!\n");
printf("the process will exit with error!!\n");
getch();
break;
}
//将循环链表循环一遍,判断页是否在主存当中
r = pre;
for(i = 0;i < P_page;i++)
{
//页在主存的情况
if(r->page == a_page)
{
in_RAM = 1; //判断值赋值为1
if(r == pre)
r->R = 1; //物理页帧结构体的R为1,代表此时指针所指的结构体为最近访问
pa[a_page].Pa_num = r->Pa_num; //用于表示虚拟页数对应的物理页帧数
pa[a_page].R = 1; //用于表示该物理页帧最近访问过
printf("the page is in the RAM!,and the physical address is %d\n",address);
break;
}
r = r->next;
} //循环结束后,可以得到是否在主存中的判断值
//页不在主存的情况
if(in_RAM==0)
{
printf("the page is not in the RAM!\n");
//主存已满
if(m_empty == 0)
{
printf("there are no free pages,so we have to exchange one!\n");
//指针所指处为对比其他页帧最先进入的,遵循先进先出原则,从物理页帧队列的指针所指处开始循环,判断指针所指是否为最近访问
for(;pre->R == 1;pre = pre->next)
{
pre->R = 0; //如果为最近访问,则令最近访问的判断数为零,指针向下循环
printf("page NO.%d is just read,we should search next one!\n",pre->Pa_num);
} //跳出此循环后,指针所指页帧为所要替换的
pa[pre->page].p = 0; //此时被替换,虚拟页号对应的物理页帧号无效
printf("NO.%d page is exchanged!!\n",pre->Pa_num);
}
//主存未满
else
{
m_empty--;
printf("there are free pages,we can get NO.%d page!\n",pre->Pa_num);
}
//对信息进行更新
pre->page = a_page;
pa[a_page].p = 1;
pa[a_page].R = 1;
pa[a_page].Pa_num = pre->Pa_num;
pre = pre->next; //指针向下循环
}
//输出页表情况
printf(" pa_num\tR\tP\n");
for(i = 0;i < V_page;i++)
{
printf(" |%d\t|%d\t|%d\n",pa[i].Pa_num,pa[i].R,pa[i].p);
}
pa[a_page].R = 0; //在一次循环的最后将页表数组的全部最近访问情况判断数置为零
//这样只需在访问到时将访问到的置为1,不需要考虑是否存在其他物理页帧的状态也为1
}
return 0;
}