1. 程式人生 > >頁面排程演算法模擬

頁面排程演算法模擬

模擬實現的演算法:FIFO,Optimal(最佳置換),LRU,Clock,改進的Clock演算法

一、先入先出(FIFO):

最簡單的頁面置換演算法是先入先出(FIFO)法。這種演算法的實質是,總是選擇在主存中停留時間最長(即最老)的一頁置換,即先進入記憶體的頁,先退出記憶體。理由是:最早調入記憶體的頁,其不再被使用的可能性比剛調入記憶體的可能性大。建立一個FIFO佇列,收容所有在記憶體中的頁。被置換頁面總是在佇列頭上進行。當一個頁面被放入記憶體時,就把它插在隊尾上。


這種演算法只是在按線性順序訪問地址空間時才是理想的,否則效率不高。因為那些常被訪問的頁,往往在主存中也停留得最久,結果它們因變“老”而不得不被置換出去。

FIFO的另一個缺點是,它會產生Belady現象,即在增加儲存塊的情況下,反而使缺頁中斷率增加了。

模擬演算法如下:
package paging;

import java.util.LinkedList;

/**
 * FIFO(先進先出)頁面置換演算法
 *
 * @author wz
 * @date 15/11/30.
 */
public class FIFO {
    private LinkedList<Integer> memoryBlock;

    void pageReplacement(int[] pageString, int memBlockNum) {
        memoryBlock = new LinkedList<>();
        int pageFaultCount = 0, pageReplaceCount = 0;
        for (int i = 0; i < pageString.length; i++) {
            if (memoryBlock.contains(pageString[i]))
                continue;
            if (memoryBlock.size() >= memBlockNum) {
                memoryBlock.pollFirst();
//                memoryBlock.set(0, pageString[i]);
                pageReplaceCount++;
            }
            memoryBlock.add(pageString[i]);
            pageFaultCount++;
        }
        System.out.println("缺頁中斷率: "+pageFaultCount/(double)pageString.length);
        System.out.println("頁面置換次數: "+pageReplaceCount);
    }
}

二、Optimal(最佳置換)

這是一種理想情況下的頁面置換演算法,但實際上是不可能實現的。該演算法的基本思想是:發生缺頁時,有些頁面在記憶體中,其中有一頁將很快被訪問(也包含緊接著的下一條指令的那頁),而其他頁面則可能要到10、100或者1000條指令後才會被訪問,每個頁面都可以用在該頁面首次被訪問前所要執行的指令數進行標記。最佳頁面置換演算法只是簡單地規定:標記最大的頁應該被置換。這個演算法唯一的一個問題就是它無法實現。當缺頁發生時,作業系統無法知道各個頁面下一次是在什麼時候被訪問。雖然這個演算法不可能實現,但是最佳頁面置換演算法可以用於對可實現演算法的效能進行衡量比較。

當請求頁面不在記憶體中時,選擇已在記憶體中的永不使用的或者是在最長時間內不再被訪問的頁面置換出去,將請求的頁面換入。

模擬演算法如下:

package paging;

import java.util.LinkedList;

/**
 * Optimal(最佳)置換演算法
 *
 * @author wz
 * @date 15/11/30.
 */
public class Optimal {
    private LinkedList<Integer> memoryBlock;

    void pageReplacement(int[] pageString, int memBlockNum) {
        memoryBlock = new LinkedList<>();
        int maxDistIndex,willVisit,replaceIndex = -1;
        int pageFaultCount = 0, pageReplaceCount = 0;
        for (int i = 0; i < pageString.length; i++) {
            if (memoryBlock.contains(pageString[i]))
                continue;
            if (memoryBlock.size() >= memBlockNum) {
                // 查詢最長時間內不被訪問的頁
                maxDistIndex = -1;
                for (int j = 0; j < memBlockNum; j++) {
                    willVisit = 0;
                    for (int k = i+1; k < pageString.length; k++) {
                        if (memoryBlock.get(j) == pageString[k]) {
                            if (k > maxDistIndex){
                                maxDistIndex = k;
                                replaceIndex = j;
                            }
                            willVisit = 1;
                            break;
                        }
                    }
                    if (willVisit == 0){
                        replaceIndex = j;
                        break;
                    }
                }
                memoryBlock.set(replaceIndex, pageString[i]);
                pageReplaceCount++;
            } else
                memoryBlock.add(pageString[i]);
            pageFaultCount++;
        }
        System.out.println("缺頁中斷率: "+pageFaultCount/(double)pageString.length);
        System.out.println("頁面置換次數: "+pageReplaceCount);
    }
}

三、最近最久未使用(LRU)演算法

當請求頁面不在記憶體中時,將最近最久未用的頁面置換出去。用棧來儲存記憶體中的頁面,將棧底頁面換出,將請求頁面換入壓入棧頂。

LRU演算法是與每個頁面最後使用的時間有關的。當必須置換一個頁面時,LRU演算法選擇過去一段時間裡最久未被使用的頁面。
LRU演算法是經常採用的頁面置換演算法,並被認為是相當好的,但是存在如何實現它的問題。LRU演算法需要實際硬體的支援。其問題是怎麼確定最後使用時間的順序,對此有兩種可行的辦法:
1.計數器。最簡單的情況是使每個頁表項對應一個使用時間欄位,並給CPU增加一個邏輯時鐘或計數器。每次儲存訪問,該時鐘都加1。每當訪問一個頁面時,時鐘暫存器的內容就被複制到相應頁表項的使用時間欄位中。這樣我們就可以始終保留著每個頁面最後訪問的“時間”。在置換頁面時,選擇該時間值最小的頁面。這樣做,[1] 不僅要查頁表,而且當頁表改變時(因CPU排程)要 維護這個頁表中的時間,還要考慮到時鐘值溢位的問題。
2.棧。用一個棧保留頁號。每當訪問一個頁面時,就把它從棧中取出放在棧頂上。這樣一來,棧頂總是放有目前使用最多的頁,而棧底放著目前最少使用的頁。由於要從棧的中間移走一項,所以要用具有頭尾指標的雙向鏈連起來。在最壞的情況下,移走一頁並把它放在棧頂上需要改動6個指標。每次修改都要有開銷,但需要置換哪個頁面卻可直接得到,用不著查詢,因為尾指標指向棧底,其中有被置換頁。

此處使用棧,模擬演算法如下:

package paging;

import java.util.LinkedList;

/**
 * LRU(最近最久未使用)頁面置換演算法
 *
 * @author wz
 * @date 15/11/30.
 */
public class LRU {
    private LinkedList<Integer> memoryBlock;

    void pageReplacement(int[] pageString, int memBlockNum) {
        memoryBlock = new LinkedList<>();
        int pageFaultCount = 0, pageReplaceCount = 0;
        for (int i = 0; i < pageString.length; i++) {
            if (memoryBlock.contains(pageString[i])){
                memoryBlock.addLast(memoryBlock.remove(memoryBlock.indexOf(pageString[i])));
                continue;
            }else if (memoryBlock.size() >= memBlockNum) {
                memoryBlock.pollFirst();
                pageReplaceCount++;
            }
            memoryBlock.addLast(pageString[i]);
            pageFaultCount++;
        }
        System.out.println("缺頁中斷率: "+pageFaultCount/(double)pageString.length);
        System.out.println("頁面置換次數: "+pageReplaceCount);
    }

}

四、Clock演算法

當某一頁首次裝入記憶體中時,則將該頁框的使用位設定為1;當該頁隨後被訪問到時(在訪問產生缺頁中斷之後),它的使用位也會被設定為1。

當請求頁面不在記憶體中時,查詢記憶體中的頁面,每當遇到一個使用位為1的頁框時,就將該位重新置為0;如果在這個過程開始時,緩衝區中所有頁框的使用位均為0時,則選擇遇到的第一個頁框置換;如果所有頁框的使用位均為1時,則指標在緩衝區中完整地迴圈一週,把所有使用位都置為0,再次迴圈遍歷,置換第一個遇到的使用位為0的頁面。

模擬演算法如下:

package paging;

import java.util.LinkedList;

/**
 * 簡單Clock置換演算法
 *
 * @author wz
 * @date 15/11/30.
 */
public class Clock {
    private LinkedList<Integer> memoryBlock;
    private int[] accessed;

    void pageReplacement(int[] pageString, int memBlockNum) {
        memoryBlock = new LinkedList<>();
        accessed = new int[memBlockNum];
        int pageFaultCount = 0, pageReplaceCount = 0;
        for (int i = 0; i < pageString.length; i++) {
            if (memoryBlock.contains(pageString[i])){
                accessed[memoryBlock.indexOf(pageString[i])] = 1;
                continue;
            }else if (memoryBlock.size() >= memBlockNum) {
                for (int j = 0; j < accessed.length;j++) {
                    accessed[j] ^= 1;   //取反
                    if(accessed[j]==1){
                        memoryBlock.set(j,pageString[i]);
                        break;
                    }
                    if (j == accessed.length-1)
                        j = -1;
                }
                pageReplaceCount++;
            } else{
                memoryBlock.addLast(pageString[i]);
                accessed[memoryBlock.size()-1] = 1;
            }
            pageFaultCount++;
        }
        System.out.println("缺頁中斷率: "+pageFaultCount/(double)pageString.length);
        System.out.println("頁面置換次數: "+pageReplaceCount);
    }
}

五、改進的Clock演算法

在將一個頁面換出時,如果該頁已被修改過,便須將它重新寫到磁碟上;但如果該頁未被修改過,則不必將它拷回磁碟。同時滿足這兩條件的頁面作為首先淘汰的頁。由訪問位A和修改位M可以組合成下面四種類型的頁面:

1.(A=0,M=0):表示該頁最近既未被訪問、又未被修改,是最佳淘汰頁。 

2. (A=0,M=1):表示該頁最近未被訪問,但已被修改,並不是很好的淘汰頁。 

3. (A=1,M=0):最近已被訪問,但未被修改,該頁有可能再被訪問。 

4. (A=1,M=1):最近已被訪問且被修改,該頁有可能再被訪問.

在進行頁面置換時,其執行過程可分成以下三次遍歷:

(1)從指標所指示的當前位置開始,掃描迴圈佇列,尋找A=0且M=0的第一類頁面,將所遇到的第一個頁面作為所選中的淘汰頁。在第一次掃描期間不改變訪問位A。   

(2)如果第一步失敗,即查詢一週後未遇到第一類頁面,則開始第二輪掃描,尋找A=0且M=1的第二類頁面,將所遇到的第一個這類頁面作為淘汰頁。在第二輪掃描期間,將所有經過的頁面的訪問位置0。   

(3)如果第二步也失敗,即未找到第二類頁面,則將指標返回到開始的位置,並將所有的訪問位復0。然後,重複第一步,如果仍失敗,必要時再重複第二步,此時就一定能夠找到被淘汰的頁。

模擬演算法如下:

package paging;

import java.util.LinkedList;

/**
 * 改進的Clock置換演算法
 *
 * @author wz
 * @date 15/11/30.
 */
public class ClockImprove {
    private LinkedList<Integer> memoryBlock;
    private int[] accessed;
    private int[] modified;

    void pageReplacement(int[] pageString, int[] modifyStatus, int memBlockNum) {
        int index;
        memoryBlock = new LinkedList<>();
        accessed = new int[memBlockNum];
        modified = new int[memBlockNum];
        int pageFaultCount = 0, pageReplaceCount = 0;
        for (int i = 0; i < pageString.length; i++) {
            if (memoryBlock.contains(pageString[i])){
                index = memoryBlock.indexOf(pageString[i]);
                accessed[index] = 1;
                if (modified[index]==0)
                    modified[index]=modifyStatus[i];
                continue;
            } else if (memoryBlock.size() >= memBlockNum) {
                index=-1;
                while (true){
                    for (int j = 0; j < accessed.length; j++) {
                        if (accessed[j] == 0 && modified[j]==0){
                            index=j;
                            break;
                        }
                    }
                    if (index >= 0)
                        break;
                    for (int j = 0; j < accessed.length; j++) {
                        if (accessed[j] == 0 && modified[j]==1){
                            index = j;
                            break;
                        }
                        accessed[j]=0;
                    }
                    if (index >= 0)
                        break;
                }
                memoryBlock.set(index,pageString[i]);
                pageReplaceCount++;
            } else{
                memoryBlock.addLast(pageString[i]);
                index = memoryBlock.size()-1;
            }
            accessed[index] = 1;
            modified[index]=modifyStatus[i];
            pageFaultCount++;
        }
        System.out.println("缺頁中斷率: "+pageFaultCount/(double)pageString.length);
        System.out.println("頁面置換次數: "+pageReplaceCount);
    }
}

部落格園部落格:欠扁的小籃子