進程調度程序是多任務操做系統的基礎,它是確保進程能有效工做的一個內核子系統,負責決定哪一個進程投入運行、什麼時候運行以及運行多長時間。只有經過進程調度程序的合理調度,系統資源纔可以最大限度地發揮做用,多進程纔會有併發執行的效果。在一組處於可運行狀態的進程中選擇一個來執行,是調度程序所需完成的基本工做。linux
在前期的 Linux 版本中,Linux 的調度程序都至關簡陋,設計近乎原始,雖然容易理解,可是在可運行進程多或者多處理器的環境下都難以勝任。在 Linux2.5 系列內核中,開始採用一種叫作 O(1) 調度程序的新調度程序,解決了先前版本 Linux 調度程序的許多不足。在 Linux2.6 版本內核開發過程當中,又引入了新的進程調度算法,其中最爲出名的是 RSDL 算法,該算法引入了公平調度的概念,隨後演化爲 CFS:徹底公平調度算法。算法
Linux 中,與進程調度相關的信息存放在進程描述符 task_struct 中:網絡
struct task_struct { .... int prio, static_prio, normal_prio; // 進程優先級 unsigned int rt_priority; const struct sched_class *sched_class; // 調度器類 struct sched_entity se; // 調度實體 struct sched_rt_entity rt; // 實時進程的調度實體 unsigned int policy; // 調度策略 cpumask_t cpus_allowed; // 用於控制進程能夠在哪一個處理器上運行 .... }
1、多任務操做系統併發
多任務操做系統就是可以同時併發地交互執行多個進程的操做系統。能夠分爲兩類:非搶佔式多任務系統和搶佔式多任務系統。app
1.一、非搶佔式多任務系統:模塊化
在非搶佔式多任務系統下,除非進程本身主動中止運行,不然它會一直執行下去。進程主動掛起本身的操做稱爲讓步。理想狀況下,進程一般會作出讓步,以便讓每一個可運行進程享有足夠的處理器時間。這種機制有不少缺點:調度程序沒法對每一個進程該執行多長時間作出統一規定,因此進程獨佔的處理器時間可能會很長;更糟的是,若是一個進程一直不做出讓步,會使系統崩潰。如今的大多數系統都採用搶佔式多任務模式,除了 MAC 0S 9(及其前身)以及 Windows 3.1(及其前身)。性能
1.二、搶佔式多任務系統:優化
搶佔式多任務系統下,由調度程序來決定何時中止一個程序的運行,以便其餘進程可以獲得執行機會,這個強制的掛起動做稱爲搶佔。被搶佔的進程仍然是處於 TASK_RUNNING 狀態的,只是暫時沒有被CPU運行。進程的搶佔發生在進程處於用戶態執行階段,在內核態執行時是不能被搶佔的。 Linux 是搶佔式多任務系統。ui
2、調度策略url
調度策略決定調度程序在什麼時候讓什麼進程運行,調度策略要根據多方因素擬定,以便讓系統資源獲得最大限度的利用。
2.一、I/O 消耗型(交互式進程)和處理器消耗型進程(批處理進程)
進程大體能夠被分爲 I/O 消耗型和處理器消耗型(還能夠既是 I/O 消耗型也是處理器消耗型):
I/O消耗型進程的大部分時間用來提交 I/O 請求或是等待 I/O 請求。這樣的進程常常處於可運行狀態,但一般運行時間都很短,由於它在等待更多的 I/O 請求時最後總會阻塞(這裏的 I/O 是指任何類型的可阻塞資源,如鍵盤輸入、網絡I/O)。
處理器消耗型進程則是把大部分時間用在執行代碼上,除非被搶佔,不然它們一般都一直不停地運行,由於它們沒有太多的 I/O 需求。對於這類進程,調度策略每每是下降它們的調度頻率,而延長其運行時間。一些須要進行大量計算工做的程序(如 MATLAB)就是處理器消耗型進程。
調度策略一般要在兩個矛盾的目標中間尋找平衡:進程響應迅速和最大系統利用率。爲此,調度程序一般採用一套很是複雜的算法來決定最值得運行的進程投入運行,但它每每並不保證低優先級進程會被公平對待。Linux 爲了保證交互式應用和桌面系統的性能,對進程的響應作了優化,更傾向於優先調用 I/O 消耗型。
2.二、進程優先級
調度算法中最基本的一類就是基於優先級的調度。一般的作法是(Linux並未徹底採用),優先級高的進程先運行,低的後運行,相同優先級的進程按輪轉方式進行調度。調度程序老是選擇時間片未用盡而優先級最高的進程先運行。用戶和系統均可以經過設置進程的優先級來影響系統的調度。
Linux 採用了兩種不一樣的優先級範圍:
1)nice值:
nice值是全部 UNIX 系統中的標準化概念,可是不一樣的 UNIX 系統因爲調度算法不一樣,nice 值的運用方式也有所差別。 nice 值的範圍是從 -20 到 +19 ,默認值爲 0;越大的 nice 值意味着更低的優先級,相比高 nice 值(低優先級)的進程,低 nice 值(高優先級)的進程能夠得到更多的處理器時間。
在 Linux 系統中,nice 值表明時間片的比例。使用如下指令能夠查看進程的 nice 值:
ps -eo uid,pid,nice
其中,nice 選項就是查看進程的 nice 值,在輸出結果中,NI 那一列即爲進程的 nice 值。
2)實時優先級:
實時優先級的值是能夠配置的,默認狀況下它的變化範圍是從 0 到 99(包括0 和 99)。與 nice 值相反,實時優先級值越高意味着進程優先級越高,任何實時進程的優先級都高於普通的進程(即實時優先級和 nice 優先級處於兩個互不相交的範疇)。可使用以下指令查看進程的實時優先級:
ps -eo uid,pid,rtprio
其中,rtprio 選項就是進程的實時優先級,在輸出結果中, RTPRIO那一列就是。若是有進程對應列顯示爲「-」,則說明該進程不是實時進程。
2.三、時間片 與 處理器使用比
在 UNIX 系統中,進程在被搶佔以前可以運行的時間是預先設置好的,被稱爲時間片。時間片是一個數值,它代表進程在被搶佔前所能持續運行的時間。調度策略必須規定一個默認的時間片,既不能太長,也不能過短:時間片太長,則系統交互性較差;時間片過短,則會增大進程切換帶來的處理器耗時。通常而言,系統的默認時間片很短,好比10ms。
可是,Linux 的 CFS 調度器並無直接分配時間片到進程,Linux 使用的是處理器使用比的方式,將處理器使用比劃分給進程,這樣一來,進程所得到的處理器時間實際上是和系統負載密切相關的。
處理器使用比受 nice 值影響,具備較高 nice 值的進程將得到較低的處理器使用比;具備較低 nice 值的進程將得到較高的處理器使用比。而 CFS 調度器,則會根據進程的處理器使用比來決定搶佔的時機:若是消耗的使用比比當前進程小,則新進程馬上投入運行,搶佔當前進程;不然,將推遲其運行。經過擯棄時間片而是分配給進程一個處理器使用比的方式,CFS 確保了進程調度中能有恆定的公平性,而將切換頻率置於不斷的變更中。
3、Linux 調度算法
3.一、調度器類
Linux 以模塊的方式提供調度器,這樣作的目的是容許不一樣類型的進程能夠有針對性地選擇調度算法。這種模塊化結構被稱爲調度器類,它容許多種不一樣的可動態添加的調度算法並存,調度屬於本身範疇的進程。每一個調度器都有一個優先級,基礎的調度器代碼定義在 kernel/sched.c 文件中,它會按照優先級順序遍歷調度類,擁有一個可執行進程的最高優先級的調度器類勝出,去選擇下面要執行的那一個程序。
在 linux 2.6.34 版本內核裏,有三種調度器類:idle_sched_class、rt_sched_class 和 fail_sched_class,在最新的 linux 4.6版本里,已經增長到了五種,另外兩種是 stop_sched_class 和 dl_sched_class:
調度器類 | 描述 | 對應調度策略 |
stop_sched_class | 優先級最高的線程,會中斷全部其餘線程,且不會被其餘任務打斷 | 無,不須要調度普通進程 |
dl_sched_class | 採用 EDF 最先截止時間優先算法調度實時進程 | SCHED_DEADLINE |
rt_sched_class | 採用提供 Roound_Robin 算法或者 FIFO 算法調度實時進程,具體調度策略由進程的 task_struct -> policy 指定 | SCHED_FIFO, SCHED_RR |
fair_sched_class | 採用 CFS 算法調度普通的非實時進程 | SCHED_NORMAL, SCHED_BATCH |
idle_sched_class | 每一個 CPU 的第一個pid=0線程:swapper,是一個靜態線程;通常運行在開機過程和 CPU 異常的時候作 dump | SCHED_IDLE |
能夠看到,五種調度器類共有六種調度策略(即調度算法),用於對不一樣類型的進程進行調度,或者支持某些特殊的功能。每一個調度器都有一個優先級,這五個調度器的優先級順序爲:
stop_sched_class > dl_sched_class > rt_sched_class > fair_sched_class > idle_sched_class
另外,關於所對應的調度策略,在內核版本 2.6.34 中有以下定義:
/* * Scheduling policies */ #define SCHED_NORMAL 0 #define SCHED_FIFO 1 #define SCHED_RR 2 #define SCHED_BATCH 3 /* SCHED_ISO: reserved but not implemented yet */ #define SCHED_IDLE 5 /* Can be ORed in to make sure the process is reverted back to SCHED_NORMAL on fork */ #define SCHED_RESET_ON_FORK 0x40000000
SCHED_DEADLINE 在最新版本中有定義,暫不作敘述。上面的五種調度策略中, SCHED_NORMAL 和 SCHED_BATCH 是針對普通進程(非實時進程)的調度策略,都是經過 CFS 調度器來實現的。更進一步劃分的話,SCHED_NORMAL 是針對交互式進程的調度策略;SCHED_BATCH 是針對批處理進程的調度策略。SCHED_FIFO 和 SCHED_RR 是針對實時進程的調度策略,稍後會再作細說。 SCHED_IDLE 的優先級最低,在系統空閒時才運行這類進程。
鑑於筆者所看的內核版本以及能力問題,暫時就先簡單討論一下實時調度器和徹底公平調度器這兩種。實際上,大部分進程也都是屬於實時調度器和徹底公平調度器的。
3.二、徹底公平調度
徹底公平調度(CFS)是一個針對普通進程的調度類,在 Linux 中被稱爲 SCHED_NORMAL(也被稱做 SCHED_OTHER),CFS 算法實現定義在文件 kernel/sched_fair.c 中。
CFS 的出發點基於這樣一個理念:進程調度的效果應該能讓系統感受像是具有一個理想中的完美多任務處理器。在完美多任務處理器下,每一個進程將能得到 1/n 的處理器時間(n 指的是可運行進程的數量)。同時,咱們能夠調度給它們無限小的時間週期,因此,在任何可測量週期內,咱們給予 n 個進程中每一個進程一樣多的運行時間。可是,這種模型是不現實的,首先,咱們沒法在一個處理器上真的同時運行多個進程,其次,每一個進程的運行時間過小的話,將會增大進程切換帶來的額外消耗,這是不高效的。
CFS 的作法是容許每一個進程運行一段時間、循環輪轉、選擇運行最少的進程做爲下一個運行進程,而不是採用分配給每一個進程時間片的作法;CFS 在全部可運行進程總數基礎上計算出一個進程應該運行多久,而不是依靠 nice 值來計算時間片。每一個進程都按其權重在所有可運行進程中所佔比例的「時間片」來運行,爲了計算準確的時間片,CFS 爲完美多任務中的無限小調度週期的近似值設立了一個目標,稱爲「目標延遲」,目標延遲越小,則系統的交互性越好,同時也更接近完美的多任務;與之相對的,是更高的切換代價和更差的系統總吞吐能力。同時,爲了不可運行進程過多而致使每一個進程所得到的處理器使用比過小帶來的鉅額切換消耗,CFS 引入了最小粒度的概念,用來表示每一個進程所能得到的時間片的底線,默認狀況下,最小粒度爲 1ms。
在 CFS 策略下,任何進程所得到的處理器時間是由它本身和其餘全部可運行進程 nice 值的相對差值決定的,nice 值對時間片的做用再也不是算數加權,而是幾何加權。所對應的也再也不是一個絕對的時間片,而是處理器的使用比。CFS 經過保證分配每一個進程的處理器使用比的公平性,從而達到公平調度。固然,CFS 也不是完美的公平,而是近乎完美的多任務,可是確實在必定程度上提高了進程調度公平性。
3.三、實時調度
實時調度是針對實時進程的調度類,Linux 提供了兩種實時調度策略:SCHED_FIFO 和 SCHED_RR。
SCHED_FIFO 實現了一種簡單的、先入先出的算法:它不使用時間片。處於可運行狀態的 SCHED_FIFO 級的進程回比任何 SCHED_NORMAL 級的進程都先獲得調度。一旦一個 SCHED_FIFO 進程處於可執行狀態,它就會一直執行,知道它本身受阻塞或顯式的釋放處理器爲止。它不基於時間片,能夠一直執行下去。只有更高優先級的 SCHED_FIFO 或者 SCHED_RR 任務才能搶佔 SCHED_FIFO 任務。若是有兩個或更多同優先級的 SCHED_FIFO 級進程,它們會輪流執行,可是依然只有在它們願意讓出處理器時纔會退出。只要有 SCHED_FIFO 級進程在執行,其餘級別較低的進程就只有等待它變爲不可運行狀態後纔有機會執行。
SCHED_RR 與 SCHED_FIFO 大致相同,只是 SCHED_RR 級的進程在耗盡事先分配給它的時間後就不在繼續執行了。也就是說,SCHED_RR 是帶有時間片的 SCHED_FIFO —— 這是一種實時輪流調度算法。當 SCHED_RR 任務耗盡它的時間片時,處於同一優先級的其餘實時進程被輪流調度。時間片只用來從新調度同一優先級的進程。對於 SCHED_FIFO 進程,高優先級老是當即搶佔低優先級,可是低優先級進程決不能搶佔 SCHED_RR 任務,即便它的時間片耗盡。
內核版本:2.6.34
參考:https://blog.csdn.net/gatieme/article/details/51702662
參考書籍:Linux內核設計與實現(第3版)