計算機操作系統實驗之頁面置換算法(C語言)
實驗目的
1、瞭解內存分頁管理策略
2、掌握一些基本的頁面置換算法
實驗內容與基本要求
用C,C++等語言編寫程序,實現OPT、FIFO、LRU置換算法
頁面置換算法的基本內容
頁面置換算法是在當進程運行過程中,若其要訪問的頁面不在內存且內存已滿時,要決定將哪個頁面換出的算法。常見的頁面置換算法包括最佳置換、先進先出置換、最近最久未使用置換和Clock置換等。本次的實驗實現的算法包括最佳置換算法(OPT)、先進先出置換算法(FIFO)和最近最久未使用算法(LRU)。
頁面置換算法涉及到一些概念如下:
- 缺頁率:當需要訪問的頁面不在內存時稱爲缺頁,此時需要將頁面調入內存。缺頁率就是要訪問的頁面不在內存中的概率。因此缺頁率=缺頁次數/要訪問的頁面總數。需要注意的是,缺頁的時候不一定需要進行頁面置換(如果內存還沒滿,直接將頁面調入內存即可)。
- 置換率:置換就是將舊頁面調出內存,新頁面調進內存,即新頁面代替舊頁面的過程。置換率就是需要進行頁面置換的概率。所以置換率=置換次數/要訪問的頁面總數。
- 命中率:就是要訪問的頁面恰好在內存中的概率。可以發現(缺頁率+命中率=1)。
最佳置換算法
最佳置換算法,就是所選擇內存中以後永遠不再使用,或者是在未來最長的一段時間內不再被訪問的頁面來換出。
用這種算法可以保證獲得最低的缺頁率,最低的置換次數,因此效率最高。然而在實際情況中,我們是無法知道哪個頁面是未來最長時間內不再被訪問的,所以實際上它是無法實現的。
先進先出置換算法
先進先出置換算法,就是選擇內存中最先進入內存,在內存中呆的最久的頁面來換出。它實現簡單,但是效率不高。
最近最久未使用算法
最近最久未使用算法,是選擇當前內存中,最久沒有被訪問的頁面來換出。它是希望通過過去頁面訪問的情況,來預測未來頁面的訪問情況,但是頁面過去與未來的走向之間並沒有必然的聯繫,因此它的效率也不是十分高。
實現思路
無論採用哪個算法,當進程需要訪問一個頁面時,存在三種情況:
1.頁面已經在內存中
2.頁面不在內存中,但是此時內存還未滿
3.頁面不在內存中,且此時內存已滿,需要把頁面換出
不同算法的區別主要是決定換出哪個頁面
最佳置換算法中,被換出的算法是在最長未來時間內不再被訪問的頁面。也就是說,需要計算出當前內存中頁面的下一次訪問位置,哪個頁面的下一次訪問位置最遠,就將它換出。因此需要一個數組額外記錄下次訪問位置,每當訪問完一個頁面(不管這個頁面是新換入的,還是早就在內存中的),都需要遍歷剩下的頁面號引用串,更新這個數組。
先進先出置換算法比較簡單,用一個變量記錄當前內存中最先進入頁面的下標。由於頁面都是按數組下標順序保存的,因此每訪問一個頁面,該變量就加一。等變量等於數組長度時,再重新歸零即可。
最近最久未使用算法有兩種思路:1.與最佳置換算法類似,設置一個時間數組,記錄從內存中頁面上次訪問至今的時間,哪個頁面的時間最長則將它換出。如果要訪問的頁面已在內存中,則時間歸零。當每次發起一個訪問請求,則所有頁面訪問時間加一,更新該數組。2.用數組模擬隊列的結構,隊列頭出隊列尾入,每當需要訪問新的頁面時,就將數組內的數據前移一位,新頁面加入數組最後。如果要訪問的頁面已在內存中,則用一個臨時變量記錄該頁面,然後從該頁面起的數據前移一位,把該頁面加入數組最後(課本上說是用棧的結構,但是嚴格上來說,棧只允許一端出入。因此按照課本上的功能描述,實際應該採用的結構仍是隊列)
流程圖
程序總流程圖
OPT算法流程圖
FIFO算法流程圖
LRU算法流程圖
全部代碼
代碼
//
// main.c
// pageReplacement
//
// Created by Apple on 2019/11/12.
// Copyright © 2019 Yao YongXin. All rights reserved.
//
#include <stdio.h>
//初始化隊列
void initializeList(int list[],int number){
for (int i = 0; i < number; i ++) {
list[i] = -1;
}
}
//展示隊列狀態
void showList(int list[], int number){
for (int i = 0; i < number; i ++) {
printf("%2d",list[i]);
}
printf("\n");
}
//展示當前內存狀態
void showMemoryList(int list[],int phyBlockNum){
for (int i = 0; i < phyBlockNum; i ++) {
if (list[i] == -1) {
break;
}
printf(" |%d|",list[i]);
}
printf("\n");
}
void informationCount(int missingCount,int replaceCount,int pageNum){
printf("缺頁次數:%d 缺頁率:%d/%d\n",missingCount,missingCount,pageNum);
double result = (double)(pageNum - missingCount)/(double)pageNum;
printf("置換次數:%d 命中率:%.2f\n",replaceCount,result);
}
//找到該頁面下次要訪問的位置
int getNextPosition(int currentPage,int currentPosition,int strList[],int pageNum){
for (int i = currentPosition+1; i < pageNum; i ++) {
if (strList[i] == currentPage) {
return i;
}
}
return 100;
}
//最佳置換算法
void replacePageByOPT(int memoryList[],int phyNum,int strList[],int pageNum){
//置換次數
int replaceCount = 0;
//缺頁次數
int missingCount = 0;
//記錄在內存的物理塊的下一次訪問位置
int nextPosition[phyNum];
//初始化
initializeList(nextPosition, phyNum);
//記錄當前頁面的訪問情況: 0 未訪問
int isVisited;
for (int i = 0; i < pageNum; i ++) {
isVisited = 0;
//判斷是否需要置換->內存已滿且需要訪問的頁面不在內存中
for (int j = 0; j < phyNum; j ++) {
if (memoryList[j] == strList[i]) {
//該頁面已經存在內存中
//記錄下一次訪問它的位置
nextPosition[j] = getNextPosition(memoryList[j], i, strList, pageNum);
//修改訪問情況
isVisited = 1;
//展示
printf("%d\n",strList[i]);
break;
}
if (memoryList[j] == -1) {
//頁面不在內存中且內存未滿->直接存入
memoryList[j] = strList[i];
nextPosition[j] = getNextPosition(memoryList[j], i, strList, pageNum);
missingCount ++;
//修改訪問情況
isVisited = 1;
//展示
printf("%d\n",strList[i]);
showMemoryList(memoryList, phyNum);
break;
}
}
if (!isVisited) {
//當前頁面還沒訪問過
//內存已滿且當前訪問不在內存中->進行置換
//1.尋找到最晚才被訪問到的頁面
int max = 0;
for (int k = 1; k < phyNum; k ++) {
if (nextPosition[max] < nextPosition[k]) {
max = k;
}
}
//2.將該位置的頁面換出
memoryList[max] = strList[i];
nextPosition[max] = getNextPosition(memoryList[max], i, strList, pageNum);
missingCount ++;
replaceCount ++;
//展示
printf("%d\n",strList[i]);
showMemoryList(memoryList, phyNum);
}
}
informationCount(missingCount, replaceCount,pageNum);
}
//先進先出置換算法
void replacePageByFIFO(int memoryList[],int phyNum,int strList[],int pageNum){
//置換次數
int replaceCount = 0;
//缺頁次數
int missingCount = 0;
//記錄當前最早進入內存的下標
int pointer = 0;
//記錄當前頁面的訪問情況: 0 未訪問
int isVisited = 0;
for (int i = 0; i < pageNum; i ++) {
isVisited = 0;
//判斷是否需要置換->內存已滿且需要訪問的頁面不在內存中
for (int j = 0; j < phyNum; j ++) {
if (memoryList[j] == strList[i]) {
//該頁面已經存在內存中
//修改訪問情況
isVisited = 1;
//修改訪問時間
//展示
printf("%d\n",strList[i]);
break;
}
if (memoryList[j] == -1) {
//頁面不在內存中且內存未滿->直接存入
memoryList[j] = strList[i];
//修改訪問情況
isVisited = 1;
missingCount ++;
//展示
printf("%d\n",strList[i]);
showMemoryList(memoryList, phyNum);
break;
}
}
if (!isVisited) {
//當前頁面還未被訪問過->需要進行頁面置換
//直接把這個頁面存到所記錄的下標中
memoryList[pointer] = strList[i];
//下標指向下一個
pointer ++;
//如果到了最後一個,將下標歸零
if (pointer > phyNum-1) {
pointer = 0;
}
missingCount ++;
replaceCount ++;
//展示
printf("%d\n",strList[i]);
showMemoryList(memoryList, phyNum);
}
}
informationCount(missingCount, replaceCount, pageNum);
}
//最近最久未使用置換算法
void replacePageByLRU(int memoryList[],int phyNum,int strList[],int pageNum){
//置換次數
int replaceCount = 0;
//缺頁次數
int missingCount = 0;
//記錄內存中最近一次訪問至今的時間
int timeRecord[phyNum];
//初始化
initializeList(timeRecord, phyNum);
//記錄當前頁面的訪問情況: 0 未訪問
int isVisited = 0;
//記錄已經在內存中的頁面數量
int pageCount = 0;
for (int i = 0; i < pageNum; i ++) {
isVisited = 0;
//時間加一
for (int p = 0; p < pageCount; p ++) {
if (memoryList[p] != -1) {
timeRecord[p] ++;
}
}
//是否需要置換
for (int j = 0; j < phyNum; j ++) {
if (memoryList[j] != -1) {
timeRecord[j] ++;
}
if (memoryList[j] == strList[i]) {
//該頁面已經存在內存中
//修改訪問情況
isVisited = 1;
//重置訪問時間
timeRecord[j] = -1;
//展示
printf("%d\n",strList[i]);
break;
}
if (memoryList[j] == -1) {
//頁面不在內存中且內存未滿->直接存入
memoryList[j] = strList[i];
pageCount ++;
//修改訪問情況
isVisited = 1;
//修改訪問時間
timeRecord[j] ++;
missingCount ++;
//展示
printf("%d\n",strList[i]);
showMemoryList(memoryList, phyNum);
break;
}
}
if (!isVisited) {
//需要置換
//1.遍歷時間記錄表,尋找最久未訪問的頁面所在的內存下標
int max = 0;
for (int k = 0; k < phyNum; k ++) {
if (timeRecord[max] < timeRecord[k]) {
max = k;
}
}
//2.將該位置的頁面換出
memoryList[max] = strList[i];
timeRecord[max] = -1;
missingCount ++;
replaceCount ++;
//展示
printf("%d\n",strList[i]);
showMemoryList(memoryList, phyNum);
}
}
informationCount(missingCount, replaceCount, pageNum);
}
//最近最久未使用置換算法-2
//void replacePageByLRU(int memoryList[],int phyNum,int strList[],int pageNum){
//
// int isVisited = 0;
//
// //記錄已經在內存中的頁面數量
// int pageCount = 0;
// for (int i = 0; i < pageNum; i ++) {
// isVisited = 0;
// //判斷內存是否需要換頁
// for (int j = 0; j < phyNum; j ++) {
// if (memoryList[j] == strList[i]) {
// //已經存在於內存中,把它換到隊列尾部
// int temp = memoryList[j];
// for (int k = j; k < pageCount-1; k ++) {
// memoryList[k] = memoryList[k+1];
// }
// memoryList[pageCount-1] = temp;
//
// //修改訪問情況
// isVisited = 1;
// //修改訪問時間
// //展示
// printf("%d\n",strList[i]);
// break;
// }
//
// if (memoryList[j] == -1) {
// //頁面不在內存中且內存未滿->直接存入
// memoryList[j] = strList[i];
// //修改訪問情況
// isVisited = 1;
//
// pageCount ++;
//
// //展示
// printf("%d\n",strList[i]);
// showMemoryList(memoryList, phyNum);
// break;
// }
// }
//
// if (!isVisited) {
// //需要換頁
// //1.直接將數組整體往前移一位
// for (int k = 0; k < phyNum; k ++) {
// memoryList[k] = memoryList[k+1];
// }
// //2.將當前頁面加到隊尾
// memoryList[phyNum-1] = strList[i];
// //展示
// printf("%d\n",strList[i]);
// showMemoryList(memoryList, phyNum);
// }
//
//
// }
//}
int main(int argc, const char * argv[]) {
//物理塊的數量
int phyBlockNum;
printf("請輸入物理塊數量:\n");
scanf("%d",&phyBlockNum);
//生成內存隊列
int memoryList[phyBlockNum];
//初始化內存狀態
initializeList(memoryList, phyBlockNum);
//showMemoryList(memoryList,phyBlockNum);
//頁面數量
int pageNum;
printf("請輸入要訪問的頁面總數:\n");
scanf("%d",&pageNum);
//保存頁面號引用串
int pageNumStrList[pageNum];
printf("請輸入要訪問的頁面號:\n");
for (int i = 0; i < pageNum; i ++) {
scanf("%d",&pageNumStrList[i]);
}
showList(pageNumStrList, pageNum);
int chose;
while (1) {
printf("請選擇所需的置換算法:\n");
printf("1.OPT 2.FIFO 3.LRU 4.退出\n");
scanf("%d",&chose);
switch (chose) {
case 1:
showList(pageNumStrList, pageNum);
replacePageByOPT(memoryList, phyBlockNum, pageNumStrList, pageNum);
//重新初始化內存
initializeList(memoryList, phyBlockNum);
break;
case 2:
showList(pageNumStrList, pageNum);
replacePageByFIFO(memoryList, phyBlockNum, pageNumStrList, pageNum);
//重新初始化內存
initializeList(memoryList , phyBlockNum);
break;
case 3:
showList(pageNumStrList, pageNum);
replacePageByLRU(memoryList, phyBlockNum, pageNumStrList, pageNum);
//重新初始化內存
initializeList(memoryList, phyBlockNum);
break;
default:
return 0;
break;
}
}
return 0;
}
實驗截圖