摘 要: 調度器" title="調度器">調度器是內核負責分配處理器時間的子系統。本文主要研究并對比了Linux2.4和Linux2.6的調度器,分析了新調度器的不足并提出了改進建議。
關鍵詞: Linux 調度 SMP 處理器親和性
Linux的內核開發是一個漫長的過程,自2001年11月開發出2.5.0以來,Linux內核的發展十分迅速,作了很多重大的改進,性能也有了很大的提高。內核調度器的改進是最主要的進步之一,本文對比研究了Linux2.4和Linux2.6的調度器,全面剖析了Linux2.6對調度器的改進。
一個成功的調度器的基本要求可以概括為以下三點:
(1)減少花在調度上的時間,以增加花在執行程序上的時間;
(2)在多處理器" title="多處理器">多處理器系統上,保持處理器的負載平衡;
(3)對交互式應用有良好的響應速度。
但是,一個成功的調度器是很難設計好的, 因為一個真正投入運行的系統受到很多因素的制約。相對于Linux2.6,Linux2.4的調度器有很多的不足之處, 2.6版本的Linux內核使用了新的調度器算法,稱為O(1)算法,它在高負載的情況下執行得極其出色,并且當有很多處理器時也可以很好地擴展。
O(n)算法,O代表order,括號里的數字代表最壞情況下算法效率的上限取決于算法涉及到的元素的個數,O(1)說明是一個常數,在這種情況下,每次調度的效率是一樣的,與涉及的元素的多少沒有關系,O(n)表示算法效率取決于算法涉及元素的個數。
1 Linux2.4的調度機制
Linux2.4的調度機制可以用下面的" title="面的">面的算法來描述[3],示意圖如圖1所示。
for(each runnable process on the system) {
find worthiness of this process
if(this is the worthiest process yet) remember it
}
if(the worthiest process′timeslice is zero) recalculate timeslice
else run the most worthy process
所有的就緒進程都在一個全局的就緒進程隊列中,這個隊列沒有任何有意義的排序;時間片重算算法是在所有的進程都用盡它們的時間片以后才重新計算。整個隊列由一個讀/寫自旋鎖(read/write spinlock)保護著,這樣多個處理器可以并行訪問,但同時提供寫操作的互斥訪問。
由算法可以看出,Linux2.4的調度算法可以說是一個O(n)算法,因為調度器挑選執行進程的開銷是隨系統中就緒進程的增長而線性增長的[1]。同時,當系統中有多個處理器時,訪問就緒進程隊列就成了瓶頸,性能也會顯著的下降。因而有很多的缺點:
(1)每次調度時,調度器都要線性遍歷" title="遍歷">遍歷這個隊列,以找出最值得運行的進程執行:當系統負載很高的時候,可執行進程隊列會很長,線性搜索的時間是線性增長的,這個時間會很長,當這個時間足夠長的時候,有可能出現多個處理器選擇了同一個進程的情況,這樣,有些處理器會發現,他選擇的進程已經分配了其他的處理器,而不得不重新選擇,甚至出現選擇運行進程的時間比實際執行進程的時間還要長的情況。
(2)當大多數的就緒進程的時間片都用完而又還沒有重新分配時間片的時候,SMP系統中有些處理器處于空閑狀態,這將影響SMP的效率。
(3)當空閑的處理器開始執行那些時間片尚未用盡而處于等待狀態的進程(如果它們自己的處理器忙)時,會導致進程開始在處理器之間“跳躍”,實時進程或者占用內存大的進程在處理器之間跳躍會嚴重影響系統的性能。
(4)在一個有很多處理器的系統中,當進程用完它們的時間片以后需等待重算,以得到新的時間片,從而導致大部分的處理器處于空閑狀態;這將影響SMP的效率。
因此,不難看出當系統中有大量的可執行進程時,選擇一個進程去執行可能要花費較長的時間,系統中有多個處理器的時候,難度就更大了,這種調度,在多處理器或者系統負載比較高的情況下,性能受到影響。
2 Linux2.4調度器性能低下的原因
從上面的分析可以看出,造成Linux2.4調度器性能低下的主要原因如下:
(1)系統中調度算法屬于O(n),開銷是線性增長的;
(2)只有一個全局的就緒進程隊列,對多處理器的伸縮性支持不好;
(3)處理器的親和性不好,容易導致進程在處理器之間“跳躍”;
(4)時間片的重算循環制約了多處理器的效率。
Linux2.6做了很大的改進,它采用O(1)算法,它在高負載的情況下執行得極其出色,并且當有很多處理器時也可以很好地擴展,不但大大改善了對SMP的支持,同時也兼顧了單CPU或者雙CPU系統的要求。
3 Linux2.6調度器的改進目標
為了改善Linux2.4的上述不足,linux2.6的調度器可以通過提供下列新的特性來改善調度器的性能:
(1)提供完全的O(1)調度算法,也就是說,不管系統中進程數量的多少,調度器中所有的算法都必須在常數時間內完成。
(2)應該對SMP有良好的可伸縮性,理想情況下,每個處理器應該有獨立的可執行進程隊列和鎖機制。
(3)應該提高SMP 的處理器親和性,但是同時也應該有在負載不平衡" title="不平衡">不平衡的時候在處理器間遷移進程的能力。
4 Linux2.6的調度機制
新的調度器都實現了這些目標,具體方法是,基于每個 CPU 來分布時間片,并且取消了全局同步和重算循環[2]。
每個進程有兩個數組,活動就緒進程隊列數組和不活躍就緒進程隊列數組。每個數組中有140個就緒進程隊列(runqueue),每個隊列對應于140個優先級的某一個。由一個位圖來指示哪些隊列是空的,哪些不是空的,每個隊列都是先進先出的(FIFO)。這樣,在挑選進程的時候,只要通過find_first_bit找到第一個不為空的隊列,并取隊首的進程就可以了。
如果一個進程消耗完了它的“時間片”,就進入不活躍就緒進程數組的相應隊列的隊尾。當所有的進程都“耗盡”了它的“時間片”后,交換活躍與不活躍就緒進程隊列數組的指針就可以了,不需要任何其他的開銷。
這樣,不管隊列中有多少個就緒進程,挑選就緒程的速度是一定的,所以稱為O(1)算法,該算法可描述如下,示意圖如圖2所示。
get the highest priority level that has processes
get first process in the list at that priority level
run this process
這個算法有很多的優點,簡述如下:
(1)每個處理器都有獨立的就緒進程隊列,各個處理器可以并行地運行Scheduler程序來挑選進程運行,不同處理器上的進程可以完全并行地休眠、喚醒和上下文切換。
(2)進程只映射到一個處理器的就緒進程隊列中,不會被其他的處理器選中,因而也就不會在不同的處理器之間跳躍。
當然,處理器有時確實需要在處理器之間遷移進程,例如負載不平衡的時候,每個處理器每200ms檢查一次其他的處理器是不是處在負載不平衡的狀況下,就緒進程隊列為空的處理器會每1ms檢查一次。
但是這種情況并不是頻繁的發生,所以處理器的親和性基本能得到保證。
新的調度器的性能確實有很大提高,一個服務器在多個處理器間傳送大量的消息的測試結果如表1所示。從表中可以看出,使用新的調度器,在同樣的時間內系統能作更多的事情。
5 Linux2.6調度器的不足
新的調度算法在以下幾個方面有待改進。
首先,盡管處理器的速度在很快的發展,但是存儲體系的速度發展卻是相對比較緩慢,對存儲器的操作時間往往形成瓶頸。
調度器給處理器分配進程的時候應該考慮進程的相關性。考慮這樣的一種情況:兩個進程頻繁的通過管道或者共享內存通信,測試表明,它們在同一個處理器上工作會更好,因為不用涉及到把數據從一個處理器的cache里拷貝到另一個處理器的cache里。而目前的調度器不能保證將這樣有著密切聯系的進程分配到同一個處理器上。同樣的問題也存在于設備的相關性。
其次,仍是進程遷移問題,因為在處理器間遷移不同進程的代價是不盡相同的,所以在遷移進程的時候,應該適當考慮進程的特點。
遷移進程的時應考慮進程的大小(這里是指占有內存資源的大小),遷移進程的時候,并沒有考慮到進程占用內存的大小,遷移大的進程到其他的處理器會較嚴重的影響系統的性能。試想出現這樣情況:處理器A把它惟一的大進程遷移到了處理器B,而處理器B上的所有進程都是大進程,存儲資源原本就緊張,這樣一來,處理器A上的進程存儲資源就很豐富,而處理器B 則更加糟糕。目前,Linux2.6調度器在遷移進程的時候還沒有考慮進程的大小。
最后,當系統檢測到需要遷移進程以平衡負載的時候,是不是真的非平衡負載不可呢?當系統的負載不平衡且很輕微的時候,是不一定需要平衡負載的。假設有這樣情況:有六個進程要求同時執行完畢,但是系統中只有四個處理器。這樣,總有兩個處理器有兩個進程,而其他兩個處理器只有一個進程。這就出現問題,因為系統總是不平衡的,導致總有進程在同處理器間遷移,這也就形成了跳躍。
6 對Linux2.6調度器的幾點改進建議
同一個任務隊列的進程和同一家族的進程盡量映射到同一個處理器上,因為這些進程之間需要頻繁通信的可能性是最大的;還可以動態地調整進程與處理器的映射,當監測出兩個處在不同的處理器上的進程頻繁通信的時候,就利用每200ms檢查負載平衡的計劃將它們調整到同一個處理器上。
可以在每個進程的就緒進程位圖中存儲一些大進程的標志信息,跟本處理器中大進程占的比重來遷出或者遷入大進程。
設置一個調節負載平衡的處理器負載閾值load_threshold,在load_balance函數中檢查系統欲調節負載的處理器的實際負載,沒有超過事先給定的 threshold,就不對這個處理器作真正意義上的負載平衡調節。
參考文獻
1 毛德操,胡希明.Linux內核源代碼情景分析.杭州:浙江大學出版社,2001
2 Hagen W.Real-Time and Performance Improvements in the 2.6 Linux Kernel.Linuxjornal#134,2005;(6)
3 Love R.Introducing the 2.6 Kernel.Linuxjornal,#109,2003;(5)