文章目录
一、实验内容
1、模拟实现请求页式存储管理的几种基本页面置换算法
(1)最佳淘汰算法(OPT)
(2)先进先出的算法(FIFO)
(3)最近最久未使用算法(LRU))
二、实验原理
1、虚拟存储系统
UNIX中,为了提高内存利用率,提供了内外存进程对换机制;内存空间的分配和回收均以页为单位进行;一个进程只需将其一部分(段或页)调入内存便可运行;还支持请求调页的存储管理方式。
当进程在运行中需要访问某部分程序和数据时,发现其所在页面不在内存,就立即提出请求(向CPU发出缺中断),由系统将其所需页面调入内存。这种页面调入方式叫请求调页。
为实现请求调页,核心配置了四种数据结构:页表、页框号、访问位、修改位、有效位、保护位等。
2、页面置换算法
当CPU接收到缺页中断信号,中断处理程序先保存现场,分析中断原因,转入缺页中断处理程序。该程序通过查找页表,得到该页所在外存的物理块号。如果此时内存未满,能容纳新页,则启动磁盘I/O将所缺之页调入内存,然后修改页表。如果内存已满,则须按某种置换算法从内存中选出一页准备换出,是否重新写盘由页表的修改位决定,然后将缺页调入,修改页表。利用修改后的页表,去形成所要访问数据的物理地址,再去访问内存数据。整个页面的调入过程对用户是透明的。
(1)最佳淘汰算法(OPT):选择永不使用或在未来最长时间内不再被访问的页面予以替换。
(2)先进先出的算法(FIFO):选择在内存中驻留时间最久的页面予以替换。
(3)最近最久未使用算法(LRU):选择过去最长时间未被访问的页面予以替换。
3、首先用srand( )和rand( )函数定义和产生指令序列,然后将指令序列变换成相应的页地址流,并针对不同的算法计算出相应的命中率。
(1)通过随机数产生一个指令序列,共320条指令。指令的地址按下述原则生成:
A:50%的指令是顺序执行的
B:25%的指令是均匀分布在前地址部分
C:25%的指令是均匀分布在后地址部分
具体的实施方法是:
A:在[0,319]的指令地址之间随机选取一起点m
B:顺序执行一条指令,即执行地址为m+1的指令
C:在前地址[0,m+1]中随机选取一条指令并执行,该指令的地址为m’
D:顺序执行一条指令,其地址为m’+1
E:在后地址[m’+2,319]中随机选取一条指令并执行
F:重复步骤A-E,直到320次指令
(2)将指令序列变换为页地址流
设:页面大小为1K;
用户内存容量4页到32页;
用户虚存容量为32K。
在用户虚存中,按每K存放10条指令排列虚存地址,即320条指令在虚存中的存放方式为:
第 0 条-第 9 条指令为第0页(对应虚存地址为[0,9])
第10条-第19条指令为第1页(对应虚存地址为[10,19])
………………………………
第310条-第319条指令为第31页(对应虚存地址为[310,319])
按以上方式,用户指令可组成32页。
因为是模拟程序,可以不使用系统调用函数
三、实验步骤
1.画出每个页面置换算法流程图
S=MAX(320)
OPT:
⭐LRU也是这样的流程,不同的地方是Ofar函数不同
Ofar()流程图:
P=start(图写错了)
Far算法是变量内存数组,然后寻找距离最远的数,目的是更新或使最远页数在内存中的下标值,然后返回这个需要更改的下标值
Lfar()流程图:
Lfar的流程,只改变了distance计算方法和p的移动方向
FIFO:
2.对算法所用的数据结构进行说明:
数组说明:
随机生成320条指令函数:
cpage():
将随机指令转换成页地址流的函数
find():
判断页是否在内存中 其实是一个查找数num是否在参数数组内的一个函数
Ofar():
在OPT算法中寻找最久未使用或未来不使用的内存内的页号的下标
Lfar():
LRU算法中寻找最近最久未使用的内存内的页号的下标
这里加入LRU算法中的查找所需下标的函数,因为两个函数很像,只是查找方向相反
LRU算法实现
OPT算法实现
和上图一样,因为这部分函数主要是判断指针所指的页号是否在内存,并调用查找需要替换的内存下标并进行替换
FIFO算法实现
3. 测试数据随机产生。不可手工输入:
4.多次测试程序,截屏输出实验结果:
OPT
因为320,32,不易观察,下图数据暂时修改成150和15
只显示发生缺页中断时的页地址流和内存表
LRU
因为320,32,不易观察,下图数据暂时修改成150和15
只显示发生缺页中断时的页地址流和内存表
FIFO
出现该问题的原因: 首次构造链表算法写错了,改成后插法就可以了(为了记录问题所以没有更改)
第一次输出出问题原因在于我算法是先将内存表填满,FIFO算法内存表用了链表,在第一次构建链表的时候没有用后插法创建链表,有问题的算法为:
过程为先赋值p->data,在创建新结点,p后移,但是这样的问题是当最后一个数赋值后,后面还是会连着一个data为空的新结点,导致在print的时候p指向最后一个数据时p->next不为NULL(指向空结点,空结点next为NULL)
但是不影响后面的算法,因为last指向是正确的
5.根据实验结果与理论课讲述的原理进行实验分析
OPT算法页号命中率:
LRU算法页号命中率:
输出LRU命中率会出现只输出一部分从内存大小最小值开始的命中率的问题(如只输出内存4k到17k就停下来了)通过更改内存大小的区间(如设置18k-31k)才得到内存由4k-31k的命中率,所以上面的结果图是运行了两次然后截图的
( 另外两个就不会,我也不知道为什么😔)
FIFO算法命中率
五、实验数据及源代码
写了很多输出语句用于判断算法是否正确,可以忽略
#include <iostream>
#include <ctime>
#include <iomanip>
using namespace std;
#define MAX 320
#define MAX2 32
void create(int sequence[])
{
int i = 0;//计数器
while (1)
{
if (i == MAX)break;
int m = rand() % MAX;
sequence[i++] = m++;//执行m+1
m = rand() % m;//在前地址[0,m+1]中随机选取一条指令并执行
if (i == MAX)break;
sequence[i++] = m;//执行m'
if (i == MAX)break;
sequence[i++] = ++m;//执行m'+1
m = rand() % MAX + (m+1);
if (i == MAX)break;
sequence[i++] = m;//执行后地址中随机指令
}
cout << "指令生成成功" << endl;
}
static int s = MAX;//页地址流最大长度
void cpage(int sequence[MAX], int page[MAX])
{
int i=0,j;
while (i < MAX)
{
j = sequence[i] / 10;//求页号
page[i] = j;
i++;
}
}
int find(int pnum, int caqa[MAX2],int sum)//页是否在内存
{
int flag = 0;
for (int i = 0; i < sum;i++)
{
if (pnum == caqa[i]) { flag = 1; break; }
flag = 0;
}
return flag;
}
void print(int A[],int sum)//数组输出函数
{
for (int i = 0; i < sum; i++)
cout << A[i] << " ";
}
void printhit(double A[], double Asum[],int ckind)//数组输出函数
{
for (int i = 0; i < ckind; i++)
{
if(A[i]!=NULL)
cout << "page" << i << " 命中率:"<< A[i]/Asum[i] << endl;
}
}
int Ofar(int page[MAX], int caqa[MAX2], int sum, int start)
{
int p = start;
int i = 0;
int index;
int distance = 0;
while (i<sum)
{
if (caqa[i] != page[p])p++;
else {
index = distance < p - start ? i : index;
distance = distance < p - start ? p - start : distance;
i++; p = start;
}
if (p == s + 1) { index = i; break; }//如果p到了最后还没找到,则说明是不被使用的老页面
}
return index;
}
void OPT(int page[MAX],int caqa[MAX2],int sum)
{
double falsehit = 0;
int ckind = 1;//页地址流的总页类数
int change;
int ci = 0;
for (int i = 0; i < MAX;i++)
{
if (!find(page[i], caqa, sum))
caqa[ci++] = page[i];
if (ci ==sum)break;
}//填充
int p = sum;//p指向当前位置
while (p < s)//页号小于页地址最大长度时
{
if (!find(page[p], caqa, sum))//缺页中断(page不在caqa里)
{
falsehit++;//缺页次数+1;
change = Ofar(page, caqa, sum, p);//替换的数组下标
/*print(page, s); cout << "\n";cout << "缺页中断! 需要页号:" << page[p] << endl;cout << "原内存表:";print(caqa, sum); cout << "\n";cout << "替换的页:" << caqa[change] << endl;*/
caqa[change] = page[p];//替换后p指针后移
/*cout << "内存表:";
print(caqa, sum); cout << "\n";*/
}
p++;
}
cout << "内存大小" << sum <<"k" << " 命中率:" << 1 - (falsehit / MAX) << endl;
}
//先进先出用链表
typedef struct LNode
{
int data;//存放页号
struct LNode* next;
}*LinkList;
int Ffind(LinkList& L,int num)//寻找num在不在链表里
{
int flag = 0;
auto p = L;
while (p != NULL)
{
if (p->data == num) { flag = 1; break; }
p = p->next;
}
return flag;
}
void printL(LinkList& L)
{
auto p = L;
while (p!= NULL)
{
cout << p->data << "-> ";
p = p->next;
}
cout << "\n";
}
void FIFO(int page[MAX], int caqa[MAX2], int sum)
{
LinkList L;
double falsehit = 0;
L = new LNode;
L->next = NULL;
auto head = L;
auto p = head;
int i = 0, j = 0;//j相当于在页地址流移动的快指针
while (i<sum)//填充
{
//如果j所指的页号不在链表
if (!Ffind(L, page[j])) { p->data = page[j]; auto temp = new LNode; temp->next = NULL; p->next = temp; p = p->next; p->next = NULL; i++;}
j++;
}
j = sum;
auto last = head;
while (last->next->next != NULL)
last = last->next;
while (j<s)
{
if (!Ffind(head, page[j]))
{
falsehit++;
/*print(page, s); cout << "\n";cout << "缺页中断! 需要页号:" << page[j] << endl;cout << "原内存表:";printL(head);*/
auto newhead = head->next; head->next = NULL; head = newhead;
auto temp = new LNode; temp->next = NULL; temp->data = page[j];
last->next = temp; last = last->next; last->next = NULL;
/*cout << "修改后内存表:";
printL(head);*/
}
j++;
}
cout << "内存大小" << sum << "k" << " 命中率:" << 1 - (falsehit / MAX) << endl;
}
int Lfar(int page[MAX], int caqa[MAX2], int sum, int start)
{
int p = start-1;
int i = 0;
int index;
int distance = 0;
while (i < sum)
{
if (caqa[i] != page[p])p--;
else {
index = distance < start - p ? i : index;
distance = distance < start - p ? start - p : distance;
i++; p = start-1;
}
}
return index;
}
void LRU(int page[MAX], int caqa[MAX2], int sum)
{
double falsehit = 0;
int change;
int ci = 0;
for (int i = 0; i < MAX; i++)
{
if (!find(page[i], caqa, sum))
caqa[ci++] = page[i];
if (ci == sum)break;
}//填充
//print(caqa, sum);
int p = sum;//p指向当前位置
while (p < s)//页号小于页地址最大长度时
{
if (!find(page[p], caqa, sum))//缺页中断(page不在caqa里)
{
falsehit++;//缺页次数+1;
change = Lfar(page, caqa, sum, p);//替换的数组下标
/*print(page, s); cout << "\n";
cout << "缺页中断! 需要页号:" << page[p] << endl;
cout << "原内存表:";
print(caqa, sum); cout << "\n";
cout << "替换的页:" << caqa[change] << endl;*/
caqa[change] = page[p];//替换后p指针后移
/*cout << "内存表:";
print(caqa, sum); cout << "\n";*/
}
p++;
}
cout << "内存大小" << sum << "k" << " 命中率:" << 1 - (falsehit / MAX) << endl;
}
int main()
{
srand((unsigned long)time(0));
int sequence[MAX]{};//指令序列
int page[MAX]{};//页地址流
int capacity = rand() % (MAX2-4+1) + 4;//随机内存
int caqa[MAX2];//内存
create(sequence);
cpage(sequence,page);
//OPT(page, caqa, capacity);
//LRU(page, caqa, capacity);
//FIFO(page, caqa, capacity);
}