一、实验目的
存储管理的主要功能之一是合理地分配空间。请求页式管理是一种常用的虚拟存储管理技术。本实验的目的是通过请求页式管理中页面置换算法模拟设计,了解虚拟存储技术的特点,掌握请求页式存储管理的页面置换算法。
二、实验内容
1、通过计算不同算法的命中率比较算法的优劣。同时也考虑了用户内存容量对命中率的影响。
2、页面失效次数为每次访问相应指令时,该指令所对应的页不在内存中的次数。
3、在本实验中,假定页面大小为 1k,用户虚存容量为 32k,用户内存容量为 4 页到 32 页。
三、实验要求
1、计算并输出下属算法在不同内存容量下的命中率。
1)先进先出的算法(FIFO);
2)最近最少使用算法(LRU);
2、address通过随机数产生一个指令序列,共320条指令。
指令的地址按下述原则生成:
1)50%的指令是顺序执行的
2)25%的指令是均匀分布在前地址部分
3)25%的指令是均匀分布在后地址部分
四、实验过程
具体的实施方法是:
1)在[0,319]的指令地址之间随机选取一起点m;
2)顺序执行一条指令,即执行地址为m+1的指令;
3)在前地址[0,m+1]中随机选取一条指令并执行,该指令的地
址为 m1;
4)顺序执行一条指令,地址为m1+1的指令
5)在后地址[m1+2,319]中随机选取一条指令并执行;
6)重复上述步骤 1)~5),直到执行320次指令
五、实验分析与实现
1、避免伪随机数生成使用c++的time.h的库
#include <time.h>
使用srand((unsigned)time(NULL))及rand()实现
Address::Address()
{
cout<<"Start memory management."<<'\n';
cout<<"Producing address flow, wait for while, please."<<'\n';
int j=0;
flag=true;
srand((unsigned)time(NULL));
/* 生成指令序列 */
while(j<320)
{
/* 在[0,319]的指令地址之间随机选取一起点 m; */
m=rand()%320;
/* 顺序执行一条指令,即执行地址为 m+1 的指令 */
order[j]=m+1;
cout<<order[j++]<<'\t';
/* 在前地址[0,m+1]中随机选取一条指令并执行,该指令的地址为 m1 */
m1=rand()%(m+2);
/* 顺序执行一条指令,地址为 m1+1 的指令 */
order[j]=m1+1;
cout<<order[j++]<<'\t';
/* 在后地址[m’+2,319]中随机选取一条指令并执行 */
order[j]=(m1+2)+(rand()%(320-m1-2));
cout<<order[j++]<<'\t';
}
}
2、队列的数据结构的实现,使用模板类实现队列
linkqueue.h文件:
#include <iostream>
using namespace std;
template <class T>
struct Node
{
T data;
Node<T> *next;
};
template <class T>
class LinkQueue
{
public:
LinkQueue(); /* 构造函数,初始化空的链队列 */
~LinkQueue(); /* 析构函数,释放链队中各结点的存储空间 */
void EnQueue(T x); /* 将元素x入队 */
T DeQueue(); /* 将队头元素出队 */
void DeQueue(T x); /* 将队头元素出队 */
void SetNullQueue(); /* 队列清空 */
bool Empty(); /* 判断队列是否为空 */
bool Exist(T x); /* 判断队列里是否存在这个页面 */
int GetLength(); /* 返回队列的长度 */
// friend void FIFO(); /* 先进先出算法 */
// friend void LRU(); /* 最近最久未使用算法 */
private:
Node<T> *front, *rear;
int length;/* 计算队列长度 */
int m,m1;/* 起点 m,m1 */
int S; /* 算法号 */
bool flag; /* 判断输入的算法号是否符合要求 */
int Msize=2; /* 用户内存空间 */
};
template <class T>
LinkQueue<T>::LinkQueue()
{
Node <T> *s;
s=new Node<T>;
s->next=NULL;
front=rear=s;
}
template <class T>
LinkQueue<T>::~LinkQueue()
{
while(front)
{
Node <T> *p;
p=front->next;
delete front;
front=p;
}
}
/* 入队函数 */
template<class T>
void LinkQueue<T>::EnQueue (T x)
{
Node<T> *s;
s=new Node<T> ; /* 生成结点s */
s->data=x;
s->next =NULL;
rear->next=s ; /* 将结点s接队尾 */
rear=s ; /* 队尾指针指向新队尾结点 */
length++;
}
template <class T>
T LinkQueue<T>:: DeQueue ()
{
Node<T>*p;
T x;
if(rear==front)
throw "下溢";
p=front->next;
x=p->data; /* 暂存队头元素 */
front->next=p->next; /* 将队头元素所在结点摘链 */
if (p->next==NULL)
rear=front; /* 判断出队前队列长度是否为1 */
length--;
delete p;
return x;
}
template <class T>
void LinkQueue<T>::DeQueue (T x)
{
Node<T>*p,*s;
if(rear==front)
throw "下溢";
p=front;
s=front->next;
while(p!=rear)
{
if(s->data==x)
{
p->next=s->next; /* 将元素所在结点摘链 */
}
s=s->next;
p=p->next;
}
if (p->next==rear)
rear=front; /* 判断出队前队列长度是否为1 */
length--;
delete p,s;
}
/* 判断链队列是否为空函数 */
template <class T>
bool LinkQueue<T>::Empty()
{
if(front==rear)
return 1;
else
return 0;
}
/* 队列清空函数 */
template <class T>
void LinkQueue<T>::SetNullQueue ()
{
rear = front;
}
/* 返回队列长度 */
template <class T>
int LinkQueue<T>::GetLength ()
{
return length;
}
/* 判断队列里是否存在这个页面 */
template <class T>
bool LinkQueue<T>::Exist(T x)
{
Node<T> *p;
if(rear==front)
throw "下溢";
p=front->next;
while(p!=rear->next)
{
if(p->data==x)
{
return 1;
}
else
p=p->next;
}
return 0;
}
3、实现最近最久未使用算法
步骤:
判断页面是否在队列物理块里面。若不存在则先判断是否29个物理块全部被占用,若未被全部占用,直接将此页插在队列最后面,若全部被占用,则将先进队列的队头元素出队,再将此页面入队,缺页次数+1;若存在,将此页面先出队,再在最后入队,将最近最久未使用的页面放在队首。
/* 最近最久未使用算法 */
void LRU(int order[],int size)
{
int number=0; /* 初始化缺页次数 */
LinkQueue<int> q;
/* 执行320条指令序列 */
for(int i=0;i<size;i++)
{
/* 如果队列为空,则将此页面放入物理块的队列里 */
if(q.Empty())
{
q.EnQueue(order[i]);
number++;
}
else
{
/* 判断页面是否在队列物理块里面
* 若不存在则先判断是否29个物理块全部被占用,
* 若未被全部占用,直接将此页插在队列最后面,
* 若全部被占用,则将先进队列的队头元素出队,再将此页面入队
* 缺页次数+1;
* 若存在,将此页面先出队,再在最后入队,将最近最久未使用的页面放在队首*/
if(!q.Exist(order[i]))
{
if(q.GetLength()<=29)
q.EnQueue(order[i]);
else
{
q.DeQueue();
q.EnQueue(order[i]);
}
number++;
}
else if(q.Exist(order[i]))
{
q.DeQueue(order[i]);
q.EnQueue(order[i]);
}
}
}
/* 记录命中率 */
double rate=1-number/320.0;
cout<<"LRU算法命中率: "<<rate<<'\t';
}
六、实验过程中的问题及对应思考
1.如何实现先进先出算法?
解决:使用数据结构队列
2.模板类实例定义无效
解决:将模板类.cpp文件内容加到.h文件中。
思考:模板类的实现,脱离具体的使用,是无法单独的编译的;把声明和实现分开的做法也是不可取的,必须把实现全部写在头文件里面。或者在类的定义源文件中使用关键字export,然后其他文件使用时只需包含类的定义头文件就可以。
3.如何利用已有的代码(已实现FIFC算法)实现LRU算法
解决:将最近使用的页面出队,插入到队列最后面,最近未使用的页面排在队列的最前面
七、附录
address.h文件内容:
/* c++头文件 */
#include <iostream>
#include <time.h>
using namespace std;
/* 指令类,生成指令序列 */
class Address
{
public:
Address();/* 生成指令序列 */
int Judge(int m);/* 判断输入的算法号是否在[1,4] */
void GetOrder(int a[]);
private:
int m,m1;/* 起点 m,m1 */
int order[320];/* 指令序列 */
int S; /* 算法号 */
bool flag; /* 判断输入的算法号是否符合要求 */
};
algorithm.h文件
/* 先进先出算法 */
void FIFO(int order[],int size);
/* 最近最久未使用算法 */
void LRU(int order[],int size);
linkqueue.h文件:
#include <iostream>
using namespace std;
template <class T>
struct Node
{
T data;
Node<T> *next;
};
template <class T>
class LinkQueue
{
public:
LinkQueue(); /* 构造函数,初始化空的链队列 */
~LinkQueue(); /* 析构函数,释放链队中各结点的存储空间 */
void EnQueue(T x); /* 将元素x入队 */
T DeQueue(); /* 将队头元素出队 */
void DeQueue(T x); /* 将队头元素出队 */
void SetNullQueue(); /* 队列清空 */
bool Empty(); /* 判断队列是否为空 */
bool Exist(T x); /* 判断队列里是否存在这个页面 */
int GetLength(); /* 返回队列的长度 */
// friend void FIFO(); /* 先进先出算法 */
// friend void LRU(); /* 最近最久未使用算法 */
private:
Node<T> *front, *rear;
int length;/* 计算队列长度 */
int m,m1;/* 起点 m,m1 */
int S; /* 算法号 */
bool flag; /* 判断输入的算法号是否符合要求 */
int Msize=2; /* 用户内存空间 */
};
template <class T>
LinkQueue<T>::LinkQueue()
{
Node <T> *s;
s=new Node<T>;
s->next=NULL;
front=rear=s;
}
template <class T>
LinkQueue<T>::~LinkQueue()
{
while(front)
{
Node <T> *p;
p=front->next;
delete front;
front=p;
}
}
/* 入队函数 */
template<class T>
void LinkQueue<T>::EnQueue (T x)
{
Node<T> *s;
s=new Node<T> ; /* 生成结点s */
s->data=x;
s->next =NULL;
rear->next=s ; /* 将结点s接队尾 */
rear=s ; /* 队尾指针指向新队尾结点 */
length++;
}
template <class T>
T LinkQueue<T>:: DeQueue ()
{
Node<T>*p;
T x;
if(rear==front)
throw "下溢";
p=front->next;
x=p->data; /* 暂存队头元素 */
front->next=p->next; /* 将队头元素所在结点摘链 */
if (p->next==NULL)
rear=front; /* 判断出队前队列长度是否为1 */
length--;
delete p;
return x;
}
template <class T>
void LinkQueue<T>::DeQueue (T x)
{
Node<T>*p,*s;
if(rear==front)
throw "下溢";
p=front;
s=front->next;
while(p!=rear)
{
if(s->data==x)
{
p->next=s->next; /* 将元素所在结点摘链 */
}
s=s->next;
p=p->next;
}
if (p->next==rear)
rear=front; /* 判断出队前队列长度是否为1 */
length--;
delete p,s;
}
/* 判断链队列是否为空函数 */
template <class T>
bool LinkQueue<T>::Empty()
{
if(front==rear)
return 1;
else
return 0;
}
/* 队列清空函数 */
template <class T>
void LinkQueue<T>::SetNullQueue ()
{
rear = front;
}
/* 返回队列长度 */
template <class T>
int LinkQueue<T>::GetLength ()
{
return length;
}
/* 判断队列里是否存在这个页面 */
template <class T>
bool LinkQueue<T>::Exist(T x)
{
Node<T> *p;
if(rear==front)
throw "下溢";
p=front->next;
while(p!=rear->next)
{
if(p->data==x)
{
return 1;
}
else
p=p->next;
}
return 0;
}
address.cpp内容
#include "address.h"
Address::Address()
{
cout<<"Start memory management."<<'\n';
cout<<"Producing address flow, wait for while, please."<<'\n';
int j=0;
flag=true;
srand((unsigned)time(NULL));
/* 生成指令序列 */
while(j<320)
{
/* 在[0,319]的指令地址之间随机选取一起点 m; */
m=rand()%320;
/* 顺序执行一条指令,即执行地址为 m+1 的指令 */
order[j]=m+1;
cout<<order[j++]<<'\t';
/* 在前地址[0,m+1]中随机选取一条指令并执行,该指令的地址为 m1 */
m1=rand()%(m+2);
/* 顺序执行一条指令,地址为 m1+1 的指令 */
order[j]=m1+1;
cout<<order[j++]<<'\t';
/* 在后地址[m’+2,319]中随机选取一条指令并执行 */
order[j]=(m1+2)+(rand()%(320-m1-2));
cout<<order[j++]<<'\t';
}
}
int Address::Judge(int m)
{
S=m;
/* 判断输入的算法号是否在[1,4] */
switch(S)
{
case 1:
flag=true;
break;
case 2:
flag=true;
break;
case 3:
flag=true;
cout<<"Wait for this algorithm to be written"<<'\n';
break;
case 4:
flag=true;
cout<<"Wait for this algorithm to be written"<<'\n';
break;
default:
cout<<"there is not the algorithm in the program"<<'\n';
flag=false;
}
return flag;
}
void Address::GetOrder(int a[])
{
for(int i=0;i<320;i++)
{
a[i]=order[i];
}
}
algorithm.cpp内容
#include "algorithm.h"
#include "linkqueue.h"
/* 先进先出算法 */
void FIFO(int order[],int size)
{
int number=0;
LinkQueue<int> q;
/* 执行320条指令序列 */
for(int i=0;i<size;i++)
{
/* 如果队列为空,则将此页面放入物理块的队列里 */
if(q.Empty())
{
q.EnQueue(order[i]);
number++;
}
else
{
/* 判断页面是否在队列物理块里面
* 若不存在则先判断是否29个物理块全部被占用,
* 若未被全部占用,直接将此页插在队列最后面,
* 若全部被占用,则将先进队列的队头元素出队,再将此页面入队
* 缺页次数+1;
* 若存在,则继续进入下一个页面*/
if(!q.Exist(order[i]))
{
if(q.GetLength()<=29)
q.EnQueue(order[i]);
else
{
q.DeQueue();
q.EnQueue(order[i]);
}
number++;
}
else if(q.Exist(order[i]))
continue;
}
}
double rate=1-number/320.0;
cout<<"FIFO算法命中率: "<<rate<<'\t';
}
/* 最近最久未使用算法 */
void LRU(int order[],int size)
{
int number=0; /* 初始化缺页次数 */
LinkQueue<int> q;
/* 执行320条指令序列 */
for(int i=0;i<size;i++)
{
/* 如果队列为空,则将此页面放入物理块的队列里 */
if(q.Empty())
{
q.EnQueue(order[i]);
number++;
}
else
{
/* 判断页面是否在队列物理块里面
* 若不存在则先判断是否29个物理块全部被占用,
* 若未被全部占用,直接将此页插在队列最后面,
* 若全部被占用,则将先进队列的队头元素出队,再将此页面入队
* 缺页次数+1;
* 若存在,将此页面先出队,再在最后入队,将最近最久未使用的页面放在队首*/
if(!q.Exist(order[i]))
{
if(q.GetLength()<=29)
q.EnQueue(order[i]);
else
{
q.DeQueue();
q.EnQueue(order[i]);
}
number++;
}
else if(q.Exist(order[i]))
{
q.DeQueue(order[i]);
q.EnQueue(order[i]);
}
}
}
/* 记录命中率 */
double rate=1-number/320.0;
cout<<"LRU算法命中率: "<<rate<<'\t';
}
main.cpp内容
#include "address.h"
#include "algorithm.h"
#include "linkqueue.h"
int main()
{
int order[320];/* 指令 */
int S; /* 算法号 */
Address ad;
cout<<"There are algorithms in the program"<<'\n';
cout<<"\t1、 Optimization algorithm"<<'\n';
cout<<"\t2、 Least recently used algorithm"<<'\n';
cout<<"\t3、 First in first out algorithm"<<'\n';
cout<<"\t4、 Least frequently used algorithm"""<<'\n';
cout<<" Select an algorithm number, please."<<endl;
while(true)
{
cin>>S;
if(ad.Judge(S))
{
break;
}
}
ad.GetOrder(order);
/* 判断输入的算法号是否在[1,4] */
switch(S)
{
case 1:
cout<<"1";
FIFO(order,320);
break;
case 2:
cout<<"2";
LRU(order,320);
break;
case 3:
cout<<"3";
break;
case 4:
cout<<"4";
break;
default:
cout<<"there is not the algorithm in the program"<<'\n';
}
}