1. 程式人生 > >作業系統:程序排程演算法詳解之FCFS和SPF篇

作業系統:程序排程演算法詳解之FCFS和SPF篇

前言:

  在學習作業系統的時候,總是可以聽到一些與“排程”相關的東西。記得剛學計算機作業系統的時候,總是覺得除錯是一個很高大上的東西,不敢太深入地去接觸。這可能是因為學生時代的我在演算法方面並不強,而這些排程過程又常以演算法的形式出現在課本上。本身上這確實是一些演算法。我對這些演算法有一些抗拒,可能當時是被DotA給迷惑了吧。現在倒真是感覺演算法是一個迷人的東西,有時在沒有學到某一種演算法,自己也可以在已有的一些演算法思維的基礎上想出個一二來,這讓人很爽。

  本文也是我在溫習《計算機作業系統》這本書時候,想著來用程式碼的形式來描述這些迷人的東西。

概述:

  我們在編碼開發的時候,就是在跟程序打交道。不過,可能由於一些高階語言的封裝,我們在開發的過程可能感覺不到我們的程式碼對程序的建立或呼叫過程。當然,這也不是本文的重點。但是,作業系統卻不能不理會程序。下面我就使用Java開發語言來模擬一下程序在作業系統中的排程過程。

程序排程演算法:

1.FCFS:

  FCFS的意思是先來先服務(First Come First Service)。顧名思義就是按照程序被新增到等待佇列的先後順序來進行呼叫的。這裡可以先來看一張FCFS的演算法過程圖:


圖-1 程序FCFS排程過程

  從上面的過程圖中就可以清楚地知道,程序在整個過程被排程的順序及過程。

  不過,不知道讀者有沒有注意到一個問題,那就是上面的圖中,我們是讓程序(矩形)緊緊挨著的。那麼有沒有什麼情況是讓這些矩形不在一起緊挨著呢?如果你是一個注意細節的人,我想你已經注意到了這一點吧。說到這裡,我想問另一個問題,如果當我們的佇列中的程序都執行完成,而等待佇列中已經沒有程序了,那麼這個時候要怎麼處理?在這種情況下CPU一直是處於空閒的狀態,直到等待佇列中出現了一個程序請求處理機來執行。所以,在模擬程式裡我們就可以直接讓時間跳過這一段。

  關於上面的一點,在我們的程式碼裡也要考慮到。關鍵的步驟如下:

<pre name="code" class="java">@Override
    public int execute(ProcessModel... processList) {
        if (processList == null || processList.length == 0) {
            System.out.println(TAG + ">資料為空");
            return -1;
        }

        if (!(processList instanceof ProcessFCFSModel[])) {
            System.out.println(TAG + ">資料型別出錯");
            return -2;
        }

        ProcessFCFSModel[] fcfsModels = (ProcessFCFSModel[])processList;
        int runTimeSum = 0;
        for (ProcessFCFSModel model : fcfsModels) {
            if (runTimeSum < model.getComingTime()) {
                runTimeSum = (int)model.getComingTime();
            }

            model.setStartRunTime(runTimeSum);
            runTimeSum += model.getRunTime();
            model.setFinishTime(runTimeSum);
            model.setTurnaroundTime(runTimeSum - model.getComingTime());
            model.setTurnaroundWeightTime(1.0 * model.getTurnaroundTime() / model.getRunTime());
        }

        return runTimeSum;
    }

2.SPF:

  SPF的意思是短程序優先(Short Process First)。意思也很清楚,就是讓短的程序先執行,是什麼短呢?這裡說的是程序的執行時間。不要誤以為系統好牛逼,帶程序要執行多長時間都可以提前知道。其實,這個值是程式提前寫在程序裡面的了,系統只要去讀取這個值就可以了。

  這裡同樣也為SPF演算法畫了一個過程圖。其實這兩個演算法在過程圖沒有太大的出入,不過在程式碼差別還不小。見圖-2。

圖-2 程序SPF排程過程

  可以很明顯地看到這裡的程序B與左邊的程序佇列有一些分離的感覺,並且在程序B的上方還有“最短執行時間”的描述。的確,這裡的B是在執行過程中選出執行時間最短的程序。

  你不會要問我為什麼不事先給這個等待的程序佇列進行排序吧。如果是事先對佇列進行排序,在“排序”這個操作上時間複雜度的確可以降低。不過,很遺憾。我們不能這樣做。因為系統中的程序是一個動態的過程,在什麼時候有什麼程序過來,都還是未知數。我們不能讓作業系統這麼神通廣大,不讓我們人類怎麼辦?-_-!

SPF模擬排程過程的關鍵程式碼如下:

<pre name="code" class="java"><pre name="code" class="java">@Override
    public int execute(ProcessModel... processList) {
        if (processList == null || processList.length == 0) {
            System.out.println(TAG + ">資料為空");
            return -1;
        }

        if (!(processList instanceof ProcessSPFModel[])) {
            System.out.println(TAG + ">資料型別出錯");
            return -2;
        }

        ProcessSPFModel[] processArray = (ProcessSPFModel[])processList;
        boolean[]runFlag = new boolean[processArray.length];
        int runTimeSum = 0;
        int index = 0;
        ProcessSPFModel currentProcess = processArray[index];
        while(!noProcessWaitting(runFlag)) {
            currentProcess.setStartRunTime(runTimeSum);
            if (runTimeSum < currentProcess.getComingTime()) {
                runTimeSum = (int)currentProcess.getComingTime();
            }

            runTimeSum += currentProcess.getRunTime();
            currentProcess.setFinishTime(runTimeSum);
            currentProcess.setTurnaroundTime(runTimeSum - currentProcess.getComingTime());
            currentProcess.setTurnaroundWeightTime(1.0 * currentProcess.getTurnaroundTime() / currentProcess.getRunTime());

            runFlag[index] = true;

            index = getIndexMinRuntime(processArray, runFlag, runTimeSum);
            if (0 <= index && index < processArray.length) {
                currentProcess = processArray[index];
            } else {
                System.out.println("未知異常");
                break;
            }
        }

        return runTimeSum;
    }
這裡有一個getIndexMinRuntime(...)方法,它的功能就是獲得程序列表中服務時間最短的程序,當然前提是這個程序已經Coming。
private int getIndexMinRuntime(ProcessSPFModel[] processArray, boolean[] runFlag, int runTimeSum) {
        if (processArray.length == 0 || runFlag.length != processArray.length) {
            return -1;
        }

        int earliestIndex = 0;
        long earliestTime = Long.MAX_VALUE;
        int index = -1;
        long minTime = Long.MAX_VALUE;
        for (int i = 0; i < processArray.length; i++) {
            if (runFlag[i]) {
                continue;
            }

            if (processArray[i].getComingTime() < earliestTime) {
                earliestIndex = i;
                earliestTime = processArray[i].getComingTime();
            }

            if (processArray[i].getComingTime() > runTimeSum) {
                continue;
            }

            if (processArray[i].getRunTime() < minTime) {
                minTime = processArray[i].getRunTime();
                index = i;
            }
        }

        index = index < 0 ? earliestIndex : index;

        return index;
    }
在上面的程式碼裡,有兩個變數earliestIndex和earliestTime。前者是指尚未執行的最早到達的程序的下標,後者尚未執行的最早到達程序的到達時間。這定義這兩個變數的目的是針對於,如果等待的佇列中沒有可執行的程序,那麼我們就去在還沒有執行且是最早到達的程序去選擇。這一步事實上是模擬時間再向後推移。

例項和結果:

  這裡我們通過一個例子來說明採用FCFS和SPF排程演算法時的排程效能。現有五個程序A、B、C、D、E,它們到達的時間分別是0、1、2、3、4,所要求的服務時間分別是4、3、5、2 、4。

  通過FCFS和SPF排程演算法計算出來的結果如下的圖-3所示.


圖-3 通過FCFS和SPF演算法的排程過程

效能分析:

  從上面的結果中,我們不難計算出FCFS演算法的平均週轉時間為2.8,而SPF演算法的平均週轉時間只有2.1。

  程序D的週轉時間從11降到了3,帶權週轉時間也是從5.5降到了1.5.而對於程序C就要稍微差一點了。C的週轉時間從10升到了16,帶權週轉時間也從2升到了3.2。所以在短程序優先的排程演算法中,短程序得到了很好地照顧。

  因為SPF相對於FCFS平均的週轉時間降了很多。所以,一般情況下我們可以考慮使用SPF排程演算法來替代FCFS。不過,有時候還是要具體問題具體問題具體對待了。畢竟是FCFS也有相對SPF的優勢。

  關於FCFS和SPF的內容目前就到這裡了。下一篇會再來詳解一下“非搶佔式優先權演算法”和“搶佔式優先權排程演算法”。盡請期待。

GitHub原始碼下載: