实验目的
- 理解内存页面调度的机理;
- 掌握几种理论调度算法实现;
- 通过实验比较各种调度算法的优劣;
- 通过实验了解HASH表数据结构的使用;
准备知识
C++、指针和结构体(类)
哈希表查找方式
操作系统内存交换的知识
可能用到的几个函数
1 2 3
| int getpid () void srand (int a) int rand ()
|
实验内容
对比以下几种页面置换算法的命中率
- 先进先出的算法FIFO(First In First Out)
- 最近最少使用的算法LRU(Least Recently Used)
- 最近未使用算法NUR(Never Recently Used)
- 最佳置换算法OPT(Optimal Replacement)
页面置换算法介绍
FIFO页面置换算法
原理简述
- 在分配内存页面数(AP)小于进程页面数(PP)时,最先的AP个页面放入内存;
- 需要处理新页面时,将原来在内存中的AP个页面中最先进入的调出,放入新的页面;
- 算法特点:使用内存页面构成一个队列;
图表描述
算法实现
- 要得到命中率,必然要有一个常量
total_instruction
记录页面总共使用次数;此外需要一个变量记录总共换入页面的次数(需要换出页面,总是因为没有命中而产生的)diseffect,可利用公式得到命中率(公式如下)。
初始化,设置两个数组page[ap]
和pagecontrol[pp]
分别表示进程页面数和内存分配的页面数,并产生一个的随机数序列main[total_instruction]
(当然这个序列由page[]
的下标随机构成),表示待处理的进程页面顺序,diseffect置0;
看main[]
中是否有下一个元素,有就由main[]
中获取该页面下标,并转到步骤3;若没有,就转到步骤7;
如果该page页已在内存中,就转到步骤2;否则就到步骤4,同时未命中的diseffect加1;
观察pagecontrol是否占满,如果占满需将使用队列(步骤六中建立的)中最先进入的(就是队列第一个单元)pagecontrol单元“清干净”,同时将对应的page[]
单元置为“不在内存中”。
将该page[]
与pagecontrol[]
建立关系(可以改变pagecontrol[]
的标示位,也可以采用指针连接。总之,至少要使对应的pagecontrol单元包含两个信息:一是它被使用了,二是哪个page[]
单元使用的;page[]
单元包含两个信息:对应的pagecontrol单元号、本page[]
单元已在内存中);
将用到的pagecontrol置入使用队列(这里的队列当然是一种先进先出的数据结构),返回步骤2;
显示命中率(命中率公式如下) ,算法完成。
$$
hit_-rate=1-\frac{diseffect}{total_-instruction}\times100%
$$
LRU页面置换算法
原理简述
- 在分配内存页面数(AP)小于进程页面数(PP)时,最先的AP个页面放入内存;
- 当需要调入页面进入内,而当前分配的内存页面不空闲时,选择其中最长时间没有用的那个页面调出,以控制内存来放置新调入的页面;
- 算法特点:每个页面都有属性表示有多长时间未被CPU使用的信息;
图表描述
假设某个进程与内存可控制页面数不变:AP=3
,PP=5
;
处理机调用它们的顺序不变:1、4、2、5、3、3、2、4、2、5
使用LRU算法时,这3个页面的内存使用情况如下图所示
共换入页面7次,diseffect=7
;
算法实现
- 和FIFO算法相同,只有先得到diseffect才能获得最终“命中率”;
- 初始化,主要是进程页面
page[]
和分配的内存页面pagecontrol[]
,同时产生随机序列main[]
,diseffect置0;
- 看序列
main[]
是否有下一个元素,有就由main[]
中获取该页面下标,并转到步骤3;若没有,就转到步骤6;
- 如果该page页已在内存中,便改变页面属性,使它保留“最近使用”的信息,转到步骤2;否则就到步骤4,同时未命中的diseffect加1;
- 判断是否有空闲的内存页面,如果有,就返回页指针,转到步骤5;否则在内存页面中找出最长时间没有使用到的页面,将其“清干净”,并返回该页面指针;
- 在需要处理的
page[]
与步骤4中得到的pagecontrol[]
建立关系,同时让对应的page[]
单元保存“最新使用”的信息,返回步骤2;
- 如果序列处理完成,就输出命中率(命中率公式同上),算法结束。
NUR页面置换算法
原理简述
- 所谓的“最近未使用”首先要对“近”做一个界定,比如
CLEAR_PERIOD=50
,便是在CPU最近执行的50次进程页面处理工作中,找出这50次都没有处理的页面;
- 如果这样的页面只有一个,就将其换出,放入需处理的新页面;
- 如果有不止一个这样的页面,在这些页面中任取出一个换出,放入需处理的页面;
- 如果没有这样的页面,则随意换出一个页面(页码可以最大也可以最小);
- 算法特点:有一个循环周期,每达到这个周期,所有页面存放是否被CPU处理信息的属性均被置于初始态;
图表描述
假设某个进程与内存可控制页面数不变:AP=3,PP=5
;
处理机调用它们的顺序不变:1、4、2、5、3、3、2、4、2、5
;
使用NUR算法时,这3个页面的内存使用情况如下图所示;
算法实现
需要对数组设置“访问位”标识;
- 初始化,设置两个数组
page[ap]
和pagecontrol[pp]
分别表示进程页面数和内存分配的页面数,并产生一个的随机数序列main[total_instruction]
(当然这个序列由page[]
的下标随机构成),表示待处理的进程页面顺序,diseffect置0,设定循环周期CLEAR_PERIOD;
- 看
main[]
中是否有下一个元素,有就从main[]
中获得一个CPU将处理页面的序号;没有,就转到步骤8;
- 如果待处理的页面已在内存中,就转到步骤2;否则diseffect加1,并转到步骤4;
- 看是否有空闲的内存页面,如果有,返回空闲页面指针,转到步骤5;否则,在所有没有被访问且位于内存中的页面中按任意规则(或者取最近的一个页面;或者取下标最小的页面,等等)取出一个,返回清空后的内存页面指针;
- 在待处理进程页面与内存页面之间建立联系,并标注该页面被访问;
- 如果CPU已处理了CLEAR_PERIOD个页面,就将所有页面均设为“未访问”;
- 返回步骤2;
- 如果CPU所有处理工作完成,就返回命中率的结果,算法结束。
OPT页面置换算法
原理简述
在分配的内存页面占满的情况下,最佳置换算法是一种理想状况下的算法,他要求先遍历所有的CPU待处理的进程页面序列(实际上,由于待处理的页面有取决于先前处理的页面,所有很多情况下不可能得到完整的待处理页面序列。在这个层面上,才说该算法足理想的。),在这些页面中,如果有已经在内存当中,而不再处理的,就将其换出:如果页面在内存中,并待处理,就取从当前位置算起,最后处理到的页面,将其换出。比如待处理的页面序列号为:
已经处理了5个页面,那么页面5是第一个待处理的页面:2是第二个;1是第四个;4是第五个;3是第六个。那么页面3就是针对当前位置而言,最后处理到的页面。
图表描述
假设某个进程与内存可控制页面数不变:AP=3,PP=5
;
处理机调用它们的顺序不变:1、4、2、5、3、3、2、4、2、5
使用OPT算法时,这3个页面的内存使用情况如下图所示;
一共发生了6次页面交换,diseffect=6
;
算法实现
- 初始化,设置两个数组
page[ap]
和pagecontrol[pp]
分别表示进程页面数和内存分配的页面数,并产生一个的随机数序列main[total_instruction]
(当然这个序列由page[]的下标随机构成),表示待处理的进程页面顺序,diseffect置0,然后扫描整个页面访问序列,对vDistance[TOTAL_VP]
数组进行赋值,表示该页面将在第几步被处理;
- 看
main[]
中是否有下一个元素,有就从main[]
中获取一个CPU待处理的页面号;没有,就转到步骤6;
- 如果该page页已在内存中,就转到步骤2;否则就到步骤4,同时未命中的diseffect加1;
- 看是否有空闲的内存页面,如果有,就直接返回该页面指针;如果没有,遍历所有未处理的进程页面序列,如果有位于内存中的页面,而以后CPU不再处理,首先将其换出,返回页面指针;如果没有这样的页面,找出CPU最晚处理到的页面,将其换出,返回该内存页面指针;
- 在内存页面和待处理的进程页面之间建立联系,返回步骤2;
- 输出命中率,算法结束。(命中率在扫描时完成计算)
数据结构
数据结构的详细分析在注释中已经标注;
CPage类
1 2 3 4 5 6 7
| class CPage{ public: int m_nPageNumber; int m_nPageFaceNumber; int m_nCounter; int m_nTime; };
|
CPageControl类
1 2 3 4 5 6
| class CPageControl{ public: int m_nPageNumber; int m_nPageFaceNumber; class CPageControl * m_pNext; };
|
CMemory类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class CMemory{ public: CMemory(); void initialize(const int nTotal_pf); void FIFO(const int nTotal_pf); void LRU(const int nTotal_pf); void NUR(const int nTotal_pf); void OPT(const int nTotal_pf);
private: vector<CPage> _vDiscPages; vector<CPageControl> _vMemoryPages; CPageControl *_pFreepf_head; CPageControl *_pBusypf_head; CPageControl *_pBusypf_tail; vector<int> _vMain; vector<int> _vPage; vector<int> _vOffset; int _nDiseffect; };
|
CMemory类的构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| CMemory::CMemory(): _vDiscPages(TOTAL_VP), _vMemoryPages(TOTAL_VP), _vMain(TOTAL_INSTRUCTION), _vPage(TOTAL_INSTRUCTION), _vOffset(TOTAL_INSTRUCTION) { int S,i,nRand;
srand(getpid()*10);
nRand=rand()%32767; S=(float)319*nRand/32767+1; for(i=0;i<TOTAL_INSTRUCTION;i+=4) { _vMain[i]=S; _vMain[i+1]=_vMain[i]+1;
nRand=rand()%32767; _vMain[i+2]=(float)_vMain[i]*nRand/32767; _vMain[i+3]=_vMain[i+2]+1; nRand=rand()%32767; S=(float)nRand*(318-_vMain[i+2])/32767+_vMain[i+2]+2; }
for(i=0;i<TOTAL_INSTRUCTION;i++) { _vPage[i]=_vMain[i]/10; _vOffset[i]=_vMain[i]%10; _vPage[i]%=32; } }
|
类成员函数initialize(const int nTotal_pf)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| void CMemory::initialize(const int nTotal_pf) { int ix; _nDiseffect=0; for(ix=0;ix<_vDiscPages.size();ix++) { _vDiscPages[ix].m_nPageNumber=ix; _vDiscPages[ix].m_nPageFaceNumber=INVALID; _vDiscPages[ix].m_nCounter=0; _vDiscPages[ix].m_nTime=-1; } for(ix=1;ix<nTotal_pf;ix++) { _vMemoryPages[ix-1].m_pNext=&_vMemoryPages[ix]; _vMemoryPages[ix-1].m_nPageFaceNumber=ix-1; } _vMemoryPages[nTotal_pf-1].m_pNext=NULL; _vMemoryPages[nTotal_pf-1].m_nPageFaceNumber=nTotal_pf-1; _pFreepf_head=&_vMemoryPages[0]; }
|
类成员函数FIFO(const int nTotal_pf)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| void CMemory::FIFO(const int nTotal_pf) { int i; CPageControl *p; initialize(nTotal_pf); _pBusypf_head=_pBusypf_tail=NULL; for(i=0;i<TOTAL_INSTRUCTION;i++) { if(_vDiscPages[_vPage[i]].m_nPageFaceNumber==INVALID) { _nDiseffect+=1; if(_pFreepf_head==NULL) { p=_pBusypf_head->m_pNext; _vDiscPages[_pBusypf_head->m_nPageNumber]. m_nPageFaceNumber=INVALID; _pFreepf_head=_pBusypf_head; _pFreepf_head->m_pNext=NULL; _pBusypf_head=p; } p=_pFreepf_head->m_pNext; _pFreepf_head->m_pNext=NULL; _pFreepf_head->m_nPageNumber=_vPage[i]; _vDiscPages[_vPage[i]].m_nPageFaceNumber=_pFreepf_head->m_nPageFaceNumber; if(_pBusypf_tail==NULL) { _pBusypf_head=_pBusypf_tail=_pFreepf_head; } else{ _pBusypf_tail->m_pNext=_pFreepf_head; _pBusypf_tail=_pFreepf_head; } _pFreepf_head=p; } } cout<<"FIFO: "<<fixed<<setprecision(6)<<1-(float)_nDiseffect/320<<"\t"; }
|
类成员函数LRU(const int nTotal_pf)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| void CMemory::LRU(const int nTotal_pf) { int i,j,nMin,minj,nPresentTime(0); initialize(nTotal_pf); for(i=0;i<TOTAL_INSTRUCTION;i++) { if(_vDiscPages[_vPage[i]].m_nPageFaceNumber==INVALID) { _nDiseffect++; if(_pFreepf_head==NULL) { nMin=32767;
for(j=0;j<TOTAL_VP;j++) { if((nMin>_vDiscPages[j].m_nTime)&& (_vDiscPages[j].m_nPageFaceNumber!=INVALID)) { nMin=_vDiscPages[j].m_nTime; minj=j; } } _pFreepf_head=&_vMemoryPages[_vDiscPages[minj]. m_nPageFaceNumber]; _vDiscPages[minj].m_nPageFaceNumber=INVALID; _vDiscPages[minj].m_nTime=-1; _pFreepf_head->m_pNext=NULL; } _vDiscPages[_vPage[i]].m_nPageFaceNumber= _pFreepf_head->m_nPageFaceNumber; _vDiscPages[_vPage[i]].m_nTime=nPresentTime; _pFreepf_head=_pFreepf_head->m_pNext; } else{ _vDiscPages[_vPage[i]].m_nTime=nPresentTime; } nPresentTime++; } cout<<"LRU: "<<fixed<<setprecision(6)<<1-(float)_nDiseffect/320<<"\t"; }
|
类成员函数NUR(const int nTotal_pf)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| void CMemory::NUR(const int nTotal_pf){ int i,j,nDiscPage,nOld_DiscPage; bool bCont_flag; initialize(nTotal_pf); nDiscPage=0; for(i=0;i<TOTAL_INSTRUCTION;i++) { if(_vDiscPages[_vPage[i]].m_nPageFaceNumber==INVALID) { _nDiseffect++; if(_pFreepf_head==NULL) { bCont_flag=true; nOld_DiscPage=nDiscPage; while(bCont_flag) { if(_vDiscPages[nDiscPage].m_nCounter==0&& _vDiscPages[nDiscPage].m_nPageFaceNumber!=INVALID) { bCont_flag=false; } else{ nDiscPage++; if(nDiscPage==TOTAL_VP) { nDiscPage=0; } if(nDiscPage==nOld_DiscPage) { for(j=0;j<TOTAL_VP;j++) { _vDiscPages[j].m_nCounter=0; } } } } _pFreepf_head=&_vMemoryPages[_vDiscPages[nDiscPage]. m_nPageFaceNumber]; _vDiscPages[nDiscPage].m_nPageFaceNumber=INVALID; _pFreepf_head->m_pNext=NULL; } _vDiscPages[_vPage[i]].m_nPageFaceNumber= _pFreepf_head->m_nPageFaceNumber; _pFreepf_head=_pFreepf_head->m_pNext; } else{ _vDiscPages[_vPage[i]].m_nCounter=1; } if(i%CLEAR_PERIOD==0) { for(j=0;j<TOTAL_VP;j++) { _vDiscPages[j].m_nCounter=0; } } } cout.setf(ios::fixed); cout<<"NUR:"<<fixed<<setprecision(6)<<1-(float)_nDiseffect/320<<"\t"; }
|
类成员函数OPT(const int nTotal_pf)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| void CMemory::OPT(const int nTotal_pf) { int i,j,max,maxpage,nDistance,vDistance[TOTAL_VP]; initialize(nTotal_pf); for(i=0;i<TOTAL_INSTRUCTION;i++) { if(_vDiscPages[_vPage[i]].m_nPageFaceNumber==INVALID) { _nDiseffect++; if(_pFreepf_head==NULL) { for(j=0;j<TOTAL_VP;j++) { if(_vDiscPages[j].m_nPageFaceNumber!=INVALID) { vDistance[j]=32767; } else{ vDistance[j]=0; } } nDistance=1; for(j=i+1;j<TOTAL_INSTRUCTION;j++) { if((_vDiscPages[_vPage[j]].m_nPageFaceNumber!=INVALID)&& (vDistance[_vPage[j]]==32767)) { vDistance[_vPage[j]]=nDistance; } nDistance++; } max =-1; for(j=0;j<TOTAL_VP;j++) { if(max<vDistance[j]) { max=vDistance[j]; maxpage=j; } } _pFreepf_head=&_vMemoryPages[_vDiscPages[maxpage]. m_nPageFaceNumber]; _pFreepf_head->m_pNext=NULL; _vDiscPages[maxpage].m_nPageFaceNumber=INVALID; } _vDiscPages[_vPage[i]].m_nPageFaceNumber= _pFreepf_head->m_nPageFaceNumber; _pFreepf_head=_pFreepf_head->m_pNext; } } cout.setf(ios::fixed); cout<<"OPT:"<<fixed<<setprecision(6)<<1-(float)_nDiseffect/320<<"\t"; }
|
main()函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <iostream> #include <string> #include <vector> #include <cstdlib> #include <cstdio> #include <unistd.h> #include <iomanip> #define INVALID -1 using namespace std; const int TOTAL_INSTRUCTION(320); const int TOTAL_VP(32); const int CLEAR_PERIOD(50); #include "Page.h" #include "PageControl.h" #include "Memory.h" int main(){ int i; CMemory a; for(i=4;i<=32;i++) { a.FIFO(i); a.LRU(i); a.NUR(i); a.OPT(i); cout<<"\n"; } return 0; }
|
实验结果及分析
在RED Hat Linux中通过输入以下命令执行程序;
1 2
| g++ -o main mian.cpp ./main
|
得到程序结果如下;
结果分析;
- 四种页面置换算法OPT算法的命中率最高,其它三种基本相同;
- 随着分配内存页面逐渐增加,每种页面置换算法的命中率也会相应增加,并且趋于一个稳定值90%;
- 如果次数非常大,这四种置换算法的命中率基本一致;
实验难点与收获
难点
- 对OPT置换算法和对NUR置换算法的理解;
- C++编程中类的使用规范等;
收获
- 学习了四种置换算法的基本原理和C++语言实现;
- 通过实验结果,对比得到了四种算法的优劣;
- 了解了内存页面调度的机理;
- 进一步熟悉了C++中类的调用规范;
实验思考
实验通过对四种页面置换算法进行横向对比和纵向对比,展示了四种算法的优缺点;但是实验结果对实验的原理体现的不太明显,可以在实验结果中增加每种算法的执行过程,让算法原理理解。