操作系統實驗三——請求頁式存儲管理
實驗環境
C++ | g++ 8.1.0 |
---|---|
IDE | Visual Studio 2017 Enterprise (15.9.13) |
操作系統 | Windows 10 x64 中文專業版 (1903) |
實驗目的
近年來,由於大規模集成電路(LSI)和超大規模集成電路(VLSI)技術的發展,使存儲器的容量不斷擴大,價格大幅度下降。但從使用角度看,存儲器的容量和成本總受到一定的限制。所以,提高存儲器的效率始終是操作系統研究的重要課題之一。虛擬存儲技術是用來擴大內存容量的一種重要方法。學生應獨立地用高級語言編寫幾個常用的存儲分配算法,並設計一個存儲管理的模擬程序,對各種算法進行分析比較,評測其性能優劣,從而加深對這些算法的了解。
為了比較真實地模擬存儲管理,可預先生成一個大致符合實際情況的指令地址流。然后模擬這樣一種指令序列的執行來計算和分析各種算法的訪問命中率。
實驗內容
-
編制和調試示例給出的請求頁式存儲管理程序,並使其投入運行。
-
增加1~2 種已學過的淘汰算法,計算它們的頁面訪問命中率。試用各種算法的命中率加以比較分析。
【示例】
1、題目本示例是采用頁式分配存儲管理方案,並通過分析計算不同頁面淘汰算法情況下的訪問命中率來比較各種算法的優劣。另外也考慮到改變頁面大小和實際存儲器容量對計算結果的影響,從而可為算則好的算法、合適的頁面尺寸和實存容量提供依據。
本程序是按下述原則生成指令序列的:
(1)50%的指令是順序執行的。
(2)25%的指令均勻散布在前地址部分。
(3)25%的指令均勻散布在后地址部分。
示例中選用最佳淘汰算法(OPT)和最近最少使用頁面淘汰算法(LRU)計算頁面命中率。
公式為:
假定虛存容量為32K,頁面尺寸從1K 至8K,實存容量從4 頁至32 頁。
2、算法與框圖
(1)最佳淘汰算法(OPT)。
這是一種理想的算法,可用來作為衡量其他算法優劣的依據,在實際系統中是難以實現的,因為它必須先知道指令的全部地址流。由於本示例中已預先生成了全部的指令地址流,故可計算出最佳命中率。該算法的准則是淘汰已滿頁表中不再訪問或是最遲訪問的的頁。這就要求將頁表中的頁逐個與后繼指令訪問的所有頁比較,如后繼指令不在訪問該頁,則把此頁淘汰,不然得找出后繼指令中最遲訪問的頁面淘汰。可見最佳淘汰算法要花費較長的運算時間。
(2)最近最少使用頁淘汰算法(LRU)。
這是一種經常使用的方法,有各種不同的實施方案,這里采用的是不斷調整頁表鏈的方法,即總是淘汰頁表鏈鏈首的頁,而把新訪問的頁插入鏈尾。如果當前調用頁已在頁表內,則把它再次調整到鏈尾。這樣就能保證最近使用的頁,總是處於靠近鏈尾部分,而不常使用的頁就移到鏈首,逐個被淘汰,在頁表較大時,調整頁表鏈的代價也是不小的。
(3)主程序框圖如下圖 1示。
操作過程
1. 編制和調試示例給出的請求頁式存儲管理程序,並使其投入運行。【完整程序見附錄】
- 指令流生成
本程序按下述原則生成指令序列的:
(1)前50%的指令是順序執行的。
(2)后25%的指令隨機散布在前地址部分。
(3)后25%的指令隨機散布在后地址部分。
正整數的范圍從0~32767,指令地址均在此區間,所以限制程序中的虛存尺寸為32K,且設置指令流長度為256。關鍵代碼如下:
// 生成指令流
void init()
{
srand(time(0));
printf("vmsize = 32k\n");
printf("length = 256\n");
printf("= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =\n");
printf("THE VIRTUAL ADDRESS STREAM AS FOLLOWS:\n");
printf("= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =\n");
for (int i = 0; i < length; i++)
{
if (i == 0)
a[i] = rand() % 32767;
else if (i < length / 2)
a[i] = a[i - 1] + 1;
else if (i < length / 4 * 3)
a[i] = rand() % (32767 / 2);
else
a[i] = (32767 / 2) + rand() % (32767 / 2);
printf("a[%d]=%d ", i, a[i]);
if (i % 6 == 0)
printf("\n");
}
printf("\n");
printf("= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =\n");
}
- OPT算法實現
OPT算法為淘汰已滿頁表中不再訪問或是最遲訪問的的頁。這就要求將頁表中的頁逐個與后繼指令訪問的所有頁比較,如后繼指令不在訪問該頁,則把此頁淘汰,不然得找出后繼指令中最遲訪問的頁面淘汰。本實驗采取鏈表方式實現OPT算法,其算法框圖和關鍵代碼如下:
// 淘汰已滿頁表中不再訪問或是最遲訪問的的頁
void OPT(int memcount, int pagesize)
{
/*
淘汰已滿頁表中不再訪問或是最遲訪問的的頁。
這就要求將頁表中的頁逐個與后繼指令訪問的所有頁比較,
如后繼指令不在訪問該頁,則把此頁淘汰,不然得找出后繼指令中最遲訪問的頁面淘汰。
*/
node *table = new node; // 頁表 table[i]=j,表示虛存的第j 頁在實存的第i 頁中。
node *tail = table;
int called; // 當前請求的頁面號
bool pagefault; // 頁面失效標志,如當前請求頁called 已在頁表內,則置pagefault=false,否則為true
int used = 0; // 當前被占用的實存頁面數,可用來判斷當前實存中是否有空閑頁。
int misscount = 0; // 頁面失效次數
for (int j = 0; j < length; j++) // 順序執行指令流
{
called = page[j];
node *p = table;
node *q = p;
pagefault = false;
/* 如果當前調用頁已在頁表內,則頁面有效 */
while (p->next != NULL)
{
if (p->next->target == called)
{
pagefault = true;
break;
}
p = p->next;
}
/* 如果當前調用頁不在頁表內,則在空間不足時執行淘汰算法 */
if (pagefault == false)
{
misscount++; // 頁面訪問時產生中斷--->頁面失效
// 無空閑物理塊則淘汰已滿頁表中不再訪問或是最遲訪問的的頁
if (used == memcount)
{
p = table;
q = p->next; // q記錄當前最遲訪問頁面在鏈表中的位置
int index = 0; // index記錄當前最遲訪問頁面的序號
while (p->next != NULL)
{
int i;
for (i = j + 1; i < length; i++)
{
if (p->next->target == page[i])
{
break;
}
}
if (i > index) // 該頁面為當前最遲訪問頁面
{
index = i;
q = p;
}
if (i == length) // 該頁面以后不會被訪問了
{
q = p;
break;
}
p = p->next;
}
// q->next指向頁表中不再訪問或是最遲訪問的的頁
if (tail == q->next) // 特殊情況:要刪除末頁的話要修改tail的指向
tail = q;
// 刪除淘汰的頁面
p = q->next;
q->next = p->next;
delete p;
used--;
}
//插入新的頁面
p = new node(called);
tail->next = p;
tail = p;
used++;
}
}
double rate = 100 - misscount * 100 / double(length);
printf("%3d %3d %3.3lf% \n", memcount, misscount, rate);
}
- LRU算法實現
這里采用的是不斷調整頁表鏈的方法,即總是淘汰頁表鏈鏈首的頁,而把新訪問的頁插入鏈尾。如果當前調用頁已在頁表內,則把它再次調整到鏈尾。算法框圖和關鍵代碼如下:
void LRU(int memcount, int pagesize)
{
/*
總是淘汰頁表鏈鏈首的頁,而把新訪問的頁插入鏈尾。
如果當前調用頁已在頁表內,則把它再次調整到鏈尾。
這樣就能保證最近使用的頁,總是處於靠近鏈尾部分,而不常使用的頁就移到鏈首,逐個被淘汰
*/
node *table = new node; // 頁表 table[i]=j,表示虛存的第j 頁在實存的第i 頁中。
node *tail = table;
int called; // 當前請求的頁面號
bool pagefault; // 頁面失效標志,如當前請求頁called 已在頁表內,則置pagefault=false,否則為true
int used = 0; // 當前被占用的實存頁面數,可用來判斷當前實存中是否有空閑頁。
int misscount = 0; // 頁面失效次數
for (int j = 0; j < length; j++) // 順序執行指令流
{
called = page[j];
node *p = table;
node *q = p;
pagefault = false;
/* 如果當前調用頁已在頁表內,則把它再次調整到鏈尾 */
while (p->next != NULL)
{
if (p->next->target == called)
{
pagefault = true;
if (p->next != tail)
{
// 調整位置
q = p->next;
p->next = q->next;
q->next = NULL;
tail->next = q;
tail = q;
}
break;
}
p = p->next;
}
/* 如果當前調用頁不在頁表內,則把它添加到鏈尾 */
if (pagefault == false)
{
misscount++;
// 無空閑物理塊則淘汰第一個頁面
if (used == memcount)
{
p = table->next;
table->next = p->next;
delete p;
used--;
}
//插入新的頁面
p = new node(called);
tail->next = p;
tail = p;
used++;
}
}
double rate = 100 - misscount * 100 / double(length);
printf("%3d %3d %3.3lf% \n", memcount, misscount, rate);
}
2. 增加1~2 種已學過的淘汰算法,計算它們的頁面訪問命中率。試用各種算法的命中率加以比較分析。【完整程序見附錄】
- FIFO算法實現
這里增加了FIFO淘汰算法,即先調用到內存的頁面先被淘汰出去。程序打印出不同頁面大小和物理塊數時三種算法的命中率。下面時該算法的程序框圖和關鍵代碼:
\實驗\請求頁式存儲管理\image7.png)// 先訪問的頁先淘汰
void FIFO(int memcount, int pagesize)
{
node *table = new node; // 頁表 table[i]=j,表示虛存的第j 頁在實存的第i 頁中。
node *tail = table;
int called; // 當前請求的頁面號
bool pagefault; // 頁面失效標志,如當前請求頁called 已在頁表內,則置pagefault=false,否則為true
int used = 0; // 當前被占用的實存頁面數,可用來判斷當前實存中是否有空閑頁。
int misscount = 0; // 頁面失效次數
for (int j = 0; j < length; j++) // 順序執行指令流
{
called = page[j];
node *p = table;
node *q = p;
pagefault = false;
/* 如果當前調用頁已在頁表內(頁面有效),則保持其位置不動 */
while (p->next != NULL)
{
if (p->next->target == called)
{
pagefault = true;
break;
}
p = p->next;
}
/* 如果當前調用頁不在頁表內(頁面失效),則執行淘汰算法,並把它添加到鏈尾 */
if (pagefault == false)
{
misscount++;
// 無空閑物理塊則淘汰第一個頁面
if (used == memcount)
{
if (tail == table->next) // 特殊情況:要刪除末頁的話要修改tail的指向
tail = table;
p = table->next;
table->next = p->next;
delete p;
used--;
}
//插入新的頁面
p = new node(called);
tail->next = p;
tail = p;
used++;
}
}
double rate = 100 - misscount * 100 / double(length);
printf("%3d %3d %3.3lf% \n", memcount, misscount, rate);
}
void Test_alg()
{
int pagesize; // 頁面大小
int alg;
setpage(pagesize);
printf("The algorithm is [OPT = 1 LRU = 2]:");
scanf("%d", &alg);
//memcount: 物理地址塊數
printf("MEMORY MISS COUNT HIT RATE\n");
for (int memcount = 2; memcount <= 32 / pagesize; memcount = memcount + 2)
{
if (alg == 1)
{
OPT(memcount, pagesize);
}
else if (alg == 2)
{
LRU(memcount, pagesize);
}
else
{
break;
}
}
}
結果
1. 編制和調試示例給出的請求頁式存儲管理程序,並使其投入運行。
下面截取了73~156的指令地址流:
頁面大小為1K時對應的虛擬頁號:
MEMORY為內存塊數,MISS COUNT為缺頁次數,HIT RATE為命中率。
這里截取了使用OPT算法和LRU算法,頁面大小為1K,不同物理塊數情況下的命中率情況。
可以明顯看出隨着物理塊數增大,OPT算法和LRU算法的命中率都明顯增大,OPT在塊數達到18以后保持最大值87.5%,LRU在塊數達到28以后才達到最大值87.5%。在16塊以下時LRU算法總是比OPT算法的命中率低,但在16以后都達到了87%,兩者相差較小。
2. 增加1~2 種已學過的淘汰算法,計算它們的頁面訪問命中率。試用各種算法的命中率加以比較分析。
部分運行結果:
分析
從結果可以看出,相同頁面大小不同物理塊數時,LRU算法和FIFO算法命中率不相上下,這跟頁面的分布情況有關。但是這兩種算法都比OPT算法低,且隨着物理塊數增大而增大。在vmsize = 32k ,pagesize = 8k memcount = 2時三種算法都達到最大命中率HIT RATE: 98.438%,這是因為總共只有4個虛擬頁面,在兩個物理塊的情況下頁面交換次數極小,說明命中率與主存容量、頁面大小、程序局部性和替換算法等因素都有很大的關系。