進程調度就是讓進程從一種狀態切換到另外一種狀態。Linux中進程的主要狀態以下,html
值 | 狀態 | 縮寫 | 含義 |
---|---|---|---|
0 | TASK_RUNNING | R | 正在運行或可運行 |
1 | TASK_INTERRUPTIBLE | S | 可中斷的休眠 |
2 | TASK_UNINTERRUPTIBLE | D | 不可中斷的休眠 |
4 | __TASK_STOPPED | T | 中止狀態,當進程接收到SIGSTOP等signal信息 |
8 | __TASK_TRACED | t | 跟蹤狀態,進程被debugger程序暫停,好比使用ptrace()調試 |
16 | EXIT_ZOMBIE | Z | 殭屍狀態,進程結束時調用do_exit()先進入殭屍狀體 |
32 | EXIT_DEAD | X | 死亡狀態,父進程使用waitpid()或wait4()回收死亡的子進程後,狀態由EXIT_ZOMBIE轉換爲EXIT_DEAD |
下圖粗略的展現了進程狀態的切換,能夠更加直觀的理解各類進程狀態。算法
上圖中須要補充說明的是,進程正在」佔有CPU執行「時應該處於TASK_RUNNING狀態。EXIT_ZOMBIE狀態應該是很短暫的,父進程須要使用wait類系統調用回收子進程,回收後進程狀態就轉變爲EXIT_DEAD,若是父進程一直不回收,子進程就變爲殭屍進程。__TASK_TRACED狀態只有在使用debugger是纔會出現,例如使用gdb設置斷點,進程在斷點上暫停時就處於跟蹤狀態。緩存
系統中包含許多進程,內核有責任讓全部進程都獲得運行,並讓重要的進程得到更多的運行。這就須要一種機制管理全部的進程,在內核中安排進程執行的模塊被稱爲調度器(scheduler)。數據結構
因此,調度器將解決兩個核心的問題:分配合適的時間片和合理安排進程執行順序。最原始的調度策略是按照優先級排列好進程,等到一個進程運行完了再運行優先級較低的一個,但這種策略徹底沒法發揮多任務系統的優點。所以,隨着時間推移,操做系統的調度器也屢次進化。app
Linux 2.4內核推出了O(n)調度器,O(n)調度器把時間分紅大量的微小時間片(Epoch)。在每一個時間片開始的時候,調度器會檢查全部處在就緒狀態的進程。調度器計算每一個進程的優先級,而後選擇優先級最高的進程來執行。一旦被調度器切換到執行,進程能夠不被打擾地用盡這個時間片。若是進程沒有用盡時間片,那麼該時間片的剩餘時間會增長到下一個時間片中。O(n)調度器在每次使用時間片前都要檢查全部就緒進程的優先級。這個檢查時間和進程中進程數目n成正比,這也正是該調度器複雜度爲O(n)的緣由。當計算機中有大量進程在運行時,這個調度器的性能將會被大大下降。函數
爲了解決O(n)調度器的性能問題,O(1)調度器被髮明瞭出來,並從Linux 2.6內核開始使用。O(1)調度器的創新之處在於,它會把進程按照優先級排好,放入特定的數據結構中。在選擇下一個要執行的進程時,調度器不用遍歷進程,就能夠直接選擇優先級最高的進程。O(1)調度器會用兩個隊列來存放進程。一個隊列稱爲活躍隊列,用於存儲那些待分配時間片的進程。另外一個隊列稱爲過時隊列,用於存儲那些已經享用過期間片的進程。O(1)調度器把時間片從活躍隊列中調出一個進程。這個進程用盡時間片,就會轉移到過時隊列。當活躍隊列的全部進程都被執行事後,調度器就會把活躍隊列和過時隊列對調,用一樣的方式繼續執行這些進程。性能
Linux 2.6.23版本起,徹底公平調度器(CFS,Completely Fair Scheduler)取代了O(1)調度器。CFS調度器不對進程進行任何形式的估計和猜想。這一點和O(1)區分互動和非互動進程的作法徹底不一樣。CFS調度器增長了一個虛擬運行時(virtual runtime)的概念。每次一個進程在CPU中被執行了一段時間,就會增長它虛擬運行時的記錄。在每次選擇要執行的進程時,不是選擇優先級最高的進程,而是選擇虛擬運行時最少的進程。徹底公平調度器用一種叫紅黑樹的數據結構取代了O(1)調度器的140個隊列。紅黑樹能夠高效地找到虛擬運行最小的進程。CFS調度器會根據進程的優先級來計算一個時間片因子。一樣是增長250納秒的虛擬運行時,優先級低的進程實際得到的可能只有200納秒,而優先級高的進程實際得到可能有300納秒。這樣,優先級高的進程就得到了更多的計算資源。spa
進程優先級影響調度器的時間片分配和進程執行順序。Linux根據進程特性,在優先級上把進程分爲兩大類:實時進程和普通進程。操作系統
實時進程也並非真正的實時,一樣須要通過進程調度,只是會先級於普通進程運行。進程的優先級是一個0到139的整數。數字越小,優先級越高。其中,優先級0到99留給實時進程,100到139留給普通進程。普通進程的默認優先級時120,能夠經過nice命令來修改進程的默認優先級。下面的命令表示將默認優先級改成(120-20)。線程
$nice -n -20 ./app
普通進程的默認優先級稱爲靜態優先級,進程運行時實際採用的是動態優先級。調度程序經過增長或減小進程靜態優先級的值來獎勵IO消耗型進程或懲罰cpu消耗型進程,調整後的優先級稱爲動態優先級。動態優先級的計算公式以下。
動態優先級 = max(100 , min(靜態優先級 – bonus + 5 ,139))
bonus是範圍0~10的值,值小於5表示下降動態優先級以示懲罰,值大於5表示增長動態優先級以示獎賞。
Linux系統中,實時進程和普通進程採起了不一樣的調度策略。實時進程使用是實時調度策略,有三種:SCHED_FIFO,SCHED_RR,SCHED_DEADLINE。
實時進程使用0~99的優先級,就緒進程使用隊列的方式組織。相同優先級的實時進程都保存在一個列表中,再根據優先級排列起來。調度器老是先選取優先級最高的進程來運行。實時進程在下列狀況下可讓出CPU,
普通進程的調度策略有三種,由CFS調度器實現,分別是:SCHED_NORMAL,SCHED_BATCH,SCHED_IDLE。
Linux進程調度的介紹就簡單說這些,主要是概念普及,實用爲主。下面簡單列舉一下調度相關的函數。
Affinity表示CPU的親和性,就是讓進程在指定的CPU上儘可能長時間地運行而不被遷移到其餘處理器,也稱爲CPU關聯性。再簡單的點的描述就將指定的進程或線程綁定到相應的CPU上。在多核運行的機器上,每一個CPU自己本身會有緩存,緩存着進程使用的信息。若是進程被調度到其餘CPU上,cache命中率就會下降。當綁定CPU後,程序就會一直在指定的CPU上運行,不會被調度到其餘CPU上,能夠提升性能。
參考文章: