1. 程式人生 > >5種頁面置換演算法的實現

5種頁面置換演算法的實現

前幾天做了一個有關頁面替換演算法的題,要求如下:

1 簡介
要求實現多種頁面替換演算法,然後利用隨機產生的引用串測試其效能。
2 頁面替換演算法
   我們做如下假設:
• 虛擬記憶體頁面總數為P,標號從0到P1;
• 引用串RS(reference string)是一個整數序列,整數的取值範圍為0到P1。RS中的每個元素p表示對頁面p的一次引用;
• 實體記憶體由F幀組成,標號從0到F1。我們引入一個數組M[F], 陣列元素M[f]中包含數字p,它表示幀f中包含頁面p。頁面替換演算法順次讀取RS中的每個元素。對於RS中的元素值p,演算法搜尋陣列M[F],判斷是否存在某個f,使得M[f] == p。如果未發現,則表示頁面缺失。這時,演算法必須根據其特定的替換規則,選擇一幀M[i],用頁面p替換其中的內容,即令M[i] = p。
   下面討論各種替換演算法所需要的不同資料結構:
• 最佳替換演算法和隨機替換演算法不需要其它的資料結構。 對於最佳替換 演算法,通過搜尋RS即足以確定應被替換的頁面;對於隨機替換演算法,產生一個取值範圍在0和F1之間的隨機數,該隨機數即可表示應被替換的頁面。
• FIFO需要一個指向最老頁面的指標(陣列索引)。每當該頁面被替換的時候,把該指標加1(模F)即可。
• LRU演算法則需要一個尺寸為F的陣列,該陣列用來實現排隊功能:每次處理一個新的頁面引用時,則把該頁放置在佇列的末尾。這樣, 每當需要淘汰一個頁面時,從隊首取到的即最長時間未被用到的頁面。
• Clock演算法(也叫secondchance演算法)和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"

程式的註釋也很詳細,我就不做過多解釋了。有問題可以私信問我。