16281005操作系统实验四
实现页面置换算法
实验目的及要求
设计和实现最佳置换算法、先进先出置换算法、最近最久未使用置换算法、页面缓冲置换算法;通过页面访问序列随机发生器实现对上述算法的测试及性能比较。
模块设计
菜单模块:提供对应功能的界面选择
主要函数为:功能主菜单:main()
功能函数模块:主要实现程序要求的集中算法功能
主要函数:
①最佳置换算法:void OPT()
②先进先出置换算法:void FIFO()
③最近最久未使用置换算法:void LRU()
④改进型Clock置换算法:void gClock()
⑤页面缓冲算法PBA:void PBA()
辅助函数模块:对于功能函数的实现进行辅助作用
主要函数:
初始化函数:void initialize()
访问序列生成函数:void createps(void)
查找页面函数:int findpage(int page)
显示函数:void displayinfo(void)
函数功能及接口
功能主菜单:通过该函数向用户介绍本实验的菜单函数,使用户能够直观的查看实验程序如果实现的主要功能有哪些,并且能够通过相应的功能选择函数进入到指定的算法测试模块,进行实验。
初始化:对于程序中的各个函数变量进行初始化,同时实现将上次程序运行中程序内数据的清空,从而避免上次程序运行产生的影响。
访问序列生成函数:根据已有算法实现对应访问序列的生成
查找页面函数:其输入值为当前访问的页面页号,根据当前页号与内存框中页面号的匹配,从而获取当前访问内存的情况,对于此次访问的页号判断是缺页还是在内存内,返回缺页情况。1为在内存中,0为不在即缺页
显示函数:输出当前访问页号,内存框中存储页号情况,已经当前访问是否缺页。
主要功能函数:
- 最佳置换算法:设计实现最佳置换算法,并计算对应缺页率
- 先进先出置换算法函数:设计实现先进先出置换算法,并计算对应缺页率
- 最近最久未使用置换算法:设计实现最近最久未使用算法,并计算对应缺页率
- 改进型Clock置换算法:设计实现改进型Clock置换算法,并计算对应缺页率
- 页面缓冲算法PBA:设计实现页面缓冲算法PBA算法,并计算对应缺页率
辅助功能函数具体设计
1.功能主菜单
主要在于switch语句分支结构的使用,进行不同函数块功能的调用,流程如下:
具体实现代码如下:
int main()
{
int flag = 1;
int chioce;
while (flag)
{
cout<<"*******************************************"<<endl;
cout << " 若要执行最佳置算法 请按1"<< endl;
cout << " 若要执行FIFO 页面置算法 请按2" << endl;
cout << " 若要执行LRU页面置算法 请按3" << endl;
cout << " 若要执行改进型Clock算法 请按4" << endl;
cout << " 若要执行页面缓冲算法PBA 请按5" << endl;
cout << " 若要退出 请按0" << endl;
cout << "*******************************************\n" << endl;
cout << "输入你的选项 " ;
cin >> chioce;
while (chioce != 1 && chioce != 2 && chioce != 3 && chioce != 4 && chioce != 5 && chioce != 0){
cout << "输入错误!!!请重新输入" << endl;
cin >> chioce;
}
switch (chioce)
{
case 1:
cout << "*****************************页面最佳置换算法****************************"<< endl;
OPT();
break;
case 2:
cout << "*****************************FIFO页面置换算法****************************" << endl;
FIFO();
break;
case 3:
cout << "*****************************LRU页面置换算法****************************" << endl;
LRU();
break;
case 4:
cout << "*****************************改进型Clock算法****************************" << endl;
gClock();
break;
case 5:
cout << "*****************************页面缓冲算法PBA****************************" << endl;
PBA();
break;
case 0:
cout << "再见!!!" << endl;
flag = 0;
break;
}
system("pause");
}
return 0;
}
实现如下图所示的程序菜单:
2.初始化函数
对于程序中的各个函数变量进行初始化,在这里我们可以实现对于内存中页框数的相关设置,同时实现将上次程序运行中程序内数据的清空,从而避免上次程序运行产生的影响。
void initialize()
{
int i, pf;
inpflag = 0;
pf_info.diseffect = 0;
pf_info.flag = 0;
printf("\n请输入要分配的页框数:");
scanf("%d", &pf);
pf_info.total_pf = pf;
for (i = 0; i<100; i++) // 清空页面序列
{
pf_info.serial[i] = -1;
}
}
3.访问序列生成函数
随机生成访问序列:为了满足生成序列的局部性原理,采取以下的算法进行对应访问序列的生成过程。
算法过程如下:
1、设定工作集的起始位置p,工作集中包含的页数e,工作集移动率m(每处理m个页面访问则确定一次工作集的起始位置),以及一个范围在0和1之间的值t;
2、生成m个取值范围在p和p + e间的随机数,并记录到页面访问序列串中;
3、生成一个随机数r,0 ≤ r ≤ 1;
4、如果r < t,则为p生成一个新值;
如果想继续加大页面访问序列串的长度,请返回第2步,否则结束。
函数流程图如下:
函数具体实现代码如下:
// 随机生成访问序列
void createps(void)
{
int pn;
initialize(); //初始化相关数据结构
cout<<"请输入要随机生成访问序列的长度"; //自定义随机生成访问序列的长度
scanf("%d", &pn);
int p=1, e,m;
double t = 0.5;
cout << "请输入工作集中页面数:";
cin >> e;
cout << "请输入工作集移动率:" ;
cin >> m;
srand((unsigned)time(NULL)); //初始化随机数队列的"种子"
pf_info.total_pn = pn;
for (int j = 0; j < pn;){
for (int i = 0; i<m; i++,j++) //产生随机访问序列
{
pf_info.serial[j] = rand() % (e+1) + p; //随机数的大小在p-p+e之间
}
double r = rand() / double(RAND_MAX);
if (r>t){
p = rand() % ( 15- 1 + 1) + 1;//为p生成一个在1-15之间的新值
}
else{
p = p + 1;
}
}
}
4.查找页面函数
进行当前页号与内存框中页面号的匹配,并且记录访问页面的历史情况,返回缺页情况,设置全局变量缺页指针inpflag对应的值 ,0为不缺页,1为缺页。
代码实现如下:
// 查找页面是否在内存,1为在内存,0为不在即缺页
int findpage(int page)
{
int n;
for (n = 0; n<pf_info.total_pf; n++)
{
pagehistory[n] ++; // 访问历史加1
}
for (n = 0; n<pf_info.total_pf; n++)
{
if (pageframe[n] == page)
{
inpflag = 0; //inpflag缺页标志,0为不缺页,1为缺页
pagehistory[n] = 0; //置访问历史为0
return 1;
}
}
inpflag = 1; //页面不存在,缺页
return 0;
}
5.显示函数
输出当前访问页号,内存框中存储页号情况,以及当前访问是否缺页。
实现如下图所示的程序输出界面:
代码实现如下:
// 显示当前状态及缺页情况
void displayinfo(void)
{
int i, n;
//显示页面访问序列情况
if (vpoint == 0)
{
printf("\n=============页面访问序列=============\n");
for (i = 0; i<pf_info.total_pn; i++)
{
printf("%4d", pf_info.serial[i]);
if ((i + 1) % 10 == 0) printf("\n"); //每行显示10个
}
printf("\n======================================\n");
}
//以后每次访问页面,输出当前的访问页面与对应页框信息及缺页率
printf("访问%3d : 内存<", pf_info.serial[vpoint]);
for (n = 0; n<pf_info.total_pf; n++) // 页框信息
{
if (pageframe[n] >= 0) //如果页框内有值(值大于-1),进行对应输出
printf("%3d", pageframe[n]);
else
printf(" ");
}
printf(" >");
if (inpflag == 1)
{
printf(" ==>缺页 ");
}
printf("\n");
}
主要算法函数具体设计
1.最佳置换算法 OPT
算法思想:选择永不使用或是在最长时间内不再被访问(即距现在最长时间才会被访问)的页面淘汰出内存,也即需要发生页面置换时,算法总是选择在将来最不可能访问的页面进行置换。
实例说明:进程运行时,先将7, 0, 1三个页面依次装入内存。进程要访问页面2时,产生缺页中断,根据最佳置换算法,选择第18次访问才需调入的页面7予以淘汰。然后,访问页面0时,因为已在内存中所以不必产生缺页中断。访问页面3时又会根据最佳置换算法将页面1淘汰……依此类推,如图3-26所示。从图中可以看出釆用最佳置换算法时的情况。
实现:
1、如果页框中的某个页面P以后永不使用,则该页面为淘汰页面Pt。
2、如果每个P都会再次被访问,那么其中最长未来时间内不再被访问的页面为淘汰页面Pt。
程序流程图设计如下:
对应部分程序代码如下:
for (int i = 0; i < pf_info.total_pf; i++){
for (int n = vpoint; n<pf_info.total_pn; n++)
{
if (pageframe[i] == pf_info.serial[n] && n>max)
{
max = n;
p = i;
break;
}
else if (n == pf_info.total_pn - 1){
p = i;
i = pf_info.total_pf;
break;
}
}
}
对于所有置换算法而言,对于访问页面的装入过程以及对于访问页面是否缺页的判断和实现都是相似之处,核心内容的不同之处在于当访问页面不存在与内存中时,我们需要的进行置换页面的方式方法,对于算法实现主要针对于这部分内容进行详细描述。而对于所有置换算法相类似的页面装入和是否缺页的判断过程则如下代码所示。
pstate = findpage(pf_info.serial[vpoint]); //查找页面是否在内存
if (count<pf_info.total_pf) // 开始时不计算缺页
{
if (pstate == 0) // 页不存在则装入页面
{
pageframe[rpoint] = pf_info.serial[vpoint]; //把要调入的页面放入一个空的页框里
rpoint = (rpoint + 1) % pf_info.total_pf;
count++;
}
inpflag = 0;
}
2.先进先出置换算法FIFO
算法思想:优先淘汰最早进入内存的页面,亦即在内存中驻留时间最久的页面。该算法实现简单,只需把调入内存的页面根据先后次序链接成队列,设置一个指针总指向最早的页面。但该算法与进程实际运行时的规律不适应,因为在进程中,有的页面经常被访问。
实例说明:进程运行时,先将7, 0, 1三个页面依次装入内存。进程要访问页面2时,产生缺页中断,根据先进先出算法,将最先进入内存的7置换出页面,装入2。当访问3号页面时,将从上依次装入内存到现在为止的最早装入内存的0置换出页面……依此类推,如图所示。从图中可以看出釆用先进先出时的情况。
实现:选择最先进入内存即在内存驻留时间最久的页面换出到外存,进程已调入内存的页面按进入先后次序链接成一个队列,并设置替换指针以指向最老页面。
算法程序框图:
当内存框装满时,访问序列的页面发生缺页现象时,使用先进先出置换算法的核心代码如下:依次置换出在内存中存在时间最长的页面。
算法代码:
for (vpoint = 0; vpoint<pf_info.total_pn; vpoint++) // 执行算法
{
pstate = findpage(pf_info.serial[vpoint]); //查找页面是否在内存
```
内存页面未满时,装入内存
```
else // 页面满时,正常缺页置换
{
if (pstate == 0) //页不存在则置换页面
{
pageframe[rpoint] = pf_info.serial[vpoint];
rpoint = (rpoint + 1) % pf_info.total_pf;
pf_info.diseffect++; // 缺页次数加1
}
}
displayinfo(); // 显示当前状态
} // 置换算法循环结束
3.最近最久置换算法LRU
算法思想:选择最近最长时间未访问过的页面予以淘汰,它认为过去一段时间内未访问过的页面,在最近的将来可能也不会被访问。该算法为每个页面设置一个访问字段,来记录页面自上次被访问以来所经历的时间,淘汰页面时选择现有页面中值最大的予以淘汰。
实例说明:进程运行时,先将7, 0, 1三个页面依次装入内存。进程要访问页面2时,产生缺页中断,根据最近最久置换算法,判断内存中的页面,发现1号页面在上一时刻刚刚使用过,在之比较0后页面和7号页面,发现7号页面最近最久未使用,所以将7号页面置换出来,装入2号页面。同理当访问3号页面时,将1号页面置换出内存……依此类推,如图所示。从图中可以看出釆用LRU时的情况。
算法程序框图:
算法代码:
if (pstate == 0)// 页不存在则置换页面
{
max = 0;
for (n = 1; n<pf_info.total_pf; n++)
{
if (pagehistory[n]>pagehistory[max])
{
max = n;
}
}
rpoint = max;
pageframe[rpoint] = pf_info.serial[vpoint];
pagehistory[rpoint] = 0;
pf_info.diseffect++; // 缺页次数加1
}
4.改进Clock()算法
算法思想:
在将一个页面换出时,如果该页已被修改过,便须将该页重新写回到磁盘上;但如果该页未被修改过,则不必将它拷回磁盘。在改进型Clock算法中,除须考虑页面的使用情况外,还须在增加一个因素,即置换代价,这样页面换出时,既要是未使用过的页面,又要是未被修改过的页面。把同时满足这两个条件的页面作为首选淘汰的页面。由访问位A和修改位M可以组合成下面四种类型的页面:
1类(A=0,M=0):表示该页最近既未被访问,又未被修改,是最佳淘汰页。
2类(A=0,M=0):表示该页最近未被访问,但已被修改,并不是很好的淘汰页。
3类(A=1,M=0):表示该页最近已被访问,但未被修改,该页有可能在被访问。
4类(A=1,M=1):表示该页最近已被访问且被修改,该页可能再被访问。
实现:
①:从查寻指针当前位置起扫描内存分页循环队列,选择A=0且M=0的第一个页面淘汰;若未找到,转②;
②:开始第二轮扫描,选择A=0且M=1的第一个页面淘汰,同时将经过的所有页面访问位置0;若不能找到,转①
实例说明:进程运行时,先将7, 0, 1三个页面依次装入内存。进程要访问页面2时,产生缺页中断,根据改进clock算法,遍历内存中的页面,寻找访问位和修改位均为0的优先置换页面如果发现则进行由2号页面将其置换出去,如未发现则进行第二次遍历。第二次遍历是查找访问位为0,修改位均为1的置换页面,同时在查找页面时将页面的访问位置置为0……依此类推,进行实现。
算法程序框图:
算法代码:
if (pstate == 0)// 页不存在则置换页面
{
rpoint = 0;
int flag=0;
for (n = 0; n<pf_info.total_pf; n++)
{
if (paccess[n] == 0 && pmodify[n]==0)
{
rpoint = n;
break;
}
if (n == pf_info.total_pf - 1)flag = 1;
}
if (flag){
for (n = 0; n<pf_info.total_pf; n++)
{
if (paccess[n] == 0 && pmodify[n] == 1)
{
rpoint = n;
break;
}
paccess[n] = 0;
}
}
pageframe[rpoint] = pf_info.serial[vpoint];
pmodify[rpoint] = rand() % 2; //随机植入修改位值
pf_info.diseffect++; // 缺页次数加1
}
5.页面缓冲PBA算法
算法思想:选择最近最长时间未访问过的页面予以淘汰,它认为过去一段时间内未访问过的页面,在最近的将来可能也不会被访问。该算法为每个页面设置一个访问字段,来记录页面自上次被访问以来所经历的时间,淘汰页面时选择现有页面中值最大的予以淘汰。
实例说明:进程运行时,先将7, 0, 1三个页面依次装入内存。进程要访问页面2时,产生缺页中断,根据最近最久置换算法,判断内存中的页面,发现1号页面在上一时刻刚刚使用过,在之比较0后页面和7号页面,发现7号页面最近最久未使用,所以将7号页面置换出来,装入2号页面。同理当访问3号页面时,将1号页面置换出内存……依此类推,如图所示。从图中可以看出釆用LRU时的情况。
算法程序框图:
在PBA置换算法中新增变量为Pb的结构体数组,用以模拟在操作系统中的空闲链的情况
struct Pb
{
int Memnum; //空闲列对应内存块号
int Pagenum; //空闲列对应访问页号
}pb[2];
在算法的运行之前我们还需要对于空闲链信息进行初始化如下所示:
existence[pf_info.total_pf] = 0;
existence[pf_info.total_pf+1] = 0;//设置两位的存在位
pb[0].Memnum = pf_info.total_pf;
pb[0].Pagenum = -1;
pb[1].Memnum = pf_info.total_pf+1;
pb[1].Pagenum = -1; //初始化空闲链
我们在程序中人工的设置了两个空闲链的内存块,也即在新增两块内存框,同时为内存页框中设置了存在位,如两个新增空闲链的内存块若其不使用则其存在位为0,不在输出中进行显示,算法主旨仍然为使用FIFO算法进行置换页面,但是不同之处在于他有二级缓冲内存块也即存储在空闲链中,当访问页面不在内存中时,会首先访问空闲链内对应的内存块,观察页面是否存储在其中,如果存储在空闲链的内存块中,则会减少进程访问内存的开销,程序效率更高。
算法代码:
else // 正常缺页置换
{
if (pstate == 0)// 页不存在则置换页面
{
inpflag = 1;//缺页
int flag = 0;//页面是否在空闲链内,0不在,1在
for (int i = 0; i < 2; i++){
if (pb[i].Pagenum == pf_info.serial[vpoint]){
existence[pb[i].Memnum] = 1;
pageframe[pb[i].Memnum] = pb[i].Pagenum;
if (i == 0){//如果是链首弹出
pb[0].Memnum = pb[1].Memnum;
pb[0].Pagenum = pb[1].Pagenum; //空闲链首移动一位
existence[rpoint] = 0; //从访问页框根据先进先出弹出页框,放入空闲链尾
pb[1].Memnum = rpoint;
pb[1].Pagenum = pageframe[rpoint];
}
else{//如果是链尾
existence[rpoint] = 0; //从访问页框根据先进先出弹出页框,放入空闲
pb[1].Memnum = rpoint;
pb[1].Pagenum = pageframe[rpoint];
}
flag = 1;
}
}
if (flag == 0){
existence[pb[0].Memnum] = 1;
pageframe[pb[0].Memnum] = pf_info.serial[vpoint];
pb[0].Memnum = pb[1].Memnum;
pb[0].Pagenum = pb[1].Pagenum; //空闲链首移动一位
existence[rpoint] = 0; //从访问页框根据先进先出弹出页框,放入空闲链尾
pb[1].Memnum = rpoint;
pb[1].Pagenum = pageframe[rpoint];
}
rpoint = (rpoint + 1) % pf_info.total_pf;//页面指针下移
while (existence[rpoint] == 0)
rpoint = (rpoint + 1) % pf_info.total_pf;//页面指针下移
pf_info.diseffect++; // 缺页次数加1
}
}
displayinfoPBA(); // 显示当前状态
算法的分析与比较
最佳置换算法:理想化算法,具有最好性能(对于固定分配页面方式,本法可保证获得最低的缺页率),但实际上却难于实现,故主要用于算法评价参照。
先进先出置换算法:简单直观,但不符合进程实际运行规律,性能较差,故实际应用极少
最近最久未使用置换算法:适用于各种类型的程序,性能较好,但需要较多的硬件支持
改进型Clock置换算法:与简单Clock算法相比,可减少磁盘的I/O操作次数,但淘汰页的选择可能经历多次扫描,故实现算法自身的开销增大
页面缓冲算法PBA:当已修改页面链表达到一定长度如Z个页面时,一起将所有已修改页面写回磁盘,故可显著减少磁盘I/O操作次数
针对分配页框为3,随机访问序列长度为100,工作集包含页面数为3,页面工作集移动率为20,进行各类置换算法测试获得缺页率对比情况如下。
OPT
FIFO
LRU
gClock
PBA:
页面置换算法 | 缺页率 | 算法开销 |
---|---|---|
OPT | 21% | 可以保证较低的页面更新频率。从理论上讲,由于无法预知哪一个页面是未来最长时间内不再被访问的,因而该算法无法实现 |
FIFO | 32% | 需要循环的遍历内存框时间复杂度为O(n) |
LRU | 20% | 在进行遍历内存框页面信息的同时还需要对于页面访问历史进行比较开销比FIFO更大 |
改进clock | 39% | 算法在实现的过程中考虑到了内存页面的读取过程,对于也页面信息设置了修改位状态,内存开销更小 |
页面缓冲PBA | 50% | 算法设计两个缓冲页面框,对于淘汰出的页面会先进入缓冲框,内存置换开销更小 |