5種頁面置換演算法的實現
前幾天做了一個有關頁面替換演算法的題,要求如下:
1
簡介
要求實現多種頁面替換演算法,然後利用隨機產生的引用串測試其效能。
2
頁面替換演算法
我們做如下假設:
• 虛擬記憶體頁面總數為P,標號從0到P−1;
• 引用串RS(reference string)是一個整數序列,整數的取值範圍為0到P−1。RS中的每個元素p表示對頁面p的一次引用;
• 實體記憶體由F幀組成,標號從0到F−1。我們引入一個數組M[F], 陣列元素M[f]中包含數字p,它表示幀f中包含頁面p。頁面替換演算法順次讀取RS中的每個元素。對於RS中的元素值p,演算法搜尋陣列M[F],判斷是否存在某個f,使得M[f]
== p。如果未發現,則表示頁面缺失。這時,演算法必須根據其特定的替換規則,選擇一幀M[i],用頁面p替換其中的內容,即令M[i] = p。
下面討論各種替換演算法所需要的不同資料結構:
• 最佳替換演算法和隨機替換演算法不需要其它的資料結構。 對於最佳替換 演算法,通過搜尋RS即足以確定應被替換的頁面;對於隨機替換演算法,產生一個取值範圍在0和F−1之間的隨機數,該隨機數即可表示應被替換的頁面。
• FIFO需要一個指向最老頁面的指標(陣列索引)。每當該頁面被替換的時候,把該指標加1(模F)即可。
• LRU演算法則需要一個尺寸為F的陣列,該陣列用來實現排隊功能:每次處理一個新的頁面引用時,則把該頁放置在佇列的末尾。這樣, 每當需要淘汰一個頁面時,從隊首取到的即最長時間未被用到的頁面。
• Clock演算法(也叫second−chance演算法)和FIFO演算法一樣,需要一個指標。此外,它還需要一個數組,用來記錄每一幀的使用情況。
3
引用串的生成
實現了頁面替換演算法以後,我們還需要生成引用串,用以測試頁面替換演算法的效能。生成引用串的關鍵在於如何模擬程式的區域性性。
生成不帶區域性性的引用串最為容易, 遺憾的是, 這和絕大多數的程式特性不符。多數程式都顯示出高度的區域性性,也就是說,在一個時間內,一組頁面被反覆引用。
這組被反覆引用的頁面隨著時間的推移,其成員也會發生變化。有時這種變化是劇烈的,有時這種變化則是漸進的。我們把這組頁面的集合稱 為當前工作集。
我們可以為程式的這種行為方式建立如下模型。首先,我們定當前工作集由虛擬記憶體中連續的頁面組成。這當然與實際情況不符,因為程式的程式碼、資料和堆棧通常分處在不同的虛擬記憶體區域。但是,對於研究頁面替換演算法來說,我們關心的只是當前工作集的大小,以及其成員更替的頻繁程度。因此,從這個意義上說,我們的假設是合理的。
我們進一步假設引用在當前工作集內的分佈是均勻的。工作集可用其起始頁面,以及其中包含的頁面數表示。 隨著時間的推移,工作集在虛擬記憶體中不斷移動。有時,這種移動是漸進的,而有時則是劇烈的。為了給漸進移動建立模型,我們做如下假設:
工作集在固定的方向向勻速前進(例如,在處理了m個引用後,把工作集的起始頁面加1)。對於劇烈移動,我們則重新隨機選擇工作集的起始頁面
即可。劇烈移動在整個移動中的比率,我們用t表示。根據以上假設,引用串可用如下演算法產生:
1. 確定虛擬記憶體的尺寸P,工作集的起始位置p,工作集中包含的頁數e,工作集移動率m,以及一個範圍在0和1之間的值t;
2. 生成m個取值範圍在p和p+e間的隨機數,並記錄到引用串中;
3. 生成一個隨機數r,0
≤r
≤1;
4. 如果r<t,則為p生成一個新值,否則p = (p+1) mod P;
5. 如果想繼續加大引用串的長度,請返回第而2步,否則結束。
4
效能評測
測試不同的引用串以及不同的虛擬記憶體尺寸
我把頁面置換演算法封裝起來了,引用串的生成函式放在了main函式的上面,先貼封裝的演算法類:
標頭檔案如下:
#ifndef __ALG__
#define __ALG__
#include <vector>
#include <iostream>
#include <time.h>
using namespace std;
enum Strategy
{
OPT = 0, //最佳置換演算法
RPA, //隨機置換演算法
FIFO, //先進先出演算法
LUR, //最近最久未使用演算法
CLOCK //時鐘演算法
};
class NodeForLUR
{
public:
NodeForLUR() {}
~NodeForLUR() {}
int index;
NodeForLUR* next;
};
class LinkForLUR
{
public:
LinkForLUR() {}
~LinkForLUR() {}
NodeForLUR* head;
NodeForLUR* tail;
};
class Algorithms
{
public:
explicit Algorithms(int memory_size);
~Algorithms();
void setReferenceString(std::vector<int> rs);
int exePageReplace(Strategy strategy);
private:
void resetMemory();
int OPT(int current_serial);
int RPA();
int FIFO();
int LUR();
void updateLinkForLUR(int index);
void deleteLinkForLUR(NodeForLUR* node);
int CLOCK();
//int page_size;
int memory_size;
int memory_used;
int oldest_index;
LinkForLUR link_for_LUR;
int *memory; //模擬實體記憶體
bool *clock;
int placement_count;//置換次數
std::vector<int> reference_string;
};
#endif // !__ALG__
CPP檔案如下:
#include "stdafx.h"
Algorithms::Algorithms(int memory_size)
{
this->memory_size = memory_size;
this->memory_used = 0;
this->memory = new int[memory_size];
this->clock = new bool[memory_size];
this->link_for_LUR.head = nullptr;
this->link_for_LUR.tail = nullptr;
this->oldest_index = 0;
this->placement_count = 0;
}
void Algorithms::setReferenceString(std::vector<int> rs)
{
this->reference_string.clear();
this->reference_string = rs;
}
int Algorithms::exePageReplace(Strategy strategy)
{
//檢測引用串的設定情況
if (reference_string.empty()) {
std::cout << "ERROR:引用串未設定!" << endl;
return -1;
}
//檢測是否初始化記憶體使用
if (0 != memory_used) {
resetMemory();
}
vector<int>::iterator ir = reference_string.begin();
for (; ir < reference_string.end(); ++ir) {
//是否實體記憶體中已存在該頁面
int index;
for (index = 0; index < memory_used; index++) {
if (memory[index] == *ir) {
break;
}
}
if (index != memory_used) {
//記憶體存在
//std::cout << "引用串第" << ir - reference_string.begin() << "位已存在實體記憶體中!" << endl;
if (strategy == Strategy::LUR) {//策略為LUR時,更新最近最久未使用佇列
updateLinkForLUR(index);
}
else if (strategy == Strategy::CLOCK) {
clock[index] = true;
oldest_index = (index + 1) % memory_size;
}
continue;
}
//是否有空閒記憶體
if (memory_used < memory_size) {
memory[memory_used++] = *ir;
//std::cout << "引用串第" << ir - reference_string.begin() << "位已加入空閒實體記憶體中!" << endl;
if (strategy == Strategy::LUR) {//策略為LUR時,增加最近最久未使用佇列節點
if (memory_used == 1) {
link_for_LUR.head = new NodeForLUR();
link_for_LUR.head->index = 0;
link_for_LUR.tail = link_for_LUR.head;
}
else {
auto temp_node = new NodeForLUR();
temp_node->index = memory_used - 1;
temp_node->next = link_for_LUR.head;
link_for_LUR.head = temp_node;
}
}
else if (strategy == Strategy::CLOCK) {
clock[memory_used - 1] = true;
}
continue;
}
//無空閒記憶體,頁面替換
int page;
placement_count++;
switch (strategy)
{
case Strategy::OPT:
{
int current_serial = ir - reference_string.begin();
page = OPT(current_serial);
break;
}
case Strategy::RPA:
page = RPA();
break;
case Strategy::FIFO:
page = FIFO();
oldest_index = (oldest_index+1)%memory_size;
break;
case Strategy::LUR:
page = LUR();
updateLinkForLUR(page);
break;
case Strategy::CLOCK:
page = CLOCK();
clock[page] = true;
break;
default:
_ASSERTE(0);
}
//cout << strategy <<" page :" << page << endl;
memory[page] = *ir;
}
return placement_count;
}
/*
重置記憶體使用
*/
void Algorithms::resetMemory()
{
std::cout << "記憶體使用未重置,正在重置!" << endl;
while (--memory_used >= 0)
{
memory[memory_used] = 0;
}
if (link_for_LUR.head != nullptr) {
deleteLinkForLUR(link_for_LUR.head);
link_for_LUR.head = nullptr;
link_for_LUR.tail = nullptr;
}
memory_used++;
oldest_index = 0;
placement_count = 0;
}
/*
最佳頁面替換演算法,返回替換頁面在實體記憶體中的序號,下同
*/
int Algorithms::OPT(int current_serial)
{
//分析是否有某個頁面後續永不使用
int memory_index = 0, MAX_PAGE = -1, MAX_COUNT = -1;
for (; memory_index < memory_size; memory_index++)
{
int rs_index;
for (rs_index = current_serial; rs_index < reference_string.size(); ++rs_index) {
if (memory[memory_index] == reference_string[rs_index]) {
if (MAX_COUNT < rs_index) {//當前頁面在未來將會被再次引用,記錄引用位置和頁面號
MAX_PAGE = memory_index;
MAX_COUNT = rs_index;
}
break;
}
}
if (rs_index == reference_string.size()) {//找到可替換頁面(存在某頁面永不使用)
return memory_index;
}
}
//無頁面永不使用,則返回最長時間不使用頁面在實體記憶體中的序號
return MAX_PAGE;
}
/*
隨機置換頁面演算法
*/
int Algorithms::RPA()
{
return rand()%memory_size;
}
/*
先進先出演算法
*/
int Algorithms::FIFO()
{
return oldest_index;
}
int Algorithms::LUR()
{
return link_for_LUR.tail->index;
}
void Algorithms::updateLinkForLUR(int index)
{
//移動節點
auto temp_node = link_for_LUR.head;
NodeForLUR* last_node = nullptr;
while (temp_node->index != index)
{//查詢節點
_ASSERT(temp_node != nullptr);
last_node = temp_node;
temp_node = temp_node->next;
}
if (temp_node == link_for_LUR.head) {//如果查詢到的節點是頭結點,則無改動退出。
return;
}
if (temp_node == link_for_LUR.tail && last_node != nullptr) {//是否是尾節點且鏈長大於1
link_for_LUR.tail = last_node;
}
last_node->next = temp_node->next;
temp_node->next = link_for_LUR.head;
link_for_LUR.head = temp_node;
}
/*
用於刪除LUR的連結串列
*/
void Algorithms::deleteLinkForLUR(NodeForLUR* node)
{
if (node->next != nullptr) {
deleteLinkForLUR(node->next);
delete node;
node = nullptr;
}
else {
delete node;
node = nullptr;
}
}
/*
這裡借用了FIFO演算法的oldest_index遊標。反正一個策略執行完也會重置。
*/
int Algorithms::CLOCK()
{
//檢視是否有訪問位為0的
for (int index = oldest_index; index < memory_size; index++)
{
if (!clock[index]) {
oldest_index = (index + 1) % memory_size;
return index;
}
else{
clock[index] = false;
}
}
oldest_index = 0;
return CLOCK();
}
Algorithms::~Algorithms()
{
delete[] memory;
delete[] clock;
memory = nullptr;
if (link_for_LUR.head != nullptr) {
deleteLinkForLUR(link_for_LUR.head);
}
}
main函式所在檔案如下:
#include "stdafx.h"
#define _SCALE_ 100
/*
函式功能:用於生成引用串
引數從左至右依次為:
v:用於填充的引用串
page_size:頁面總數P
set_num:工作集中包含的頁數e
move:工作集移動率m
jitter_rate:抖動率
rs_size:預期生成的引用串長度
*/
void genReferenceString(vector<int>& v, int page_size, int set_num,
int start_position, int move, float jitter_rate, int rs_size) {
int length = 0;
srand((int)time(0));
while (length < rs_size)
{
for (int count = 0; count < move; count++) {
v.push_back((start_position + rand() % set_num)% page_size);
length++;
}
int r = rand() % _SCALE_;
if (r < jitter_rate*_SCALE_) {
cout << "發生抖動,抖動位置為 " << length << endl;
start_position = rand() % page_size;
}
else
{
start_position = (start_position + 1) % page_size;
}
}
}
int main()
{
Algorithms test(10);
vector<int> rs;
genReferenceString(rs, 100, 10, 0, 10, 0.3, 40);
cout << "此次隨機生成的引用串如下所示!" << endl;
for (int index = 0; index < 40; index++) {
cout << "\t" << rs[index] ;
if (index % 5 == 4) {
cout << endl;
}
}
Strategy strategy[] = { Strategy::OPT, Strategy ::RPA, Strategy::FIFO, Strategy::LUR, Strategy::CLOCK};
test.setReferenceString(rs);
for (int index = 0; index < 5; ++index) {
cout << "第" << index << "號策略:" << endl;
cout << "共計交換頁面" << test.exePageReplace(strategy[index]) << "次!" << endl;
}
return 0;
}
stdafx.h檔案裡我放了這麼一句,所以要包含一下:
#include "Algorithms.h"
程式的註釋也很詳細,我就不做過多解釋了。有問題可以私信問我。