一、实验目的
1、了解虚拟存储技术的特点,掌握虚拟存储请求页式存储管理中几种基本页面置换算法的基本思想和实现过程,并比较它们的效率。
2、了解程序设计技术和内存泄露的原因
二、实验环境
UXIN操作系统
三、实验内容
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、 画出每个页面置换算法流程图;
OPT算法
FIFO算法:
LRU算法:
2、对算法所用的数据结构进行说明;
#define N 320//指令序列最大值
#define S 32 //内存块最大值
int num[N];//数据流
int page[N];//页地址流
int mc[S] ;//内存块
int mcs[S];//内存块扫描逻辑,1代表扫描到了,0代表没扫描到,初始化为0
double Hit1, Hit2, Hit3;
3、 测试数据随机产生。不可手工输入;
4、 编写程序并调试;
#include
#include
using namespace std;
#define N 320//指令序列最大值
#define S 32 //内存块最大值
int num[N];//数据流
int page[N];//页地址流
int mc[S] ;//内存块
int mcs[S];//内存块扫描逻辑,1代表扫描到了,0代表没扫描到,初始化为0
double Hit1, Hit2, Hit3;
void initialization()
{
srand((unsigned)time(NULL));
int k = rand() % (N-1)+1;
cout << "页指令序列:" << endl;
for (int i = 0; i < N; i++)
{
num[i] = k; //指令流序列
if (i % 4 == 0||i % 4 == 2 )//50 % 的指令是顺序执行的
{
k = (k + 1) % N;
}
else if (i % 4 == 1) //25%的指令是均匀分布在前地址部分
{
if (k == 1)
{
k = 0;
}
else
{
k = rand() % (k - 1);
}
}
else if (i % 4 == 3) //25%的指令是均匀分布在后地址部分
{
k = k + rand() % (N - k) + 1;
}
cout << num[i] << "\t";
if (i % 10 == 9)
{
cout << endl;
}
page[i] = num[i] / 10;
}
cout << "页地址流:" << endl;
for (int j = 0; j < N; j++)
{
cout << page[j] << "\t";
if (j % 10 == 9)
{
cout << endl;
}
}
cout << "-----------------------------------------------------------------------------------------" << endl;
cout << "*****************************************************************************************" << endl;
}
void OPT(int max)//最佳淘汰算法(OPT):选择永不使用或在未来最长时间内不再被访问的页面予以替换。
{
for (int l = 0; l < S; l++) //初始化mc[S]内存块数组为-1
{
mc[l] = -1;
}
int Pagemissing = 0;//缺页次数
double Hit = 0; //命中次数
int flag;//标志位,内存块内是否有当前扫描到的页面
int mcflag = 0;//扫描内存块标志位
for (int i=0;i<N;i++)
{
for (int j = 0; j < max; j++)//扫描内存块
{
if (mc[j] == page[i])
{
flag = 1;//内存块内扫描得到当前页面
Hit++; //命中次数+1
cout << page[i] << "命中\t内存块内页面情况:";
for (int x = 0; x < max; x++)
{
cout << mc[x] << "\t";
}
cout << endl;
break;
}
else
{
flag = 0;//内存块内扫描不到当前页面
}
}
if (flag == 0)//未命中
{
Pagemissing++;//内存块内没有该页面,缺页次数+1
for (int m = i + 1; m < N; m++)//扫描之后的页面
{
if (mcflag != (max - 1))
{
for (int n = 0; n < max; n++)//检查永不使用或在未来最长时间内不再被访问的页面
{
if (mc[n] == page[m] && mcs[n] != 1)
{
mcs[n] = 1;
mcflag++;
}
}
}
else
{
break;
}
}
mcflag = 0; //重置扫描内存块个数标志位
for (int s = 0; s < max; s++)//先利用内存块空余的部分
{
if (mc[s] == -1)
{
cout << page[i] << "置换" << mc[s] << "内存块内页面情况:";
mc[s] = page[i];
for (int x = 0; x < max; x++)
{
cout << mc[x] << "\t";
}
cout << endl;
for (int l = 0; l < max; l++)//使mcs[S]逻辑数组为1,堵塞后面的置换
{
mcs[l] = 1;
}
break;
}
}
for (int c=0;c<max;c++)//置换
{
if (mcs[c] == 0)
{
cout << page[i] << "置换" << mc[c]<<"内存块内页面情况:";
mc[c] = page[i];
for (int x = 0; x < max; x++)
{
cout << mc[x] << "\t";
}
cout << endl;
break;
}
}
for (int l = 0; l < max; l++)//重置mcs[S]逻辑数组为0
{
mcs[l] = 0;
}
}
}
Hit1 = Hit/N;
cout << "缺页次数为:" << Pagemissing << endl;
cout << "命中次数为:" << Hit << endl;
cout << "命中率为:" << (Hit / N) << endl;
cout << "-----------------------------------------------------------------------------------------" << endl;
cout << "*****************************************************************************************" << endl;
}
void OPTX(int max)//OPT算法不打印版本
{
for (int l = 0; l < S; l++) //初始化mc[S]内存块数组为-1
{
mc[l] = -1;
}
int Pagemissing = 0;//缺页次数
double Hit = 0; //命中次数
int flag;//标志位,内存块内是否有当前扫描到的页面
int mcflag = 0;//扫描内存块标志位
for (int i = 0; i < N; i++)
{
for (int j = 0; j < max; j++)//扫描内存块
{
if (mc[j] == page[i])
{
flag = 1;//内存块内扫描得到当前页面
Hit++; //命中次数+1
break;
}
else
{
flag = 0;//内存块内扫描不到当前页面
}
}
if (flag == 0)//未命中
{
Pagemissing++;//内存块内没有该页面,缺页次数+1
for (int m = i + 1; m < N; m++)//扫描之后的页面
{
if (mcflag != (max - 1))
{
for (int n = 0; n < max; n++)//检查永不使用或在未来最长时间内不再被访问的页面
{
if (mc[n] == page[m] && mcs[n] != 1)
{
mcs[n] = 1;
mcflag++;
}
}
}
else
{
break;
}
}
mcflag = 0; //重置扫描内存块个数标志位
for (int s = 0; s < max; s++)//先利用内存块空余的部分
{
if (mc[s] == -1)
{
mc[s] = page[i];
for (int l = 0; l < max; l++)//使mcs[S]逻辑数组为1,堵塞后面的置换
{
mcs[l] = 1;
}
break;
}
}
for (int c = 0; c < max; c++)//置换
{
if (mcs[c] == 0)
{
mc[c] = page[i];
break;
}
}
for (int l = 0; l < max; l++)//重置mcs[S]逻辑数组为0
{
mcs[l] = 0;
}
}
}
Hit1 = Hit / N;
}
void FIFO(int max)//先进先出的算法(FIFO):选择在内存中驻留时间最久的页面予以替换。
{
for (int l = 0; l < S; l++) //初始化mc[S]内存块数组为-1
{
mc[l] = -1;
}
int Pagemissing = 0;//缺页次数
double Hit = 0; //命中次数
int flag;//标志位,内存块内是否有当前扫描到的页面
int mcflag = 0;//扫描内存块标志位
for (int i = 0; i < N; i++)
{
for (int j = 0; j < max; j++)
{
if (mc[j] == page[i])
{
flag = 1;//内存块内扫描得到当前页面
Hit++; //命中次数+1
cout << page[i] << "命中\t内存块内页面情况:";
for (int x = 0; x < max; x++)
{
cout << mc[x] << "\t";
}
cout << endl;
break;
}
else
{
flag = 0;
}
}
if (flag == 0)//未命中
{
Pagemissing++;//缺页次数+1
int k;
k = mcflag % max;
cout << page[i] << "置换" << mc[k] << "内存块内页面情况:";
mc[k] = page[i];
for (int x = 0; x < max; x++)
{
cout << mc[x] << "\t";
}
cout << endl;
mcflag++;
}
}
Hit2 = Hit/N;
cout << "缺页次数为:" << Pagemissing << endl;
cout << "命中次数为:" << Hit << endl;
cout << "命中率为:" << (Hit / N) << endl;
cout << "-----------------------------------------------------------------------------------------" << endl;
cout << "*****************************************************************************************" << endl;
}
void FIFOX(int max)//FIFO算法不打印版本
{
for (int l = 0; l < S; l++) //初始化mc[S]内存块数组为-1
{
mc[l] = -1;
}
int Pagemissing = 0;//缺页次数
double Hit = 0; //命中次数
int flag;//标志位,内存块内是否有当前扫描到的页面
int mcflag = 0;//扫描内存块标志位
for (int i = 0; i < N; i++)
{
for (int j = 0; j < max; j++)
{
if (mc[j] == page[i])
{
flag = 1;//内存块内扫描得到当前页面
Hit++; //命中次数+1
break;
}
else
{
flag = 0;
}
}
if (flag == 0)//未命中
{
Pagemissing++;//缺页次数+1
int k;
k = mcflag % max;
mc[k] = page[i];
mcflag++;
}
}
Hit2 = Hit / N;
}
void LRU(int max)//最近最久未使用算法(LRU):选择过去最长时间未被访问的页面予以替换。
{
for (int l = 0; l < S; l++) //初始化mc[S]内存块数组为-1
{
mc[l] = -1;
}
int Min;
int Pagemissing = 0;//缺页次数
double Hit = 0; //命中次数
int flag;//标志位,内存块内是否有当前扫描到的页面
int mcflag = 0;//扫描内存块更换优先级,数值越低,优先级越高
for (int i = 0; i < N; i++)
{
for (int j = 0; j < max; j++)
{
if (mc[j] == page[i])
{
flag = 1;//内存块内扫描得到当前页面
Hit++; //命中次数+1
mcs[j] = mcflag;
mcflag++;
cout << page[i] << "命中\t内存块内页面情况:";
for (int x = 0; x < max; x++)
{
cout << mc[x] << "\t";
}
cout << endl;
break;
}
else
{
flag = 0;
}
}
if (flag == 0)//未命中
{
int min = N;
for (int q = 0; q < max; q++)//扫描哪个内存块最近最长时间未被访问
{
if (mcs[q] < min)
{
min = mcs[q];
Min = q; //扫描msc逻辑数组元素中的最小值,获取其下标
}
}
mcs[Min] = mcflag;
mcflag++;
Pagemissing++;//缺页次数+1
cout << page[i] << "置换" << mc[Min] << "内存块内页面情况:";
mc[Min] = page[i];
for (int x = 0; x < max; x++)
{
cout << mc[x] << "\t";
}
cout << endl;
}
}
for (int p = 0; p < max; p++)
{
mcs[p] = 0;
}
Hit3 = Hit/N;
cout << "缺页次数为:" << Pagemissing << endl;
cout << "命中次数为:" << Hit << endl;
cout << "命中率为:" << (Hit / N) << endl;
cout << "-----------------------------------------------------------------------------------------" << endl;
cout << "*****************************************************************************************" << endl;
}
void LRUX(int max)//LRU算法不打印版本
{
for (int l = 0; l < S; l++) //初始化mc[S]内存块数组为-1
{
mc[l] = -1;
}
int Min;
int Pagemissing = 0;//缺页次数
double Hit = 0; //命中次数
int flag;//标志位,内存块内是否有当前扫描到的页面
int mcflag = 0;//扫描内存块更换优先级,数值越低,优先级越高
for (int i = 0; i < N; i++)
{
for (int j = 0; j < max; j++)
{
if (mc[j] == page[i])
{
flag = 1;//内存块内扫描得到当前页面
Hit++; //命中次数+1
mcs[j] = mcflag;
mcflag++;
break;
}
else
{
flag = 0;
}
}
if (flag == 0)//未命中
{
int min = N;
for (int q = 0; q < max; q++)//扫描哪个内存块最近最长时间未被访问
{
if (mcs[q] < min)
{
min = mcs[q];
Min = q; //扫描msc逻辑数组元素中的最小值,获取其下标
}
}
mcs[Min] = mcflag;
mcflag++;
Pagemissing++;//缺页次数+1
mc[Min] = page[i];
}
}
for (int p = 0; p < max; p++)
{
mcs[p] = 0;
}
Hit3 = Hit / N;
}
void main()
{
int a;//功能序号
do
{
cout << "1、初始化" << endl;
cout << "2、最佳淘汰算法(OPT)" << endl;
cout << "3、先进先出的算法(FIFO)" << endl;
cout << "4、最近最久未使用算法(LRU)" << endl;
cout << "5、对比OPT、FIFO、LRU三个算法的命中率(加载较慢)" << endl;
cout << "6、退出程序" << endl;
cout << "请输入需要进行的算法:";
cin >> a;
switch (a)
{
case 1:initialization(); break;
case 2:int x;
cout << "请输入内存块数(4~32):";
cin >> x;
if (x >= 4 && x <= 32)
{
OPT(x); break;
}
else
{
cout << "输入错误,请重新输入!!(只剩下一次机会!!)" << endl;
cout << "请输入内存块数(4~32):";
cin >> x;
if (x >= 4 && x <= 32)
{
OPT(x); break;
}
else
{
cout << "输入错误!!" << endl; break;
}
}
case 3:int y;
cout << "请输入内存块数(4~32):";
cin >> y;
if (y >= 4 && y <= 32)
{
FIFO(y); break;
}
else
{
cout << "输入错误,请重新输入!!(只剩下一次机会!!)" << endl;
cout << "请输入内存块数(4~32):";
cin >> y;
if (y >= 4 && y <= 32)
{
FIFO(y); break;
}
else
{
cout << "输入错误!!" << endl; break;
}
}
case 4:int z;
cout << "请输入内存块数(4~32):";
cin >> z;
if (z >= 4 && z <= 32)
{
LRU(z); break;
}
else
{
cout << "输入错误,请重新输入!!(只剩下一次机会!!)" << endl;
cout << "请输入内存块数(4~32):";
cin >> z;
if (z >= 4 && z <= 32)
{
LRU(z); break;
}
else
{
cout << "输入错误!!" << endl; break;
}
}
case 5:
int s;
cout << "请输入内存块数(4~32):";
cin >> s;
if (s >= 4 && s <= 32)
{
OPTX(s);
FIFOX(s);
LRUX(s);
}
else
{
cout << "输入错误,请重新输入!!(只剩下一次机会!!)" << endl;
cout << "请输入内存块数(4~32):";
cin >> s;
if (s >= 4 && s <= 32)
{
OPTX(s);
FIFOX(s);
LRUX(s);
}
else
{
cout << "输入错误!!" << endl;
}
}
cout << "OPT算法的命中率:" << Hit1 << endl;
cout << "FIFO算法的命中率:" << Hit2 << endl;
cout << "LRU算法的命中率:" << Hit3 << endl;
cout << "-----------------------------------------------------------------------------------------" << endl;
break;
}
} while (a != 6);
}
5、 多次测试程序,截屏输出实验结果;
6、 根据实验结果与理论课讲述的原理进行实验分析。
六、思考题
1、从几种算法的命中率看,哪个算法最高?哪个算法最低?对每个页面的执行结果进行分析。
OPT算法命中率最高,FIFO和LRU相差无几
随着内存块的增加,三个算法的命中率增加,同时三个算法的命中率趋于相同
2、OPT算法在执行过程中可能会发生错误,为什么?
OPT算法本身就是一个无法实现的算法,毕竟现实中无法知道未来,只能作为用于衡量其他算法效率的标尺