1. 程式人生 > 實用技巧 >《作業系統導論》三、程序排程

《作業系統導論》三、程序排程

7-程序排程:介紹

關鍵問題:如何開發排程策略

我們該如何開發一個考慮排程策略的基本框架?什麼是關鍵假設?哪些指標非常重要?哪些基本方法已經在早期的系統中使用?

  • 工作負載

  • 排程指標:
    週轉時間:定義為任務完成時間減去任務到達系統的時間(是一個性能指標)。

    公平
    效能和公平在排程系統中往往是矛盾的。
    響應時間

下面的分析基於以下假設,然後依次放開相關假設:
1、每一個工作執行相同的時間。
2、所有的工作同時到達。
3、一旦開始,每個工作保持執行直到完成。
4、所有的工作只是用CPU(即它們不執行IO操作)。
5、每個工作的執行時間是已知的。

先進先出(FIFO)

現在假設(T到達時間 = 0),3個工作A、B、C,假設同時到達;

1、每個工作執行時間10s,那麼這些工作的平均週期時間為:
在這裡插入圖片描述

A在10s時完成,B在20s時,C在30s時。因此,平均週轉時間為(10+20+30)/3 = 20s

2、假設A執行100s,B和C執行10s
在這裡插入圖片描述

系統的平均週轉時間為:(100+110+120)/3 = 110s

提示:SJF原則

最短任務優先代表一個總體排程原則,可以應用於所有系統,只要其中平均客戶(或在我們案例中的任務)週轉時間很重要。

最短任務優先(SJF)

先執行最短的任務,然後是次短的任務,如此下去。
在這裡插入圖片描述

上面的例子採用SJF作為策略,則平均週轉時間為(10+20+120)/3 = 50s
事實上,考慮到所有工作同時到達的假設,我們可以證明SJF確實是一個最優(optimal)排程演算法。

補充:搶佔式排程程式

在過去的批處理計算中,開發了一些非搶佔式(non-preemptive)排程程式。這樣的系統會將每項工作做完,再考慮是否執行新工作。
幾乎所有現代化的排程程式都是搶佔式的,非常願意停止一個程序以執行另一個程序。這意味著排程程式採用了我們之前學習的機制。特別是排程程式可以進行上下文切換,臨時停止一個執行程序,並恢復(或啟動)另一個程序。

假設A在t=0時到達,且需要執行100s,而B和C在t=10到達,則各需要執行10s。用純SJF,我們將會得到:
在這裡插入圖片描述

平均週轉時間為(100 + (110-10) + (120-10))/3 = 103.33s

最短完成時間優先(STCF)

向SJF新增搶佔,稱為最短完成時間優先(Shortest Time-to-Completion First, STCF)或搶佔式最短作業優先(Preemptive Shortest Job First,PSJF)排程程式。

在這裡插入圖片描述

平均週轉時間
(120 + (20 - 10) + (30 - 10))/3 = 50s

新度量指標:響應時間

響應時間定義為從任務到達系統到首次執行的時間,更正式的定義是:

在這裡插入圖片描述

例如上面的排程(A在時間0到達,B和C在時間10到達)。每個作業的響應時間如下:作業A為0,B為0,C為19(平均為:3.33).

STCF和相關方法在響應時間上並不是很好。例如,如果3個工作同時到達,第三個工作必須等待前兩個工作全部執行後才能執行。這種方法雖然由很好的週轉時間,但對於響應時間和互動性是相當糟糕的。

輪轉

新的排程演算法:輪轉(Round-Robin)排程。
RR在一個時間片(time slice,又是稱為排程量子,scheduling quantum)內執行一個工作,然後切換到執行佇列中的下一個任務,而不是執行一個任務直到結束。它反覆執行,直到所有任務完成。
假設ABC三個任務同時到達,均執行5s。1s的時間片
在這裡插入圖片描述

RR的平均響應時間是:(0+1+2)/3 =1;
SJF演算法的平均響應時間是:(0+5+10)/3 = 5.

時間片長度對RR至關重要。越短,響應時間越小。然而時間片太短會造成上下文切換的成本增加,影響整體效能.

注意,上下文切換的成本不僅僅來自儲存和恢復少量暫存器的作業系統操作。程式執行時,它們在CPU高速緩衝、TLB、分支預測器和其他片上硬體中建立了大量的狀態。切換到另一個工作會導致此狀態被重新整理,且與當前執行的作業相關的新狀態被引入,這可能導致顯著的效能成本。

上面的例子中,平均週轉時間將會變成14.
任何公平的策略(如RR),即在小規模的時間內將CPU均勻的分配到活動程序之間,在週轉時間這類指標上表現不佳。事實上,這是固有的權衡:如果你願意不公平,你可以執行較短的工作直到完成,但是要以響應時間為代價。如果你重視公平性,則響應時間會較短,但會以週期時間為代價。

結合I/O

接下來,我們放開沒有I/O操作的假設。

重疊可以提高利用率

如有可能,重疊(overlap)操作可以最大限度地提高系統的利用率。重疊在許多不同的領域很有用,包括執行磁碟I/O或將訊息傳送到遠端機器時。在任何一種情況下,開始操作然後切換到其他工作都是一個好主意,這也提高了系統的整體利用率和效率。

無法預知

最後的假設:排程程式知道每個工作的長度。
這可能是可以做出的最糟糕的假設
事實上,在一個通用的作業系統中,作業系統通常對每個作業的長度知之甚少。
因此,我們如何建立一個沒有這種先驗知識的SJF/STCF?更進一步,我們如何能夠將已經看到的一些想法與RR排程程式結合起來,以便響應時間也變得相當不錯?

8-排程:多級反饋佇列

多級反饋佇列(Multi-level Feedback Queue, MLFQ)需要解決兩個問題:

  • 優化週轉時間(依賴於知道任務需要的執行時間)
  • MLFQ希望給互動使用者很好的互動體驗,因此需要降低響應時間

關鍵問題:沒有完備的知識如何排程?

沒有工作長度的先驗(priori)知識,如何設計一個能同時減少響應時間和週轉時間的排程程式?

提示:從歷史中學習

多級反饋佇列是用歷史經驗預測未來的一個典型的例子,作業系統中有很多地方採用了這種技術(同樣存在於電腦科學領域的很多其他地方,比如硬體的分支預測及緩衝演算法)。如果工作有明顯的階段性行為,因此可以預測,那麼這種方式會很有效。當然,必須十分小心地使用這種技術,因為它可能出錯,讓系統做出比一無所知的時候更糟的決定。

MLFQ:基本規則

MLFQ中有許多獨立的佇列(queue),每個佇列有不同的優先順序(priority level)。任何時刻,一個工作只能存在於一個佇列中。MLFQ總是優先執行較高優先順序的工作(即在較高佇列中的工作)。

當然,每個佇列中可能會有多個工作,因此具有同樣的優先順序。在這種情況下,我們就對這些工作採用輪轉排程。

MLFQ排程策略的關鍵在於如何設定優先順序?

MLFQ沒有為每個工作指定不變的優先順序,而是根據觀察到的行為調整它的優先順序。例如,如果一個工作不斷放棄CPU去等待鍵盤輸入,這是互動型程序的可能行為,MLFQ因此會讓它保持高優先順序。相反,如果一個工作長時間地佔用CPU,MLFQ會降低其優先順序。通過這種方式,MLFQ在程序執行過程中學習其行為,從而利用工作的歷史來預測它未來的行為。

至此,我們得到了MLFQ的兩條基本規則:
規則1:如果A的優先順序 > B的優先順序, 執行A
規則2:如果A的優先順序 = B的優先順序,輪轉執行A和B

但是存在問題:當優先順序高的任務一直執行,可能造成低優先順序的任務永遠都沒有機會執行.

嘗試1:如何改變優先順序

嘗試增加規則調整優先順序
規則3:工作進入系統時,放在最高優先順序(最上層佇列)
規則4a:工作用完整個時間片後,降低優先順序(移入下一個佇列)
規則4b:如果工作在其時間片以內主動釋放CPU,則優先順序不變。

例項1:
如果系統有一個需要長時間執行的工作,則隨著時間的推移,優先順序不斷下降。
在這裡插入圖片描述

例項2:
有兩個工作:A是一個長時間執行的CPU密集型工作,B是一個執行時間很短的互動型工作。假設A執行一段時間後B到達。
在這裡插入圖片描述

通過這個例子,你大概可以體會到這個演算法的一個主要目標:如果不知道工作是短工作還是長工作,那麼就在開始的時候假設其是短工作,並賦予最高優先順序。 類似於SJF

例項3:如果有I/O呢?
根據4b規則,互動型工作中有大量的I/O操作,它會在時間片用完之前放棄CPU。這種情況下保持它的優先順序不變。

互動型工作B每執行1ms便進行I/O操作,A為長時間執行的工作。
在這裡插入圖片描述

至此,MLFQ看起來似乎相當不錯了。但是**存在一些嚴重的缺點:飢餓(starvation)問題。**如果系統有"太多"互動型工作,就會不斷佔用CPU,導致長工作永遠無法得到CPU。

聰明的使用者會重寫程式,愚弄排程程式(game the scheduler)。愚弄排程程式指的是用一些卑鄙的手段欺騙排程程式,讓它給你超公平的資源。上述演算法對如下的攻擊束手無策:程序在時間片用完之前,呼叫一個I/O操作(比如訪問一個無關的檔案),從而主動釋放CPU。如此便可以保持在高優先順序,佔用更多的CPU時間。做得好時(比如,每執行99%的時間片時間就主動放棄依次CPU),工作可以幾乎獨佔CPU.

另一個問題:一個程式在不同的時間表現不同。一個密集的程序可能在某段時間表現為一個互動型的程序。

嘗試2:提升優先順序

規則5:經過一段時間S,就將系統中所有工作重新加入最高優先順序佇列。

新規則一下解決兩個問題:
程序不會餓死;
如果一個CPU密集型工作變成互動型,當它優先順序提升時,排程程式會正確對待它。

例子:避免餓死

S的設定:如果S設定得太高,長時間會飢餓;如果設定太低,互動型工作又得不到合適的CPU時間比例。

嘗試3:更好的即使方式

如何阻止排程程式被愚弄?

解決方案:為MLFQ的每層佇列提供更完善的CPU計時方式(accounting)。排程程式應該記錄一個程序在某一層中消耗的總時間,而不是在排程時重新計時。只要程序用完了自己的配額,就將它降低到低一優先順序的佇列中取。不論它是一次用完的,還是拆成很多次用完。

重寫規則4a和4b:
規則4:一旦工作用完了其在某一層中的時間配額(無論中間主動放棄了多少次CPU),就降低其優先順序(移入低一級佇列)

在這裡插入圖片描述

MLFQ調優及其他問題

其中一個大問題是如何配置一個排程程式:
配置多少佇列?每一層佇列的時間片配置多大?多久提升一次程序的優先順序?
這些問題都沒有顯而易見的答案,因此只有利用對工作負載的經驗,以及後續對排程程式的調優,才會導致令人滿意的平衡。

大多數的MLFQ變體都支援不同佇列可變的時間片長度。高優先順序佇列通常只有較短的時間片,因而這一層的互動工作可以更快地切換。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-Lln5xxof-1602398797972)(CA743426690641138A5C52A84C4939DD)]

MLFQ有趣的原因是:它不需要對工作的執行方式有先驗知識,而是通過觀察工作的執行來給出對應的優先順序。通過這種方式,MLFQ可以同時滿足各種工作的需求:對於短時間執行的互動型工作,獲得類似於SJF/STCF的很好的全域性效能,同時對長時間執行的CPU密集型負載也可以公平地、不斷地穩步向前。因此,許多系統使用某種型別的MLFQ作為自己的基礎排程程式,包括類BSD UNIX系統[LM+89,B86]、Solaris[M06]以及Windows NT和其後的Window系列作業系統。

9-排程:比例份額

比例排程(proportional-share)排程程式,也稱為公平排程(fair-share)排程程式。比例份額演算法基於一個簡單的想法:排程程式的最終目標,是確保每個工作獲得一定比例的CPU時間,而不是優化週轉時間和響應時間。

彩票排程(lottery scheduling):每隔一段時間,都會舉行一次彩票抽獎。以確定接下來應該執行哪個程序。

通過不斷定時地抽取彩票,彩票排程從概率上獲得這種份額比例。

例如:排程程式知道總共的彩票數(假設100)。排程程式抽取099之間的一個數,我們希望A佔用75%的CPU時間,B佔用25%。那麼程序A擁有074共75張彩票,B擁有75~99的25張。

關鍵問題:如何按比例分配CPU

如何設計排程程式來按比例分配CPU?其關鍵的機制是什麼?效率如何?

彩票數表示份額

彩票數(ticket)代表了程序(或使用者或其他)佔有某個資源的份額。

提示:利用隨機性

彩票排程最精彩的地方在於利用了隨機性。
隨機方法相對於傳統的決策方式,至少3點優勢。
第一,隨機方法常常可以避免奇怪的邊角情況.
第二,隨機方法很輕量,幾乎不需要記錄任何狀態。在傳統的公平份額排程演算法中,記錄每隔程序已經獲得了多少的CPU時間,需要對每個程序進行計時,這必須在每次執行結束後更新。而採用隨機方式後每隔程序只需要非常少的狀態(即每個程序擁有的彩票號碼)。
隨機方法很快。只要能很快的產生隨機數,做出決策就很快。

彩票機制

彩票排程還提供了一些機制,以不同且有效的方式來排程彩票。

一種方式是利用彩票貨幣(ticket currency)的概念。這種方式允許擁有一組彩票的使用者以他們喜歡的某種貨幣,將彩票分給自己的不同工作。之後作業系統再自動將這種貨幣兌換為正確的全域性彩票。
在這裡插入圖片描述

另一個有用的機制是彩票轉讓(ticket transfer)。通過轉讓,一個程序可以臨時將自己的彩票交給另一個程序。這種機制在客戶端/服務端互動的場景中尤其有用,在這種場景中,客戶端程序向服務端傳送訊息,請求其按照自己的需求執行工作,為了加速服務端的執行,客戶端可以將自己的彩票轉讓給服務端,從而儘可能加速服務端執行自己請求的速度。服務端執行結束後將這部分彩票歸還給客戶端。

最後,彩票膨脹(ticket inflation)有時也很有用。利用膨脹,一個程序可以臨時提升或降低自己擁有的彩票數量。當然在競爭環境中,程序之間互相不信任,這種機制就沒什麼意義。一個貪婪的程序可能給自己非常多的彩票,從而接管機器。但是,通脹可以用於程序之間相互信任的環境。在這種情況下,如果一個程序知道它需要更多CPU的時間,就可以增加自己的彩票,從而將自己的需求告知作業系統,這一切不需要與其他程序通訊。

實現

彩票排程只需要一個不錯的隨機數生成器來選擇中獎彩票和一個記錄系統中所有程序的資料結構(一個列表),以及所有彩票的總數。

如何分配彩票

關於彩票排程,還有一個問題,那就是如何為工作分配彩票?系統的執行嚴重依賴於彩票的分配。

步長排程

步長排程(strude scheduling),一個確定性的公平分配演算法。
假設A,B,C這3個工作的票數分佈為100,50,250;
使用10000除以這些票數,作為3個程序的步長:100,200,40;在執行過程中,執行一次時間片,累加歷程,下次選取里程最小的工作執行。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-EsdAXaoQ-1602398797977)(6F6F367F114948EB9EED8C5D5CE2CC08)]

既然有了可以精確控制的步長排程演算法,為什麼還要彩票排程演算法呢?
彩票排程有一個步長排程沒有的優勢—不需要全域性狀態。假設一個新的程序在上面的步長排程執行過程中加入系統,應該怎麼設定它的行程值呢?設定成0的話,它就獨佔CPU了。而彩票排程演算法不需要對每個程序記錄全域性狀態,只需要用新程序的票數更新全域性的總票數就可以了。因此彩票排程演算法能夠更合理地處理新加入的程序。

本章介紹的彩票排程和步長排程都沒有作為CPU排程程式被廣泛使用,一個原因是這兩種方式都不能很好地適合I/O;另一個原因是其中最難的票數分配問題並沒有確定的解決方式。