作業系統實驗(2)-- 模擬作業系統的頁面置換 LRU OPT FIFO
一、實驗內容
(1)設程式中地址範圍為0 到32767 ,採用隨機數生成256 個指令地址,滿足50%的地址是順序執行,25%向前跳,25% 向後跳。為滿足上述條件,可採取下列方法:設d0=10000,第 n個指令地址為dn,第 n+1 個指令地址為dn+1 ,n的取值範圍為0 到255。每次生成一個 1 到1024範圍內的隨機數a,如果a落在1 到512 範圍內,則dn+1 =dn+1。如果a落在513 到768範圍內,則設定dn+1 為1 到dn範圍內一個隨機數。如果a落在769 到1024範圍內,則設定dn+1 為dn到32767 範圍內一個隨機數。
通過隨機數產生一個指令序列,共320條指令。其地址按下述原則生成:
①50%的指令是順序執行的;
②25%的指令是均勻分佈在前地址部分;
③25%的指令是均勻分佈在後地址部分;
將指令序列變換為頁地址流
1k 的地址大小範圍為1024
頁面大小的取值範圍為1K,2K,4K,8K,16K 。按照頁面大小將指令
地址轉化為頁號。對於相鄰相同的頁號,合併為一個。
分配給程式的記憶體塊數取值範圍為1 塊,2 塊,直到程式的頁面數。
分別採用OPT、FIFO 和LRU演算法對頁號序列進行排程,計算出對應
的缺頁中斷率。
計算並輸出下述各種演算法在不同記憶體容量下的缺頁中斷率
v FIFO先進先出的演算法
v LRU最近最少使用演算法
v LFU最少訪問頁面演算法
二、實驗原理
(一)OPT:最佳置換演算法
該演算法選擇淘汰的頁面將是以後永遠不再使用,或者是在將來最長時間內不再被訪問的頁面,這樣產生的缺頁中斷次數是最少的。
(二)FIFO:先進先出置換演算法
該演算法認為剛被調入的頁面在最近的將來被訪問的可能性是很多的,而在主存中駐留時間最長的頁面在最近的將來被訪問的可能性最小
(三)LRU:最近最少用置換演算法
該演算法總是選擇最近一段時間內最長時間沒有被訪問過的頁面調出
為了記錄自上一次被訪問以來所經過的時間,需要在頁表中增加一個引用位,每次被訪問後將引用位置0,重新計時。
三、演算法實現
整體實現思路:
首先根據上述產生指令的原則產生256條指令,然後通過隨機數隨機產生程式執行所需的
根據輸入的頁面大小(1、2、4、8、16k),按照頁面大小將320條指令地址轉化為頁號,每一條指令地址都對應一個頁面號,這樣也就獲得了320個頁面號組成的序列。
對上述獲得的頁面號序列處理,對於相鄰相同的頁號,合併為一個。 例如:頁面號:.....12 14 14 6 8 9.... 合併後:....12 14 6 8 9 ....
根據輸入的記憶體分配塊數分配相應的記憶體塊
OPT演算法實現
FIFO演算法實現
LRU演算法實現
實現的過程如下:
1、資料結構---頁內容
struct pageInformation
{
int ID; //頁面號
int visit; //被訪問標記
};
2、對於這個實驗所用到的所有處理我整合成一個類,在類中處理所有的內容。--Pager類
①兩個重要的資料變數
private:
int count; //頁面中斷次數
int blockNum; //系統分配的記憶體塊
②相關的操作
(1)void creatInstructions();產生320條指令。其中注意產生隨機數時要注意設定種子,這樣重複執行時才會產生出不同的指令序列
srand((unsigned)time(NULL)); //隨機數種子
(2)void convertToPage(int pageSize);根據頁面大小轉換指令序列成頁面序列
(3)void mergence();對頁面序列進行合併操作
(4)//根據分配物理塊數初始化物理塊
void creatBlock(int n)
{
blockNum = n;
initalPhysicalBlock();
}
void initalPhysicalBlock()
{
block = new pageInformation[blockNum];
BlockClear(blockNum);
}
//置分配的物理塊的初值
void BlockClear(int blockNum)
{
for(int i=0; i<blockNum; i++)
{
block[i].ID = -1;
block[i].visit = 0;
}
}
③下面介紹三個演算法的實現思路(由於程式碼長度,不貼了)
For迴圈合併後的頁面序列,首先查詢在記憶體中是否存在當前要排程的頁面,如果存在就不用進行頁面缺頁中斷處理;否則,進行缺頁中斷。
缺頁中斷分成兩種情況:
1、在記憶體中存在空閒的位置,直接將當前排程的頁面調入記憶體(一般出現在排程的開始時候)
2、記憶體中沒有空閒位置,就需要選擇記憶體的某一個頁面調出,以讓當前的排程頁面進入記憶體。注意這裡淘汰的依據是引用位visit的大者。
//查詢應予置換的頁面
int findReplace()
{
int pos = 0;
for(int i=0; i<blockNum; i++)
if(block[i].visit >= block[pos].visit)
pos = i;//找到應予置換頁面,返回BLOCK中位置
return pos;
}
FIFO:淘汰的是主存中駐留時間最長的頁面,所以每一次排程都需要計算(增加)記憶體中頁面的的駐留時間。那麼在進行缺頁中斷的時候根據這個時間就可以選擇要淘汰的頁面了。
實現的程式碼如下:
for(int j=0; j<blockNum; j++)
block[j].visit++;
LRU:淘汰最近一段時間內最長時間沒有被訪問過的頁面,注意到,每一次排程後都有將引用位復位成0,重新計時。(這個要注意與FIFO的區別)。那麼處理的方法是:在頁面排程過程中,如果訪問的頁面在記憶體中,就不用進行缺頁中斷,但是要注意將訪問引用位置為0;其次在每一次排程過程中也是要增加駐留時間。
實現的程式碼如下:
if(exist != -1)
{
writeToFile<<"----------------------------"<<endl;
writeToFile<<"即將訪問的是頁面"<<page[i].ID<<"----記憶體中已存在該頁"<<endl;;
writeToFile<<"----------------------------"<<endl;
//每一次頁面被訪問後,重新計時
block[exist].visit = 0;//恢復存在的並剛訪問過的BLOCK中頁面visit為0
}
for(int j=0; j<blockNum; j++)
block[j].visit++;
OPT:淘汰的頁面將是以後永遠不再使用,或者是在將來最長時間內不再被訪問的頁面,那麼就要對後續的頁面進行預讀,並將他們的訪問引用位置為最早遇到的頁面號,如果在後續的頁面中沒有出現,那麼他的訪問引用位就置為最大,就是肯定要被淘汰的。
實現的程式碼如下:(下面的程式碼預讀所有的後續頁面,同時也可以進行設定相關的預讀頁面數,修改下面程式碼中的pageNum就可以了。)
for(int k=0; k<blockNum; k++)
for(int j=i; j<pageNum; j++) //修改這裡的j<pageNum 就可以改變預讀後續頁面的頁數
{
if(block[k].ID != page[j].ID)
{
block[k].visit = 1000;
}
else
{
block[k].visit = j;
break;
}
}
④輸出相關資訊
在程式排程過程中會輸出相關的詳細排程資訊,由於行數多,所有以輸出到檔案中。
⑤主介面處理
可以重複選擇排程演算法進行排程,同時頁面大小和系統分配的頁面塊數也是通過使用者輸入的方式。
四、程式介面
在程式執行結束後就會在當前目錄下生成對應的詳細排程資訊檔案。
輸出檔案的排程資訊如下
下面附上全部的程式碼:
//已經完成的頁面排程演算法
#include <iostream>
#include <stdlib.h>
#include <fstream>
#include <ctime>
using namespace std;
struct pageInformation
{
int ID; //頁面號
int visit; //被訪問標記
};
pageInformation * block; //物理塊
pageInformation * page; //合併後的頁面號串
pageInformation * oriPage; //原生的320條指令所對應的320頁
int instructions[320]; //原生的320條指令
int pageNum; //合併後的頁面數
class Pager
{
private:
int count; //頁面中斷次數
int blockNum; //系統分配的記憶體塊
public:
//初始化生成320條指令
void creatInstructions()
{
//生成256 個指令地址 Instruction address 地址範圍為0 到32767
int insAdress[256];
insAdress[0] = 10000;
srand((unsigned)time(NULL)); //隨機數種子
for(int i=1; i<256; i++)
{
int random = rand()%1024+1;
if(random>=1 && random<=512)
{
insAdress[i] = insAdress[i-1]+1;
}
else if(random>=513 && random<=768)
{
int random2 = rand()%insAdress[i-1]+1;
insAdress[i] = random2;
}
else if(random>=769 && random<=1024)
{
int random3 = rand()%(32767-insAdress[i-1])+insAdress[i-1];
insAdress[i] = random3;
}
}
//生成320條指令
for(int j=0; j<320; j++)
{
int r = rand()%256;
instructions[j] = insAdress[r];
}
}
//將指令轉換成頁號
void convertToPage(int pageSize)
{
oriPage = new pageInformation[320]; //一條指令對於一頁
int num = pageSize*1024; //每一頁存放的指令條數
//對第一條指令做特殊處理
oriPage[0].ID = 0;
for(int i=1; i<320; i++)
{
oriPage[i].visit = 0;
int a = instructions[i]/num;
int b = instructions[i]%num;
if(b)
oriPage[i].ID = a;
else
oriPage[i].ID = a+1;
}
}
//對於相鄰相同的頁號,合併為一個。
void mergence()
{
pageNum = 0;
page = new pageInformation[320];
//解決思路:依次掃描oriPage陣列,和page中的最後一個數組比較,如果不同就寫入page,相同就不用寫入。
page[0] = oriPage[0];
for(int i=1; i<320; i++)
{
//如果不同就寫入mergePage
if(page[pageNum].ID != oriPage[i].ID)
{
pageNum++;
page[pageNum] = oriPage[i];
}
}
}
//初始化物理塊
void creatBlock(int n)
{
blockNum = n;
initalPhysicalBlock();
}
void initalPhysicalBlock()
{
block = new pageInformation[blockNum];
BlockClear(blockNum);
}
//置分配的物理塊的初值
void BlockClear(int blockNum)
{
for(int i=0; i<blockNum; i++)
{
block[i].ID = -1;
block[i].visit = 0;
}
}
//查詢是否有空閒記憶體塊
int findSpace()
{
for(int i=0; i<blockNum; i++)
if(block[i].ID == -1)
return i;//找到空閒記憶體,返回BLOCK中位置
return -1;
}
//查詢記憶體中是否有該頁面
int findExist(int curpage)
{
for(int i=0; i<blockNum; i++)
if(block[i].ID == page[curpage].ID)
return i;//找到記憶體中有該頁面,返回BLOCK中位置
return -1;
}
//查詢應予置換的頁面
int findReplace()
{
int pos = 0;
for(int i=0; i<blockNum; i++)
if(block[i].visit >= block[pos].visit)
pos = i;//找到應予置換頁面,返回BLOCK中位置
return pos;
}
//顯示
void display(ofstream &fileStream)
{
fileStream<<"----------------------------"<<endl;
for(int i=0; i<blockNum; i++)
if(block[i].ID != -1)
fileStream<<block[i].ID<<" ";
fileStream<<endl;
fileStream<<"----------------------------"<<endl;
}
//FIFO演算法
void FIFO()
{
ofstream writeToFile("FIFO-Manager.txt");
count=0;
int exist,space,position ;
for(int i=0; i<pageNum; i++)
{
//查詢記憶體中是否存在該頁
exist = findExist(i);
if(exist != -1)
{
writeToFile<<"----------------------------"<<endl;
writeToFile<<"即將訪問的是頁面"<<page[i].ID<<"----記憶體中已存在該頁"<<endl;
writeToFile<<"----------------------------"<<endl;
}
//記憶體塊中不存在,進行缺頁中斷的排程
else
{
count++;
space = findSpace();
//在記憶體塊中找到空閒的位置,這個時候也是要中斷的
if(space != -1)
{
block[space] = page[i];
display(writeToFile);
}
else
{
position = findReplace();
writeToFile<<"----------------------------"<<endl;
writeToFile<<"即將訪問的是頁面"<<page[i].ID<<"將被置換出的是頁面"<<block[position].ID<<endl;
writeToFile<<"----------------------------"<<endl;
block[position] = page[i];
display(writeToFile);
}
}
//計算頁面在記憶體塊中駐留的時間,每一次都+1,如果越大,說明駐留時間最長,就淘汰他
for(int j=0; j<blockNum; j++)
block[j].visit++;//BLOCK中所有頁面visit++
}
writeToFile.close();
cout<<"排程過程請看檔案 FIFO-Manager.txt"<<endl;
cout<<"缺頁次數:"<<count<<endl;
cout<<"FIFO演算法的缺頁率是:"<<(float)count/pageNum<<endl;
}
//LRU演算法
void LRU()
{
ofstream writeToFile("LRU-Manager.txt");
int exist,space,position;
count=0;
for(int i=0; i<pageNum; i++)
{
exist = findExist(i);
if(exist != -1)
{
writeToFile<<"----------------------------"<<endl;
writeToFile<<"即將訪問的是頁面"<<page[i].ID<<"----記憶體中已存在該頁"<<endl;;
writeToFile<<"----------------------------"<<endl;
//每一次頁面被訪問後,重新計時
block[exist].visit = 0;//恢復存在的並剛訪問過的BLOCK中頁面visit為0
}
else
{
count++;
space = findSpace();
if(space != -1)
{
block[space] = page[i];
display(writeToFile);
}
else
{
position = findReplace();
writeToFile<<"----------------------------"<<endl;
writeToFile<<"即將訪問的是頁面"<<page[i].ID<<"將被置換出的是頁面"<<block[position].ID<<endl;
writeToFile<<"----------------------------"<<endl;
block[position] = page[i];
display(writeToFile);
}
}
//計時
for(int j=0; j<blockNum; j++)
{
block[j].visit++;
}
}
writeToFile.close();
cout<<"排程過程請看檔案 LRU-Manager.txt"<<endl;
cout<<"缺頁次數:"<<count<<endl;
cout<<"FIFO演算法的缺頁率是:"<<(float)count/pageNum<<endl;
}
//OPT演算法
void OPT()
{
ofstream writeToFile("OPT-Manager.txt");
int exist,space,position ;
count=0;
for(int i=0; i<pageNum; i++)
{
exist = findExist(i);
//頁已經在記憶體塊中
if(exist != -1)
{
writeToFile<<"----------------------------"<<endl;
writeToFile<<"即將訪問的是頁面"<<page[i].ID<<"----記憶體中已存在該頁"<<endl;
writeToFile<<"----------------------------"<<endl;
}
else
{
count++;
space = findSpace();
if(space != -1)
{
block[space] = page[i];
display(writeToFile);
}
else
{
for(int k=0; k<blockNum; k++)
for(int j=i; j<pageNum; j++) //修改這裡的j<pageNum 就可以改變預讀後續頁面的頁數
{
if(block[k].ID != page[j].ID)
{
block[k].visit = 1000;
}
else
{
block[k].visit = j;
break;
}
}
position = findReplace();
writeToFile<<"----------------------------"<<endl;
writeToFile<<"即將訪問的是頁面"<<page[i].ID<<"將被置換出的是頁面"<<block[position].ID<<endl;
writeToFile<<"----------------------------"<<endl;
block[position] = page[i];
display(writeToFile);
}
}
}
writeToFile.close();
cout<<"排程過程請看檔案 OPT-Manager.txt"<<endl;
cout<<"缺頁次數:"<<count<<endl;
cout<<"FIFO演算法的缺頁率是:"<<(float)count/pageNum<<endl;
}
};
int main()
{
while(1)
{
int selection;
cout<<"----請輸入排程演算法:1:OPT 2:FIFO 3:LRU---->>";
cin>>selection;
int pSize,num;
cout<<"----請輸入頁面大小:(1、2、4、8、16K):";
cin>>pSize;
cout<<"----請輸入系統分配的記憶體塊數:";
cin>>num;
Pager test;
test.creatInstructions();
test.convertToPage(pSize);
test.mergence();
test.creatBlock(num);
if(selection == 1)
{
test.OPT();
}
else if(selection == 2)
{
test.FIFO();
}
else if(selection == 3)
{
test.LRU();
}
delete page;
delete oriPage;
delete block;
cout<<endl;
}
return 0;
}