一.实验描述
本实验是模拟虚拟存储管理方式中的请求分页式技术中的页面置换算法,对于当前访问的页面号如果有对应的内存物理块号则直接访问(必要时可调整物理块中的页面序号),否则先输出缺页访问标志+,然后再判断物理块是否已存满,未满则直接换入当前页,若已满,则先将内存物理块中块首页面号淘汰且换出,然后再将当前页换入。FIFO是选择在内存驻留时间最长的页面进行置换,LFU是选择最长时间没有被引用的页面进行置换。
二.实验结果
三.实验代码
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
//作业结构体
typedef struct Job {
char name[20]; //作业名
int pageLen; //页面长度
int* pageSeq; //页面顺序
}Job;
//页面结构体
typedef struct Page {
int n; //页面序号
int count; //计时器
Page* next;
}Page;
//页面链表
typedef struct List {
Page* tail;
Page* head;
int len; //记录当前节点个数
bool haveNewPage; //记录当前是否有新的页面加入
int newPageNum; //记录新页面的序号
}List;
//调度结果保存在一张二维数组中
typedef struct Map {
int** arr; //调度结果图,使用动态的二维数组
//更新: 将result.arr由char**改为char*,不将结果转为字符串,而是作为整形数组输出,要添加判断来输出空格和加号
int len; //已保存的链表状态个数
int interruptNum; //记录中断次数
}Map;
List pageList; //作业页面队列
int blockNum; //物理块数
Job job; //作业
Map result; //记录调度结果
//初始化
void init() {
printf("请输入物理块数:");
scanf("%d", &blockNum);
pageList.len = 0;
pageList.head = pageList.tail = (Page*)malloc(sizeof(Page)); //先分配一个头结点
pageList.head->next = NULL;
}
//页面插入链表
void entryList(int x)
{
Page* p = (Page*)malloc(sizeof(Page));
p->n = x;
p->count = 0; //初始化p
p->next = NULL;
pageList.tail->next = p;
pageList.tail = p; //尾插法
pageList.len++;
}
//页面从表头退出
void outList()
{
Page* tmp = pageList.head->next;
pageList.head->next = tmp->next;
pageList.len--;
free(tmp);
}
//设置计时器
void setCount()
{
Page* p = pageList.head->next;
while (p != NULL)
{
p->count++;
p = p->next;
}
}
//输入作业
void inputJob() {
printf("请输入作业名:");
scanf("%s", job.name);
printf("请输入作业页面长度:");
scanf("%d", &job.pageLen);
job.pageSeq = (int*)malloc(sizeof(int) * job.pageLen); //根据页面长度动态分配数组记录页面序号
result.arr = (int**)malloc(sizeof(int*) * job.pageLen); //先分配二级指针的空间,用于后续保存每次循环链表的状态
result.len = 0;
result.interruptNum = 0; //记录中断次数
printf("请输入作业页面顺序:\n");
for (int i = 0; i < job.pageLen; i++)
scanf("%d", &job.pageSeq[i]);
}
//查找页面是否在链表中
bool isExist(int x) {
Page* p = pageList.head->next;
while (p != NULL)
{
if (x == p->n)
return true;
p = p->next;
}
return false;
}
//更新结果
void updateResult()
{
bool isFull = (pageList.len == blockNum); //判断页面链表是否已满
int* p = (int*)malloc(sizeof(int)*(blockNum + 2)); //分配用于记录当前链表状态的空间,3是保存+号和新页面的序号
Page* q = pageList.head->next;
if (isFull == false) //没满,需要填充空格
{
for (int i = pageList.len; i < blockNum; i++)
p[i] = -1; //代表空格
}
for (int i = pageList.len - 1; i >= 0; i--) //倒序存储链表的页面序号便于输出
{
p[i] = q->n;
q = q->next;
}
if (pageList.haveNewPage == true) //有新页面,更新+
p[blockNum] = -2; //代表+号
else
p[blockNum] = -1;
p[blockNum + 1] = pageList.newPageNum; //新页面序号
result.arr[result.len] = p;
result.len++;
}
//打印结果
void printResult(int x)
{
if (x == 1)
printf("**********打印作业FIFO调度进入主存页的过程**********\n");
else
printf("**********打印作业LRU调度进入主存页的过程**********\n");
double interruptRate = 0;
printf("作业名:%s\n", job.name);
printf("作业调度过程:\n");
//打印下标
printf(" ");
for (int i = 0; i < job.pageLen; i++)
printf(" %3d", i);
printf("\n ");
//打印页面顺序
for (int i = 0; i < job.pageLen; i++)
printf(" %3d", job.pageSeq[i]);
//打印调度算法情况
for (int i = 0; i < blockNum+2; i++) //2是用于打印+号和新页面号的两行
{
if (i < blockNum)
printf("\n%-2d ", i); //行号
else
printf("\n "); //最后两行不打印行号
for (int j = 0; j < job.pageLen; j++)
{
if (result.arr[j][i] == -1) //先判断是字符还是序号
printf("%3c ", ' ');
else if(result.arr[j][i] == -2)
printf("%3c ", '+');
else
printf("%3d ", result.arr[j][i]); //注意是有20行,竖过来就是将每一列作为新的行
}
}
interruptRate = (double)result.interruptNum / job.pageLen * 100;
printf("\n缺页中断率为:%.2f\n", interruptRate);//打印缺页中断率
}
//FIFO
void FIFO() {
//更新每次的状态
for (int i = 0; i < job.pageLen; i++)
{
if (isExist(job.pageSeq[i]) == true) //页面存在则不用额外操作,只需要说明无新页面
pageList.haveNewPage = false;
else {
result.interruptNum++; //页面不存在,发生中断
if (pageList.len == blockNum) //链表已满,表头节点退出,否则直接插入新节点即可
outList();
entryList(job.pageSeq[i]);
pageList.haveNewPage = true;
pageList.newPageNum = job.pageSeq[i];//更新新入队的页面号
}
//更新result
updateResult();
}
}
//LRU
void LRU() {
for (int i = 0; i < job.pageLen; i++)
{
if (isExist(job.pageSeq[i]) == true) //页面存在则找到该节点并置于尾部即可
{
Page* pre = pageList.head, * p = pageList.head->next;
while (p != NULL) //寻找节点
{
if (p->n == job.pageSeq[i])
break;
pre = p;
p = p->next;
}
if (p->next != NULL) //如果已在尾部,只需要重置计时器即可
{
pre->next = p->next; //不在尾部,插入链表尾部
pageList.tail->next = p; //调整尾指针
pageList.tail = p;
p->next = NULL; //注意要调整next
}
p->count = 0;
pageList.haveNewPage = false;
}
else {
result.interruptNum++; //页面不存在,中断次数++
if (pageList.len == blockNum) //链表已满,同上
outList();
entryList(job.pageSeq[i]);
pageList.haveNewPage = true;
pageList.newPageNum = job.pageSeq[i];//更新新入队的页面号
}
updateResult(); //更新result和计时器
setCount();
}
}
//清理页面链表和result,释放申请的空间
void clear()
{
Page* p = pageList.head->next, * tmp;
while (p != NULL) //释放掉链表所有节点
{
tmp = p;
p = p->next;
free(tmp);
}
pageList.len = 0;
pageList.tail = pageList.head;
pageList.head->next = NULL;
for (int i = 0; i < result.len; i++)//数组
free(result.arr[i]); //先释放每个二级指针指向的结果
free(result.arr); //再释放所有二级指针
result.arr = NULL;
result.len = 0;
}
//选择菜单
void choiceMenu() {
while (true) {
int cho;
printf("**********请求分页式存储管理**********\n");
printf(" * 1.FIFO分配 *\n");
printf(" * 2.LRU(LFU)分配 *\n");
printf(" * 0.退出 *\n");
printf("\t\t请输入选项:");
scanf("%d", &cho);
switch (cho) {
case 0:
return;
case 1:
inputJob();
FIFO();
printResult(1);
clear();
break;
case 2:
inputJob();
LRU();
printResult(2);
clear();
break;
}
}
}
//7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1
int main() {
init();
choiceMenu();
return 0;
}