linux核心調度演算法(2)--CPU時間片如何分配 轉!

來源:互聯網
上載者:User

標籤:cpp   命令   ase   訊號   oar   連接埠   ted   tool   plain   

核心在微觀上,把CPU的已耗用時間分成許多分,然後安排給各個進程輪流程執行,造成宏觀上所有的進程彷彿同時在執行。雙核CPU,實際上最多隻能有兩個進程在同時運行,大家在top、vmstat命令裡看到的正在啟動並執行進程,並不是真的在佔有著CPU哈。

所以,一些設計良好的高效能進程,比如nginx,都是實際上有幾顆CPU,就配幾個背景工作處理序,道理就在這。比如你的伺服器有8顆CPU,那麼nginx worker應當只有8個,當你多於8個時,核心可能會放超過多個nginx worker進程到1個runqueue裡,會發生什麼呢?就是在這顆CPU上,會比較均勻的把時間分配給這幾個nginx worker,每個worker進程運行完一個時間片後,核心需要做進程切換,把正在啟動並執行進程上下文儲存下來。假設核心分配的時間片是100ms,做進程切換的時間是5ms,那麼進程效能下降還是很明顯的,跟你配置的worker有關,越多下降得越厲害。

當然,這是跟nginx的設計有關的。nginx是事件驅動的全非同步進程,本身設計上就幾乎不存在阻塞和中斷,nginx的設計者就希望每一個nginx worker可以獨佔CPU的幾乎全部時間片,這點就是nginx worker數量配置的依據所在。

 

當然,實際的運行進程裡,大部分並不是nginx這種希望獨佔CPU全部時間片的進程,許多進程,比如vi,它在很多時間是在等待使用者輸入,這時vi在等待IO中斷,是不佔用時間片的,核心面對多樣化的進程,就需要技巧性的分配CPU時間片了。

 

核心分配時間片是有策略和傾向性的。換句話說,核心是偏心的,它喜歡的是IO消耗型進程,因為這類進程如果不能及時響應,使用者就會很不爽,所以它總會下意識的多分配CPU已耗用時間給這類進程。而CPU消耗進程核心就不太關心了。這有道理嗎?太有了,CPU消耗型慢一點使用者感知不出來,電訊號和生物訊號運轉速度差距巨大。雖然核心盡量多的分配時間片給IO消耗型進程,但IO消耗進程常常在睡覺,給它的時間片根本用不掉。很合理吧?

 

那麼核心具體是怎麼實現這種偏心呢?通過動態調整進程的優先順序,以及分配不同長短的CPU時間處來實現。先說核心如何決定時間片的長度。

對每一個進程,有一個整型static_prio表示使用者佈建的靜態優先順序,核心裡它與nice值是對應的。看看進程描述結構裡的static_prio成員。

 

[cpp] view plain copy 
  1. struct task_struct {  
  2.     int prio, static_prio;  
  3. ......}  

 

nice值是什嗎?其實就是優先順序針對使用者進程的另一種標記法,nice的取值範圍是-20到+19,-20優先順序最高,+19最低。上篇曾經說過,核心優先順序共有140,而使用者能夠設定的NICE優先順序如何與這140個優先順序對應起來呢?看代碼:

 

 

 

[cpp] view plain copy 
  1. #define MAX_USER_RT_PRIO    100  
  2. #define MAX_RT_PRIO     MAX_USER_RT_PRIO  
  3. #define MAX_PRIO        (MAX_RT_PRIO + 40)  

可以看到,MAX_PRIO就是140,也就是核心定義的最大優先順序了。

 

 

[cpp] view plain copy 
  1. #define USER_PRIO(p)        ((p)-MAX_RT_PRIO)  
  2. #define MAX_USER_PRIO       (USER_PRIO(MAX_PRIO))  

而MAX_USER_PRIO就是40,意指,普通進程指定的優先順序別最多40,就像前面我們講的那樣-20到+19。

 

 

 

[cpp] view plain copy 
  1. #define NICE_TO_PRIO(nice)  (MAX_RT_PRIO + (nice) + 20)  

nice值是-20表示最高,對應著static_prio是多少呢?NICE_TO_PRIO(0)就是120,NICE_TO_PRIO(-20)就是100。

 

 

當該進程剛被其父進程fork出來時,是平分其父進程的剩餘時間片的。這個時間片執行完後,就會根據它的初始優先順序來重新分配時間片,優先順序為+19時最低,只分配最小時間片5ms,優先順序為0時是100ms,優先順序是-20時是最大時間片800ms。我們看看核心是如何計算時間片長度的,大家先看下task_timeslice時間片計算函數:

 

[cpp] view plain copy 
  1. #define SCALE_PRIO(x, prio) \  
  2.     max(x * (MAX_PRIO - prio) / (MAX_USER_PRIO/2), MIN_TIMESLICE)  
  3.   
  4. static unsigned int task_timeslice(task_t *p)  
  5. {  
  6.     if (p->static_prio < NICE_TO_PRIO(0))  
  7.         return SCALE_PRIO(DEF_TIMESLICE*4, p->static_prio);  
  8.     else  
  9.         return SCALE_PRIO(DEF_TIMESLICE, p->static_prio);  
  10. }  

這裡有一堆宏,我們把宏依次列出看看它們的值: [cpp] view plain copy 
  1. # define HZ     1000      
  2. #define DEF_TIMESLICE       (100 * HZ / 1000)  
所以,DEF_TIMESLICE是100。假設nice值是-20,那麼static_prio就是100,那麼SCALE_PRIO(100*4, 100)就等於800,意味著最高優先順序-20情形下,可以分到時間片是800ms,如果nice值是+19,則只能分到最小時間片5ms,nice值是預設的0則能分到100ms。

 

 

貌似時間片只與nice值有關係。實際上,核心會對初始的nice值有一個-5到+5的動態調整。這個動態調整的依據是什麼呢?很簡單,如果CPU用得多的進程,就把nice值調高點,等價於優先順序調低點。CPU用得少的進程,認為它是互動性為主的進程,則會把nice值調低點,也就是優先順序調高點。這樣的好處很明顯,因為1、一個進程的初始優先值的設定未必是準確的,核心根據該進程的即時表現來調整它的執行情況。2、進程的表現不是始終如一的,比如一開始只是監聽80連接埠,此時進程大部分時間在sleep,時間片用得少,於是nice值動態降低來提高優先順序。這時有client接入80連接埠後,進程開始大量使用CPU,這以後nice值會動態增加來降低優先順序。

 

思想明白了,代碼實現上,優先順序的動態補償到底依據什麼呢?effective_prio返回動態補償後的優先順序,注釋非常詳細,大家先看下。

 

[cpp] view plain copy 
  1. /* 
  2.  * effective_prio - return the priority that is based on the static 
  3.  * priority but is modified by bonuses/penalties. 
  4.  * 
  5.  * We scale the actual sleep average [0 .... MAX_SLEEP_AVG] 
  6.  * into the -5 ... 0 ... +5 bonus/penalty range. 
  7.  * 
  8.  * We use 25% of the full 0...39 priority range so that: 
  9.  * 
  10.  * 1) nice +19 interactive tasks do not preempt nice 0 CPU hogs. 
  11.  * 2) nice -20 CPU hogs do not get preempted by nice 0 tasks. 
  12.  * 
  13.  * Both properties are important to certain workloads. 
  14.  */  
  15. static int effective_prio(task_t *p)  
  16. {  
  17.     int bonus, prio;  
  18.   
  19.     if (rt_task(p))  
  20.         return p->prio;  
  21.   
  22.     bonus = CURRENT_BONUS(p) - MAX_BONUS / 2;  
  23.   
  24.     prio = p->static_prio - bonus;  
  25.     if (prio < MAX_RT_PRIO)  
  26.         prio = MAX_RT_PRIO;  
  27.     if (prio > MAX_PRIO-1)  
  28.         prio = MAX_PRIO-1;  
  29.     return prio;  
  30. }  

可以看到bonus會對初始優先順序做補償。怎麼計算出這個BONUS的呢?

 

 

[cpp] view plain copy 
  1. #define CURRENT_BONUS(p) \  
  2.     (NS_TO_JIFFIES((p)->sleep_avg) * MAX_BONUS / \  
  3.         MAX_SLEEP_AVG)  

可以看到,進程描述符裡還有個sleep_avg,動態補償完全是根據它的值來運作的。sleep_avg就是關鍵了,它表示進程睡眠和啟動並執行時間,當進程由休眠轉到運行時,sleep_avg會加上這次休眠用的時間。在運行時,每運行一個時鐘節拍sleep_avg就遞減直到0為止。所以,sleep_avg越大,那麼就會給到越大的動態優先順序補償,達到MAX_SLEEP_AVG時會有nice值-5的補償。

 

 

核心就是這麼偏愛互動型進程,從上面的優先順序和時間片分配上都能看出來。實際上,核心還有方法對互動型進程搞優待。上篇說過,runqueue裡的active和expired隊列,一般的進程時間片用完後進expired隊列,而對IO消耗的互動型進程來說,則會直接進入active隊列中,保證高靈敏的響應,可見什麼叫萬千寵愛於一身了。

linux核心調度演算法(2)--CPU時間片如何分配 轉!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.