進程調度
1.策略
進程可以被劃分為I/O消耗型和處理器消耗型。前者指進程的大部分時間用來提交I/O請求或是等待I/O請求,常處於可運行狀態。後者把時間大多用在執行代碼上,除非被搶佔,否則一直不停地運行,調度器不應該經常讓它們運行。
調度演算法中最基本的一類就是基於優先順序的調度。這是一種根據進程的價值和其對處理器時間的需求來對進程分級的想法。優先順序高的進程先運行,低的後運行,相同優先順序的進程按輪轉方式進行調度。
Linux根據以上思想實現了一種基於動態優先順序的調度方法。一開始,該方法先設定基本的優先順序,然而它允許調度程度根據需要來加、減優先順序。例如,如果一個進程在I/O等待上耗費的時間多於其已耗用時間,那麼該進程明顯屬於I/O消耗型,它的優先順序會被動態提高。相反,處理器消耗型進程的優先順序會被動態降低。
Linux核心提供兩組獨立的優先順序範圍。第一種是nice值,範圍從-20到+19,預設值是0。nice值越大優先順序越低。第二種是即時優先順序,其值可配置,範圍從0到99,任何即時進程的優先順序都高於普通的進程。
時間片是一個數值,它表明進程在被搶佔前所能持續啟動並執行時間,I/O消耗型不需要長的時間片,而處理器消耗型的進程則希望越長越好。注意,進程並不是一定非要一次就用完它所有的時間片,例如一個擁有100毫秒時間片的進程,可以通過重複調度,分5次每次20毫秒用完這些時間片。
Linux是搶佔式的。當一個進程進入TASK_RUNNING狀態,核心會檢查它的優先順序是否高於當前正在執行的進程。如果是這樣,發送器會被喚醒,搶佔當前正在啟動並執行進程並運行新的可運行進程。此外,當一個進程的時間片變為0時,它會被搶佔,發送器被喚醒以選擇一個新的進程。
2.Linux調度演算法
發送器中最基本的資料結構是運行隊列(runqueue)。可執行隊列定義在kernel/sched.c中。可執行隊列是給定處理器上的可執行進程的鏈表,每個處理器一個。每個可投入啟動並執行進程都唯一的歸屬一個可執行隊列。此外,可執行隊列中還包含每個處理器的調度資訊。
每個運行隊列都有兩個優先順序數組,一個活躍的和一個到期的。優先順序數組是一種能夠提供O(1)級演算法複雜度的資料結構。優先順序數組使可運行處理器的每一種優先順序都包含一個相應的隊列,而這些隊列包含對應優先順序上的可執行進程鏈表。優先順序數組還擁有一個優先順序位元影像,當需要尋找當前系統內擁有最高優先順序的可執行進程時,它可以協助提高效率。
活躍數組內的可執行隊列上的進程都還有時間片剩餘,而到期數組內的都耗盡了時間片。當一個進程的時間片耗盡時,它會被移至到期數組,但在此之前,時間片已經給它重新計算好。重新計算時間片變得非常簡單,只要在活躍和到期數組之間來回切換,這是O(1)級發送器的核心。
選定下一個進程並切換到它去執行是通過schedule()函數實現的。當核心代碼想要休眠時,會直接調用該函數,另外,如果有哪個進程將被搶佔,那麼該函數也會被喚起執行。schedule()函數獨立於每個處理器運行。