linux2.6.29 CFS調度詳細分析(一)

linux2.6.29 CFS調度詳細分析(一)
 
 
   衆所周知,linux最新的內核採用了CFS的調度機制,網上也有不少文章對CFS調度的源碼做了詳細的分析,但是大部分的文章太注重細節了,所以沒有把CFS的原理進行一下從整體上的概括,基於這個原因,本文要從CFS調度的基本原理以及在公平調度類的整個執行過程爲主線來進行詳細的說明。
   CFS(completely fair schedule),故名思議完全公平的調度,那麼它到底怎麼實現了完全的公平呢?既然講公平,那就應該有個評判的標準,在這之前我們先來講幾個比較重要的概念。
調度實體(sched entiy):就是調度的對象,可以理解爲進程。
虛擬運行時間(vruntime):即每個調度實體的運行時間。
公平調度隊列(cfs_rq):採取公平調度的調度實體的運行隊列。
   1 每個進程的weight值是如何確定的呢?
上面談到公平的依據,CFS的公平依據就是每個調度實體的權重(weight),這個權重是有優先級來決定的,即優先級越高權重越高,linux內核採用了nice-prio-weight的一個轉換關係來實現了每個調度實體權重的確定。我們來回顧,進程被創建的時候他的優先級是繼承自父進程的,如果想改變有優先級,linux內核提供了幾個系統調用來改變進程的nice值,從而改變權重,不如sys_nice()系統調用,下面來看一下他們之間的轉換關係:

其中,MAX_RT_PRIO=100,nice的值在-20到19之前,那麼優先級就在100 - 139之間。 

再看prio和weight之間的轉換關係,這是個經驗公式。通過以上分析我們就可以通過修改nice來修改weight了,這也就回答了每個調度實體的weight是怎麼確定的這個問題了吧。
2 基於這些weight,CFS又是怎麼來體現公平的呢?
   CFS可實現幾種不同的公平策略,這些策略是根據調度的對象的不同來區分的。
默認的是不開組調度的公平策略,即調度的單位是每個調度實體。我們來詳細看一下是怎麼調度的:
   假設現在系統有A,B,C三個進程,A.weight=1,B.weight=2,C.weight=3.那麼我們可以計算出整個公平調度隊列的總權重是cfs_rq.weight = 6,很自然的想法就是,公平就是你在重量中佔的比重的多少來拍你的重要性,那麼,A的重要性就是1/6,同理,B和C的重要性分別是2/6,3/6.很顯然C最重要就應改被先調度,而且佔用的資源也應該最多,即假設A,B,C運行一遍的總時間假設是6個時間單位的話,A佔1個單位,B佔2個單位,C佔三個單位。這就是CFS的公平策略。
   linux內核採用了計算公式:
l        ideal_time = sum_runtime * se.weight/cfs_rq.weight
ideal_time:每個進程應該運行的時間
sum_runtime:運行隊列中所有任務運行完一遍的時間
se.weight:當前進程的權重
cfs.weight:整個cfs_rq的總權重

這裏se.weight和cfs.weight根據上面講解我們可以算出,sum_runtime是怎們計算的呢,linux內核中這是個經驗值,其經驗公式是:
  
(1) sum_runtime=sysctl_sched_min_granularity * nr_running(if 進程數 > 5)
(2) sum_runtime=sysctl_sched_latency = 20 ms           (if 進程數 
注:sysctl_sched_min_granularity =4ms
linux內核代碼中是通過一個叫vruntime的變量來實現上面的原理的,即:
每一個進程擁有一個vruntime,每次需要調度的時候就選運行隊列中擁有最小vruntime的那個進程來運行,vruntime在時鐘中斷裏面被維護,每次時鐘中斷都要更新當前進程的vruntime,即vruntime以如下公式逐漸增長:
(1) vruntime +=  delta* NICE_0_LOAD/ se.weight;(if curr.nice!=NICE_0_LOAD)
(2)vruntime +=  delta;                      (if curr.nice=NICE_0_LOAD)
在每次更新完vruntime之後,將會進行一次檢查,要不要設置調度位TIF_NEED_SCHED,表示要不要被搶佔或自動放棄cpu,其實在沒有喚醒和CPU之間的進程遷移的時候,只有當前進程主動放棄CPU這種情況,即每個進程都會運行完自己的ideal_time.

即在這裏設置搶佔位。
通過以上分析,我們基本上已經分析了不開組調度的情況下,進程一般的調度的原理,這裏沒考慮到喚醒和進程歉意的情況,在後面的文章中會詳細的介紹。

至此,我們可能還會有幾個疑問:
1 這裏只是設置了TIF_NEED_SCHED位,那麼誰來檢查這個搶佔位,來實現進程切換的呢?
  這也是在時鐘中斷裏面做的,當時鍾中斷要返回的時候,會顯示的調用schedule()函數,這個函數會檢查TIF_NEED_SCHED有沒有被置位,來決定是否進行真正的進程切換。
2 還是 A B C這三個進程,如果不考慮喚醒和進程遷移的情況,A的理想的運行時間是3個時間單位,因爲只有在
             if (delta_exec > ideal_runtime)
                     resched_task(rq_of(cfs_rq)->curr);
這個時候才設置調度位,那麼A運行完這段時間後有沒有運行完呢?我們先來仔細分析一下這個公式:
vruntime +=  delta* NICE_0_LOAD/ se.weight;(if curr.nice!=NICE_0_LOAD)
NICE_0_LOAD是個定值,及系統默認的進程的權值
se,weight是當前進程的權重
delta是當前進程運行的時間
我們可以得出這麼個關係:
vruntime與delta成正比,即當前運行時間越長vruntime增長越快
vruntime與se.weight成反比,即權重越大vunruntime增長越慢
現在我們來考慮一種極端的情況:沒有喚醒,沒有進程遷移,A B C三個進程都是第一次運行
那麼系統就會隨機從A B C中選一個來運行,因爲他們的vruntime第一次是相等的。假設選擇B來運行,那麼B將運行2個時間單位之後,在某一次時鐘中斷中發現他的運行時間>它理想運行的時間(runtime>ideal_time),那麼將設置TIF_NEED_SCHED位,來進行進程切換;又假設第二次選中了C,那麼A運行了稍大於3個時間單位,最後A運行了稍大於1個時間單位。在這種情況下,我們會問,這麼運行後A B C有沒有運行完啊?(因爲我們的理想時間是經驗值算出來的),如果沒有運行完的話,那下一輪運行又是個什麼情況呢?我的理解是經驗吧,只有運行的時候才能知道,我們只能感覺上認爲應該是對的吧,希望弄明白的同學給我留言!!!
(我想說的是怎麼提出一種定量的評價好壞的方法)
3 我們再舉一個極端的情況假設有兩個用戶A,B,注意這裏使用戶。A用戶有1個進程a且a.weight=1;B用戶也有1個進程b且b.weight=1000,根據上面的公平理論,我們可以發現B用戶可能會一直霸佔cpu,在用戶更多的情況下,肯能會更糟。爲了解決這種問題,CFS引入了組調度,即調度的對象不僅僅限於調度實體,而是可以以用戶爲調度單位,即A和B位調度單位的話,A B各佔50%的CPU。而且只要一個組裏的進程被調度,其他的進程也會跟着被調度,但是佔用的CPU卻至於用戶有關。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章