實驗目的
通過模擬實現請求頁式存儲管理的幾種基本頁面置換算法,瞭解虛擬存儲技術的特點,掌握虛擬存儲請求頁式存儲管理中幾種基本頁面置換算法的基本思想和實現過程,並比較它們的效率。
實驗內容
設計一個虛擬存儲區和內存工作區,並使用下述算法計算訪問命中率。
1、最佳淘汰算法(OPT)
2、先進先出的算法(FIFO)
3、最近最久未使用算法(LRU)
4、最不經常使用算法(LFU)
5、最近未使用算法(NUR)
命中率=1-頁面失效次數/頁地址流長度
實驗準備
本實驗的程序設計基本上按照實驗內容進行。即首先用srand( )和rand( )函數定義和產生指令序列,然後將指令序列變換成相應的頁地址流,並針對不同的算法計算出相應的命中率。
(1)通過隨機數產生一個指令序列,共320條指令。指令的地址按下述原則生成:
A:50%的指令是順序執行的
B:25%的指令是均勻分佈在前地址部分
C:25%的指令是均勻分佈在後地址部分
具體的實施方法是:
A:在[0,319]的指令地址之間隨機選取一起點m
B:順序執行一條指令,即執行地址爲m+1的指令
C:在前地址[0,m+1]中隨機選取一條指令並執行,該指令的地址爲m’
D:順序執行一條指令,其地址爲m’+1
E:在後地址[m’+2,319]中隨機選取一條指令並執行
F:重複步驟A-E,直到320次指令
(2)將指令序列變換爲頁地址流
設:頁面大小爲1K;
用戶內存容量4頁到32頁;
用戶虛存容量爲32K。
在用戶虛存中,按每K存放10條指令排列虛存地址,即320條指令在虛存中的存放方式爲:
第 0 條-第 9 條指令爲第0頁(對應虛存地址爲[0,9])
第10條-第19條指令爲第1頁(對應虛存地址爲[10,19])
………………………………
第310條-第319條指令爲第31頁(對應虛存地址爲[310,319])
按以上方式,用戶指令可組成32頁。
實驗指導
一、虛擬存儲系統
UNIX中,爲了提高內存利用率,提供了內外存進程對換機制;內存空間的分配和回收均以頁爲單位進行;一個進程只需將其一部分(段或頁)調入內存便可運行;還支持請求調頁的存儲管理方式。
當進程在運行中需要訪問某部分程序和數據時,發現其所在頁面不在內存,就立即提出請求(向CPU發出缺中斷),由系統將其所需頁面調入內存。這種頁面調入方式叫請求調頁。
爲實現請求調頁,核心配置了四種數據結構:頁表、頁框號、訪問位、修改位、有效位、保護位等。
二、頁面置換算法
當CPU接收到缺頁中斷信號,中斷處理程序先保存現場,分析中斷原因,轉入缺頁中斷處理程序。該程序通過查找頁表,得到該頁所在外存的物理塊號。如果此時內存未滿,能容納新頁,則啓動磁盤I/O將所缺之頁調入內存,然後修改頁表。如果內存已滿,則須按某種置換算法從內存中選出一頁準備換出,是否重新寫盤由頁表的修改位決定,然後將缺頁調入,修改頁表。利用修改後的頁表,去形成所要訪問數據的物理地址,再去訪問內存數據。整個頁面的調入過程對用戶是透明的。
常用的頁面置換算法有
1、最佳置換算法(Optimal)
2、先進先出法(Fisrt In First Out)
3、最近最久未使用(Least Recently Used)
4、最不經常使用法(Least Frequently Used)
5、最近未使用法(No Used Recently)
代碼
#include <iostream>
#include <ctime>
#include <random>
using namespace std;
#define TRUE 1
#define FALSE 0
#define INVALID -1
#define NULL 0
#define total_instruction 320 //指令數量
#define total_vp 32 //頁表數量
#define clear_period 50 //清0週期
struct pl_type { /*頁表結構*/
int pn, pfn, counter, time;
};
pl_type pl[total_vp]; //頁表數組
struct pfc_type { /*內存表結構*/
int pn, pfn;
pfc_type *next;
};
pfc_type pfc[total_vp], *freepf_head, *busypf_head, *busypf_tail;
int diseffect; // 未命中次數
int a[total_instruction]; // 存儲320條指令
int page[total_instruction]; // 每條指令對應的頁表號
int offset[total_instruction];
int initialize(int);
int FIFO(int);
int LRU(int);
int NUR(int);
int LFU(int);
int OPT(int);
int main(){
int s;
srand(unsigned(time(0)));
for (int i = 0; i < total_instruction; i += 4) { /*產生指令隊列*/
s = rand() % 320;
a[i] = s + 1; //順序執行一條指令
a[i + 1] = rand() % (a[i] + 1); //執行前地址指令m'
a[i + 2] = a[i + 1] + 1; //順序執行一條指令
a[i + 3] = rand() % (319 - a[i + 2]) + (a[i + 2] + 1); // 執行後地址指令
if (a[i] > 319 || a[i + 1] > 319 || a[i + 2] > 319 || a[i+3] > 319) {
i -= 4;
}
}
for (int i = 0; i < total_instruction; i++) { /*將指令序列變換成頁表地址流*/
page[i] = a[i] / 10;
offset[i] = a[i] % 10;
}
for (int i = 4; i <= 32; i++) { /*用戶內存工作區從4個頁面到32個頁面*/
printf("---%2d page frames---\n", i);
FIFO(i);
LRU(i);
NUR(i);
LFU(i);
OPT(i);
}
return 0;
}
int initialize(int total_pf) { /*初始化相關數據結構*/
diseffect = 0;
// 初始化頁表
for (int i = 0; i<total_vp; i++){
pl[i].pn = i;
pl[i].pfn = INVALID; // 初始,該頁不在內存中
pl[i].counter = 0; // 訪問次數或作爲引用位
pl[i].time = -1; // 時間
}
// 初始內存表,建立pfc[i-1]和pfc[i]之間的鏈接
for (int i = 0; i < total_pf - 1; i++){
pfc[i].next = &pfc[i + 1];
pfc[i].pfn = i;
}
pfc[total_pf - 1].next = NULL;
pfc[total_pf - 1].pfn = total_pf - 1;
freepf_head = &pfc[0]; //內存空頁面隊列的頭指針爲pfc[0]
return 0;
}
int FIFO(int total_pf) { /*先進先出算法*/
pfc_type *p;
initialize(total_pf); //初始化相關頁面控制用數據結構
busypf_head = busypf_tail = NULL; //內存頁的隊列頭,隊列尾指針接
for (int i = 0; i<total_instruction; i++){
if (pl[page[i]].pfn == INVALID) { //頁表項不在內存中
diseffect += 1; //失效次數
if (freepf_head == NULL) { //內存無空閒頁面
p = busypf_head->next;
pl[busypf_head->pn].pfn = INVALID;
freepf_head = busypf_head; //釋放忙頁面隊列的第一個頁面
freepf_head->next = NULL;
busypf_head = p;
}
// 按FIFO方式調新頁面入內存頁面
p = freepf_head->next; // 先保存內存表中當前位置的下一位置
freepf_head->next = NULL;
freepf_head->pn = page[i]; // 頁表號
pl[page[i]].pfn = freepf_head->pfn; // 內存塊號
if (busypf_tail == NULL) {
// busypf_head指向最老的,busypf_tail指向最新的
busypf_head = busypf_tail = freepf_head;
}
else{
busypf_tail->next = freepf_head; //free頁面減少一個
busypf_tail = freepf_head;
}
freepf_head = p;
}
}
printf("FIFO:%6.4f\n", 1 - diseffect / 320.0);
return 0;
}
int LRU(int total_pf) { /*最近最久未使用算法(使用時鐘計數器)*/
int min, minj, present_time;
initialize(total_pf);
present_time = 0;
for (int i = 0; i<total_instruction; i++){
if (pl[page[i]].pfn == INVALID) { //頁面失效,不在內存中
diseffect++;
if (freepf_head == NULL) { //內存無空閒頁面
min = 32767;
for (int j = 0; j < total_vp; j++) { //找出內存塊中time的最小值
if (min > pl[j].time && pl[j].pfn != INVALID) // 查詢頁表
{
min = pl[j].time;
minj = j; // 記下內存塊號
}
}
freepf_head = &pfc[pl[minj].pfn]; //騰出一個單元(對應的內存塊)
pl[minj].pfn = INVALID;
pl[minj].time = -1;
freepf_head->next = NULL;
}
pl[page[i]].pfn = freepf_head->pfn; //有空閒頁面,改爲有效(內存塊號)
pl[page[i]].time = present_time;
freepf_head = freepf_head->next; //減少一個free 頁面
}
else {
pl[page[i]].time = present_time; //命中則設置時間
}
present_time++;
}
printf("LRU:%6.4f\n", 1 - diseffect / 320.0);
return 0;
}
int NUR(int total_pf) { /*最近未使用算法(每執行50條指令引用位清零一次)*/
int dp, cont_flag, old_dp;
initialize(total_pf);
dp = 0;
for (int i = 0; i < total_instruction; i++) {
if (pl[page[i]].pfn == INVALID) { //頁面失效,不在內存中
diseffect++;
if (freepf_head == NULL) { //無空閒頁面
cont_flag = TRUE;
old_dp = dp;
while (cont_flag) {
// 查頁表(對應引用位0,在內存中)
if (pl[dp].counter == 0 && pl[dp].pfn != INVALID) {
cont_flag = FALSE;
}
else {
dp++;
if (dp == total_vp) {
dp = 0;
}
if (dp == old_dp) {
// 第2輪掃描結束,失敗(引用位全部置爲0)
for (int j = 0; j < total_vp; j++) {
pl[j].counter = 0;
}
}
}
}
freepf_head = &pfc[pl[dp].pfn]; //騰出一個單元(對應的內存塊)
pl[dp].pfn = INVALID;
freepf_head->next = NULL;
}
pl[page[i]].pfn = freepf_head->pfn;
freepf_head = freepf_head->next;
}
else {
pl[page[i]].counter = 1; // 在內存中,“引用位”置爲1
}
// 每執行50條指令,引用位清零一次
if (i%clear_period == 0) {
for (int j = 0; j < total_vp; j++) {
pl[j].counter = 0;
}
}
}
printf("NUR:%6.4f\n", 1 - diseffect / 320.0);
return 0;
}
int OPT(int total_pf) { /*最佳置換算法*/
int max, maxpage, d, dist[total_vp];
initialize(total_pf);
for (int i = 0; i < total_instruction; i++) {
if (pl[page[i]].pfn == INVALID) { //頁面失效,不在內存中
diseffect++;
if (freepf_head == NULL) { //無空閒頁面
for (int j = 0; j < total_vp; j++) {
if (pl[j].pfn != INVALID) {
dist[j] = 32767; /* 最大"距離" */
}
else {
dist[j] = 0;
}
}
d = 1;
for (int j = i + 1; j < total_instruction; j++) {
if (pl[page[j]].pfn != INVALID && dist[page[j]] == 32767) {
dist[page[j]] = d;
}
d++;
}
max = -1;
for (int j = 0; j < total_vp; j++) {
if (max < dist[j]) {
max = dist[j];
maxpage = j;
}
}
freepf_head = &pfc[pl[maxpage].pfn];
freepf_head->next = NULL;
pl[maxpage].pfn = INVALID;
}
pl[page[i]].pfn = freepf_head->pfn;
freepf_head = freepf_head->next;
}
}
printf("OPT:%6.4f\n", 1 - diseffect / 320.0);
return 0;
}
int LFU(int total_pf){ /*最不經常使用置換法*/
int i, j, min, minpage;
initialize(total_pf);
for (i = 0; i < total_instruction; i++){
if (pl[page[i]].pfn == INVALID) { //頁面失效,不在內存中
diseffect++;
if (freepf_head == NULL) { //無空閒頁面
min = 32767;
for (j = 0; j < total_vp; j++) {
if (min > pl[j].counter && pl[j].pfn != INVALID) {
min = pl[j].counter;
minpage = j;
}
//pl[j].counter = 0;
//pl[j].counter >>= 1;
}
freepf_head = &pfc[pl[minpage].pfn];
pl[minpage].pfn = INVALID;
freepf_head->next = NULL;
}
pl[page[i]].pfn = freepf_head->pfn; //有空閒頁面,改爲有效
pl[page[i]].counter++;
freepf_head = freepf_head->next; //減少一個free頁面
}
else {
pl[page[i]].counter++; // 軟件計數器(被訪問次數)
}
// 定期右移
if (i % 15 == 0) {
for (int j = 0; j < total_vp; j++) {
pl[j].counter >>= 1;
}
}
}
printf("LFU:%6.4f\n", 1 - diseffect / 320.0);
return 0;
}