07 系統排程
任務排程
在講完多執行緒併發之後,我們終於可以進入程式管理的最後一部分內容,任務排程。一些參考書上把這一部分內容叫做程式排程,我們之所以叫它任務排程,是因為在很多系統中、被排程的單位並不一定是程式。上一章開頭我們已經提到,Linux 系統中所有執行緒都是核心級執行緒,因此一個執行緒可以作為一個相對獨立的單位被排程器排程。我們將一個被排程的單位稱為任務(task),這一章中我們就來認識一下任務排程的常用演算法。
上下文切換
任務排程發生的情境——上下文切換
上下文切換是從一個任務向下一個任務切換的過程,它有可能由兩種情形觸發。
- 一種情形是任務沒有運行完,但主動讓出了處理器使用權;依賴這種形式觸發上下文切換的系統是 合作式多工系統(cooperativemultitasking)。
- 另一種情形是作業系統在任務運行一段時間後向程式發出中斷,然後在中斷處理器中選擇下一個運行的任務、切換至該程式。這種系統叫做 搶佔式多工系統(preemptive multitasking)。
我們所談到的任務排程是基於後一種多工系統,即作業系統必須擁有搶佔處理器的能力。
在搶佔式多工系統中,每個任務會被給予一段時間,我們將這段時間稱為 時間片(time slice)。
時間片耗盡後,系統會引發計時器中斷(timer interrupt),使得現在正在執行的任務被切換至核心態,在系統空間中還行計時器中斷的處理函式,我們所關心的任務排程演算法就發生在計時器中斷的處理函式中。
在這個處理函式中,系統會根據任務排程演算法、從就緒佇列裡選擇下一個執行的任務(有可能仍然是現在的這個任務),然後在處理上載入這個任務的處理器狀態、在儲存管理部件裡載入這個任務對應的頁表的起始地址、開始執行。
衡量任務排程演算法表現的量
為了使使用者獲得流暢的體驗,我們希望一個好的任務排程演算法能夠讓所有任務在一段時間內都產生一定的進度、不讓任何任務等待太久、又不浪費太多時間在演算法本身的計算中。為了能有效衡量一個演算法是否符合上面的標準,我們接下來要介紹幾個用來衡量任務排程演算法表現的量。
學完了一個理想排程演算法應符合的標準後,我們就可以開始學習各種排程演算法了。我們將在學習具體的演算法的過程中,在不同的情境下用這些標準來衡量排程演算法。你很快就會意識到,所有演算法都有其最優情境和最差情境。在選擇演算法時,我們也要根據不同的情境來選擇合適的演算法。
練習
SJF與SRTF
還記得在講頁面替換演算法時,我們講的第一個演算法就是最佳頁面替換演算法;雖然最佳頁面替換演算法要求我們能夠預知未來,但它可以被證明是在同一頁面使用序列下缺頁中斷率最低的演算法。這一節中我們要講的兩個演算法也是這樣——它們實際上很難被實現,但是它們能夠保證最短的平均週轉時間,因此我們在學習其它可以實現的演算法時就能以這兩個演算法為基準衡量其它演算法的表現。
-
最短作業優先演算法 SJF
-
最短時間優先演算法SRTF
從上面的例子我們可以看出,最短剩餘時間優先演算法是優於非搶佔式的最短作業優先演算法的。因此,我們在談到最短作業優先演算法時指的一般都是搶佔式最短作業優先演算法(最短剩餘時間優先演算法)。接下來我們就來證明下面這個結論:對於同一個任務流,最短剩餘時間優先演算法可以使所有任務的平均週轉時間達到最短。
我們可以將 SRTF 看成是一個動態的 SJF,它在每次有新任務進入系統時都重新執行 SJF。因此我們只要證明,對於一個固定的任務流(沒有新任務加入的任務流)SJF 的平均週轉時間是最短的,我們就可以證明 SRTF 對於一個有新任務加入的任務流的平均週轉時間是最短的。(因為我們在每一個時間點上都做了最佳的選擇,那麼我們整體的選擇也是最佳的)
下面我們就來證明 SJF 能夠針對一個固定的任務流給出最低的平均週轉時間。為了證明這個結論,我們先來證明一個輔助定理:穿插執行幾個任務比連續執行幾個任務所需時間更長。這個證明是很簡單的,因為穿插執行的幾個任務中後結束的任務的週轉時間一定大於等於它之間結束的幾個任務的執行時間與它的執行時間之和。因此為了減小週轉時間,我們希望上面的值取等,這就相當於要求所有的任務都連續執行至結束再切換至下一個任務執行。
從上面的證明中我們已經看出,SRTF 和 SJF 的優點就是它們能夠縮短任務平均週轉時間。但 SRTF 和 SJF 也有明顯的缺點——在短任務不斷進入系統的情況下,它們會導致執行時間長的任務不斷等待,產生飢餓。下一節中我們可以看到,為了減少任務的等待時間、提高公平性,我們可以採用一些別的演算法。
HRRF,FIFO 與 Round-Robin 排程演算法
-
最高響應比優先演算法(Highest Response Ratio First,HRRF)
先進先出演算法(First in First Out,FIFO),它又被稱為 先來先服務演算法(First Come First Served,FCFS)
它跟頁面替換演算法裡學習的先進先出演算法類似,使先進入系統的任務先執行至結束,然後再使下一個進入系統的任務執行。這種演算法不會使後進入系統的短任務耽誤先進入系統的長任務,因此解決了 SJF 和 SRTF 的問題,但 FIFO 也有自己的問題。
如果一個長任務先進入系統,後面有很多短任務進入系統,那麼這些短任務都需要等待長任務執行完之後才能執行,平均週轉時間會變得很長;另一方面,如果一個任務的大部分時間都花在 I/O 操作上,那麼 FIFO 這種非搶佔式的演算法會導致 CPU 長時間空閒,降低了資源利用率。
- 輪轉排程演算法(Round-Robin,RR)
我們可以看到 FIFO 和 SJF、SRTF 一樣都有公平性較差的問題,只不過 SJF 和 SRTF 偏向短任務,而 FIFO 偏向長任務。我們想要一個更公平的演算法,使得不同的任務在一段時間內都能產生進度,這就是 輪轉排程演算法(Round-Robin,RR)
在 RR 中,我們會規定一個固定長度的時間片。處於就緒佇列中的程式將按照他們進入就緒佇列的順序輪流獲得一個時間片執行,當時間片耗盡時任務就會被中斷、加入就緒佇列的結尾,下一個任務開始執行。如果一個任務因為 I/O 操作或無法獲取鎖等原因被阻塞,那麼即使它無法用完自己的時間片也會放棄處理器的使用權。
RR 與 SRTF 一樣,都是搶佔式演算法,它的優點是對於計算密集型的任務分配資源較為公平,且不會產生飢餓。然而,I/O 密集型的任務在這種演算法下會不斷因 I/O 放棄處理器使用權,因此獲取的資源較少。不僅如此,RR 演算法的平均週轉時間較長,這主要有兩個原因——第一,原本只需要幾個時間片就可以執行完的任務在 RR 中需要等待整個就緒佇列的任務都執行完一遍以後才能獲得一次時間片,因此等待時間很長;第二,每次時間片消耗完或任務主動讓出處理器時都會出現上下文切換,而上下文切換的代價是很大的,因此如果時間片太小、浪費在上下文切換上的時間比例就會很大。由於上述這種原因,在某些情況下 RR 的平均週轉時間可能比 FIFO 的平均週轉時間更長。
練習
多級反饋佇列排程演算法(Multi-Level Feedback Queue,MLFQ)
前幾節中我們講了 FIFO 和 RR 這兩種比較基本的演算法,它們各自都有最適宜(optimal)和最不適宜(pessimal)的任務流種類。為了避免某一種基本演算法在一些不適宜使用該演算法的任務流下表現很差,我們需要以這些基本演算法為基石、設計一些更綜合的演算法。這一節中我們就來講一個在很多系統中都得到應用的、比較綜合的演算法:多級反饋佇列排程演算法(Multi-Level Feedback Queue,MLFQ)
優先順序捐贈的過程看起來簡單,其實是很複雜的。比如,如果低優先順序執行緒 1 持有鎖 B,中優先順序執行緒 2 持有鎖 A,高優先順序執行緒 3 試圖獲得 A 失敗後將被阻塞,將自己的優先順序捐贈給執行緒 2;執行緒 2 執行後試圖獲得 B 失敗後也被阻塞,這時它就必須把執行緒 3 捐贈給它的優先順序和它自己的優先順序同時捐贈給執行緒 1,因為如果它只捐贈自己的優先順序,那麼執行緒 1 的優先順序仍然不一定是最高的。 我們把這種迴圈式的捐贈叫做 recursive donation。
還有一種情況我們必須考慮,那就是在一個執行緒持有多個鎖並釋放其中一個的時候,我們應該把它的優先順序降到什麼程度呢?我們要避免剩下幾個鎖產生優先順序倒置,就需要這個執行緒的優先順序不低於任何一個等待它的執行緒的優先順序,否則兩個優先順序之間就會出現能夠搶先執行的執行緒。因此我們應該把它的優先順序降到所有它持有的鎖的等待者中的最高優先等級。我們甚至可能面臨這樣一種情況:執行緒釋放的鎖不是把這個執行緒的優先順序提高到目前等級的執行緒所等待的鎖,在這種情況下,執行緒的優先順序就不會變化了。
雖然我們管這種提升優先順序的方式叫捐贈,但更好的考慮方法應該是一種“暫時提拔”。捐贈會給人一種捐多少就要還多少的感覺,但實際上捐贈時執行緒優先順序增加與它釋放鎖時優先順序減少的值並不一定相等(線上程持有多個鎖時很可能不相等)。對於這一部分內容的理解至關重要,在下一節中我們就來測試一下你對於這一部分內容的掌握程度,幫助你鞏固你的理解。
上一節中我們探討了優先順序倒置的情況和它的解決方法;在探討這個問題時我們故意避開了幾個知識點沒有討論,這一節裡就請你自己來思考一下下面幾個有關優先順序捐贈的說法哪兩個是正確的。
練習
Linux排程演算法
這種看似相同的執行時間片長度從比例上來講相差是很多的。我們想要一個更加公平、且普遍適用於各種型別的任務的演算法。
練習
多處理器排程
如你所知,現代的很多大型計算機都是多處理器的;在大的資料中心中(如谷歌的資料中心),一整個裝滿處理器和磁碟的倉庫可能是同一臺計算機(這種計算機被稱為 Warehouse-Scale Computer,WSC)。在這種情況下,一個作業系統的排程器就需要處理將工作分攤到多個處理器上的功能。你可能會問,為什麼不直接按照單處理器的排程演算法給空閒的處理器分配任務呢?接下來我們就來看看,按照這種思路排程任務會有什麼問題。
我們知道,一個任務不一定等於一個程式;一個執行緒也可以是一個任務。那麼,如果一個程式希望充分利用多處理器的資源,它就可以分出多個執行緒,並行地完成一段計算,完成這段計算後再合併執行緒或讓所有執行緒進入下一階段的計算。然而,在計算機中,由於系統把這幾個執行緒當成獨立的任務,系統可能把它們分開執行,這樣就會出現所有執行緒都需要等待最慢的那個執行緒完成才能進入下一階段的問題,並行對於運算速度的提升就大打折扣了。如果幾個執行緒中有一個獲得了一個鎖,然後它的處理器被搶佔了,那麼剩餘的執行緒即使獲得了處理器也只能等待鎖。如果這個鎖是自旋鎖的話,那麼所有這個程式下的執行緒都會在自旋中浪費處理器時間,導致其它程式也不能產生進度。
可見,這種盲目(oblivious)地利用單處理器的方法在多處理器計算機上排程任務的方法是不可行的。
排隊論
我們要區分等待和執行這兩部分,因為我們使用排隊論的目的就是分析一個任務在等待和執行這兩個部分分別花費的時間長度。前面幾節中我們已經說過,一個任務的週轉時間等於它的等待時間與執行時間之和,假設其執行時間是不變的,那麼它的等待時間就成為了限制系統表現的主要問題。如果一個系統中執行的部分時間較短、而等待時間很長,那麼我們就應該考慮換一種排程演算法或者多買幾個處理器;如果一個系統中執行部分時間和等待時間都很長,那我們就應該考慮,等待時間過長可能是由系統處理速度慢於任務進入系統的速度導致的,那我們就有必要改一改現在執行所使用的演算法或買一個更快的處理器了。
練習
我們現在有一個系統,每秒鐘可以處理 100 個請求,每個請求的處理時間(不包括排隊時間)是 100ms,,下面有關這個系統的說法正確的兩個是?
相關文章
- 排程系統設計精要
- APS高階計劃排程系統和生產排產系統
- Android系統“資源排程框架”Android框架
- Schedule 排程系統設計(單機版)
- 利用Mesos構建多工排程系統
- iOS系統資源排程機制解析iOS
- 論銀行的排程系統和ETL
- Yarp 讓系統內排程更靈活
- 作業系統之排程演算法作業系統演算法
- 詳解BI系統中的任務排程
- 作業系統精髓設計原理 程式排程作業系統
- 開源公開課丨大資料排程系統 Taier 任務排程介紹大資料AI
- Flink排程之排程器、排程策略、排程模式模式
- 系統架構設計之-任務排程系統的設計架構
- 進擊的 Kubernetes 排程系統(一):Kubernetes scheduling frameworkFramework
- 【作業系統】4.程序排程演算法作業系統演算法
- 技術分享| 快對講綜合排程系統
- 解決方案| 快對講綜合排程系統
- 分散式任務排程系統設計小結分散式
- 作業系統4——處理機排程與死鎖作業系統
- Golang原始碼學習:排程邏輯(四)系統呼叫Golang原始碼
- OPPO大資料離線任務排程系統OFLOW大資料
- 美團叢集排程系統的雲原生實踐
- 猿考研之作業系統篇二(處理機排程)作業系統
- 開源分散式任務排程系統就選:DolphinScheduler分散式
- 解決方案| 快對講排程系統:高效協作
- 技術分享| 快對講排程系統設計概要
- 【Java】基礎_14_開發團隊排程系統Java
- 智慧公安雪亮工程人像監控系統開發指揮排程系統開發
- 課程排課系統:智慧排課+線上約課+直播上課+作業打卡!
- 3分鐘學會如何排程運營海量Redis系統Redis
- 實現一個分散式排程系統-LoadBalance和Ha策略分散式
- 公安一體化指揮排程平臺建設解決方案,應急指揮排程系統開發
- Kubernetes 編排系統
- Go runtime 排程器精講(九):系統呼叫引起的搶佔Go
- DAG任務排程系統 Taier 演進之道,探究DataSourceX 模組AI
- vivo 自研Jenkins資源排程系統設計與實踐Jenkins
- 基於雲服務MRS構建DolphinScheduler2排程系統