linux2.4到linux2.6核心調度(9)__linux

來源:互聯網
上載者:User

(NS_TO_JIFFIES((p)->sleep_avg) * MAX_BONUS / MAX_SLEEP_AVG) - MAX_BONUS/2
如下圖所示:


再用這個 bonus 去減靜態優先順序就得到進程的動態優先順序(並限制在 MAX_RT_PRIO和MAX_PRIO 之間),bonus 越小,動態優先順序數值越大,優先順序越低。也就是說,sleep_avg 越大,優先順序也越高。
MAX_BONUS 定義為 MAX_USER_PRIO*PRIO_BONUS_RATIO/100,也就是說,sleep_avg 對動態優先順序的影響僅在靜態優先順序的使用者優先順序區(100~140)的1/4區間(±5)之內,相對而言,靜態優先順序,也就是使用者指定的 nice 值在優先順序計算的比重要大得多。這也是 2.6 調度系統中變化比較大的一個地方,調度器傾向於更多地由使用者自行設計進程的執行優先順序。
sleep_avg 反映了調度系統的兩個策略:互動式進程優先和分時系統的公平共用,在下一節中我們還要專門分析。
2) 優先順序計算時機
優先順序的計算不再集中在調度器選擇候選進程的時候進行了,只要進程狀態發生改變,核心就有可能計算並設定進程的動態優先順序:
a) 建立進程 
在wake_up_forked_process()中,子進程繼承了父進程的動態優先順序,並添加到父進程所在的就緒隊列中。
如果父進程不在任何就緒隊列中(例如它是 IDLE 進程),那麼就通過 effect_prio() Function Compute出子進程的優先順序,而後根據計算結果將子進程放置到相應的就緒隊列中。
b) 喚醒休眠進程 
核心調用 recalc_task_prio() 設定從休眠狀態中醒來的進程的動態優先順序,再根據優先順序放置到相應就緒隊列中。
c) 調度到從 TASK_INTERRUPTIBLE 狀態中被喚醒的進程 
實際上此時調度器已經選定了候選進程,但考慮到這一類型的進程很有可能是互動式進程,因此此時仍然調用 recalc_task_prio() 對該進程的優先順序進行修正(詳見"進程平均等待時間 sleep_avg"),修正的結果將在下一次調度時體現。
d) 進程因時間片相關的原因被剝奪 cpu 
在 schedule_tick() 中(由時鐘中斷啟動),進程可能因兩種原因被剝奪 cpu,一是時間片耗盡,一是因時間片過長而分段。這兩種情況都會調用effect_prio() 重新計算優先順序,重新入隊。
e) 其它時機 
這些其它時機包括 IDLE 進程初始化(init_idle())、Server Load Balancer(move_task_away(),詳見"調度器相關的Server Load Balancer")以及修改 nice 值(set_user_nice())、修改調度策略(setscheduler())等主動要求改變優先順序的情況。
由上可見,2.6 中動態優先順序的計算過程在各個進程運行過程中進行,避免了類似 2.4 系統中就緒進程很多時計算過程耗時過長,從而無法預計進程的回應時間的問題。同時,影響動態優先順序的因素集中反映在 sleep_avg 變數上。

6. 進程平均等待時間 sleep_avg
進程的 sleep_avg 值是決定進程動態優先順序的關鍵,也是進程互動程度評價的關鍵,它的設計是 2.6 調度系統中最為複雜的一個環節,可以說,2.6 調度系統的效能改進,很大一部分應該歸功於 sleep_avg 的設計。這一節,我們將專門針對 sleep_avg 的變化和它對調度的影響進行分析。
核心中主要有四個地方會對 sleep_avg 進行修改:休眠進程被喚醒時(activate_task()調用 recalc_task_prio() 函數)、TASK_INTERRUPTIBLE 狀態的進程被喚醒後第一次調度到(schedule()中調用 recalc_task_prio())、進程從 CPU 上被剝奪下來(schedule()函數中)、進程建立和進程退出,其中recalc_task_prio() 是其中複雜度最高的,它通過計算進程的等待時間(或者是在休眠中等待,或者是在就緒隊列中等待)對優先順序的影響來重設優先順序。
1) 休眠進程被喚醒時
此時 activate_task() 以喚醒的時間作為參數調用 recalc_task_prio(),計算休眠等待的時間對優先順序的影響。
在 recalc_task_prio() 中,sleep_avg 可能有四種賦值,並最終都限制在 NS_MAX_SLEEP_AVG 以內:
a) 不變 
從 TASK_UNINTERRUPTIBLE 狀態中被喚醒(activated==-1)、互動程度不夠高(!HIGH_CREDIT(p))的使用者進程(p->mm!=NULL)),如果它的 sleep_avg 已經不小於 INTERACTIVE_SLEEP(p) 了,則它的 sleep_avg 不會因本次等待而改變。
b) INTERACTIVE_SLEEP(p) 
從 TASK_UNINTERRUPTIBLE 狀態中被喚醒(activated==-1)、互動程度不夠高(!HIGH_CREDIT(p))的使用者進程(p->mm!=NULL)),如果它的 sleep_avg 沒有達到 INTERACTIVE_SLEEP(p),但如果加上本次休眠時間 sleep_time 就達到了,則它的 sleep_avg 就等於 INTERACTIVE_SLEEP(p)。
c) MAX_SLEEP_AVG-AVG_TIMESLICE 
使用者進程(p->mm!=NULL),如果不是從 TASK_UNINTERRUPTIBLE 休眠中被喚醒的(p->activated!=-1),且本次等待的時間(sleep_time)已經超過了 INTERACTIVE_SLEEP(p),則它的 sleep_avg 置為 JIFFIES_TO_NS(MAX_SLEEP_AVG-AVG_TIMESLICE)。
d) sleep_avg+sleep_time 
如果不滿足上面所有情況,則將 sleep_time 疊加到 sleep_avg 上。此時,sleep_time 要經過兩次修正:
i. 根據 sleep_avg 的值進行修正,sleep_avg 越大,修正後的 sleep_time 越小:
    sleep_time = 
sleep_time * MAX_BONUS * (1-NS_TO_JIFFIES(sleep_avg)/MAX_SLEEP_AVG)

ii. 如果進程互動程度很低(LOW_CREDIT()返回真,見"更精確的互動式進程優先"),則將 sleep_time 限制在進程的基準時間片以內,以此作為對 cpu-bound 的進程的優先順序懲罰。
總的來說,本次等待時間越長,sleep_avg 就應該更大,但核心排除了兩種情況:

從 TASK_UNINTERRUPTIBLE 狀態醒來的進程,尤其是那些休眠時間比較長的進程,很有可能是等待某種資源而休眠,它們不應該受到等待時間的優先順序獎勵,它的 sleep_avg 被限制在 INTERACTIVE_SLEEP(p) 範圍內(或不超過太多)(INTERACTIVE_SLEEP(p) 的含義見後),--這一限制對已經認定為是互動進程無效。 
不是從 TASK_UNITERRUPTIBLE 狀態中醒來的進程,如果本次等待時間過長,也不應該受到等待時間的優先順序獎勵。

INTERACTIVE_SLEEP(p) 與 nice 的關係 
NS_TO_JIFFIES(INTERACTIVE_SLEEP(p))=
TASK_NICE(p)*MAX_SLEEP_AVG/40+ 
(INTERACTIVE_DELTA+1+MAX_BONUS/2)*MAX_SLEEP_AVG/MAX_BONUS -1

這個宏與進程 nice 值成正比,說明優先順序越低的進程,允許它在"互動式休眠"狀態下的時間越長。

2) TASK_INTERRUPTIBLE 狀態的進程被喚醒後第一次被調度到時
調度器挑選出候選進程之後,如果發現它是從 TASK_INTERRUPTIBLE 休眠中醒來後第一次被調度到(activated>0),調度器將根據它在就緒隊列上等待的時間長度調用 recalc_task_prio() 重新調整進程的 sleep_avg。
recalc_task_prio() 調整的過程與"休眠進程被喚醒時"的情況是一模一樣的,所不同的是,此時作為等待時間 sleep_time 參與計算的不是進程的休眠時間,而是進程在就緒隊列上等待調度的時間。並且,如果進程不是被中斷喚醒的(activated==1),sleep_time 還將受到約束(delta=delta*ON_RUNQUEUE_WEIGHT/100),因為此時該進程不是互動式進程的可能性很大。從上面對 recalc_task_prio() 的分析可知,sleep_time 減小一般來說就意味著優先順序會相應降低,所以,這一獎勵說明調度器在進一步減小進程的回應時間,尤其是互動式進程。
3) 被切換下來的進程
前面說過,sleep_avg 是進程的"平均"等待時間,recalc_task_prio() 計算了等待時間,在 schedule() 中,被切換下來的進程的 sleep_avg 需要減去進程本次啟動並執行時間 run_time(並保證結果不小於 0),這就是對"平均"的體現:等待得越久,sleep_avg 越大,進程越容易被調度到;而運行得越久,sleep_avg 越小,進程越不容易調度到。
run_time 可以用系統目前時間與進程 timestamp(上一次被調度啟動並執行時間)的差值表示,但不能超過 NS_MAX_SLEEP_AVG。對於互動式進程(HIGHT_CREDIT(p) 為真,見"更精確的互動式進程優先"),run_time 還要根據 sleep_avg 的值進行調整:
run_time = run_time / (sleep_avg*MAX_BONUS/MAX_SLEEP_AVG)

這樣調整的結果是互動式進程的 run_time 小於實際已耗用時間,sleep_avg 越大,則 run_time 減小得越多,因此被切換下來的進程最後計算所得的 sleep_avg 也就越大,動態優先順序也隨之變大。互動式進程可以藉此獲得更多被執行的機會。
4) fork 後
在 wake_up_forked_process() 中,父進程的 sleep_avg 要乘以 PARENT_PENALTY/100,而子進程的 sleep_avg 則乘以 CHILD_PENALTY/100。實際上PARENT_PENALTY 為 100,CHILD_PENALTY 等於 95,也就是說父進程的 sleep_avg 不會變,而子進程從父進程處繼承過來的 sleep_avg 會減小 5%,因此子進程最後的優先順序會比父進程稍低(但子進程仍然會置於與父進程相同的就緒隊列上,位置在父進程之前--也就是"前言"所說"子進程先於父進程運行")。
5) 進程退出時

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.