當前位置: 華文星空 > 新聞

行程優先級

2022-03-31新聞

1.行程優先級:

內核使用[0~139]這140個數來表示140種優先級。

內核使用一個簡單些的數值範圍,從0到139(包含),用來表示內部優先級。同樣是值越低,優

先級越高。從0到99的範圍專供即時行程使用。 nice值[20, +19]對映到範圍100到139,如圖2-14所示。

即時行程的優先級總是比普通行程更高。

下面列出了task_struct結構體中與許可權相關的幾個成員:

a) static_prio,指普通行程的靜態優先級(即時行程沒用該參數),值越小優先級越高。靜態優先級是行程啟動時分配的優先級。它可以用nice()或者sched_setscheduler()系統呼叫更改,否則在執行期間一直保持恒定。

b) rt_priority,表示即時行程的優先級(普通行程沒用該參數),它的值介於[0~99]之間(包括0和99)。註意:rt_priority是值越大優先級越高。

c) normal_prio是基於前兩個參數static_prio或rt_priority計算出來的。可以這樣理解:static_prio和rt_priority分別代表普通行程和即時行程「靜態」的優先級,代表行程的固有內容。由於他們兩的「單位」不同(一個是點頭yes,搖頭no;另一個是搖頭yes,點頭no),一個是值越小優先級越高,另一個是值越大優先級越高。有必要用normal_prio統一下"單位"。統一成值越小優先級越高,因此,normal_prio也可以理解為:統一了單位的「靜態」優先級。

d) prio,叫做動態優先級,它表示行程的有效優先級,顧名思義,在系統中需要判斷行程優先級時用的便是該參數,排程器考慮的優先級也就是它。對於即時行程來說,有效優先級prio就等於它的normal_prio(「統一單位」後的優先級)。有效優先級對普通行程來說尤為重要,行程可以臨時提高優先級,透過改變prio的值實作,所以優先級的提高不影響行程的靜態優先級。順帶說明一下,子行程的有效優先級prio初始劃為父行程的靜態優先級,而不是父行程的有效優先級(也就是說,父行程的優先級如果臨時提高了,該特性不會遺傳給子行程)。

e) policy, 排程策略,共有五種可能值:SCHED_NORMAL,SCHED_IDLE,SCHED_BATCH,SCHED_FIFO,SCHED_RR。普通行程的policy是前三種值之一,即時行程的policy是後兩種值之一。

下列宏用於在各種不同表示形式之間轉換(MAX_RT_PRIO指定即時行程的最大優先級,而MAX_PRIO則是普通行程的最大優先級數值):

#define MAX_USER_RT_PRIO 100 #define MAX_RT_PRIO MAX_USER_RT_PRIO #define MAX_PRIO (MAX_RT_PRIO + 40) #define DEFAULT_PRIO (MAX_RT_PRIO + 20) /* * Convert user-nice values [ -20 ... 0 ... 19 ] * to static priority [ MAX_RT_PRIO..MAX_PRIO-1 ], * and back. */ #define NICE_TO_PRIO(nice) (MAX_RT_PRIO + (nice) + 20) #define PRIO_TO_NICE(prio) ((prio) - MAX_RT_PRIO - 20) #define TASK_NICE(p) PRIO_TO_NICE((p)->static_prio)

2.行程優先級的計算

static_prio是計算的起點。假定它已經設定好,而內核現在想要計算其他行程p的動態優先級是用函數effective_prio(p)計算出來的:

p->prio= effective_prio(p);

看看 effective_prio函數的具體實作:該函數有兩個作用:

1.設定了行程p的normal_prio。

2.返回了行程的有效優先級。

/* * Calculate the current priority, i.e. the priority * taken into account by the scheduler. This value might * be boosted by RT tasks, or might be boosted by * interactivity modifiers. Will be RT if the task got * RT-boosted. If not then it returns p->normal_prio. */ static int effective_prio ( struct task_struct * p ) { undefined //計算普通優先級 p -> normal_prio = normal_prio ( p ); /* * If we are RT tasks or we were boosted to RT priority, * keep the priority unchanged. Otherwise, update priority * to the normal priority: */ /*

* 如果是即時行程或已經提高到即時優先級,則保持優先級不變。否則,返回普通優先級:

*/ if ( ! rt_prio ( p -> prio )) return p -> normal_prio ; return p -> prio ; } /* * Calculate the expected normal priority: i.e. priority * without taking RT-inheritance into account. Might be * boosted by interactivity modifiers. Changes upon fork, * setprio syscalls, and whenever the interactivity * estimator recalculates. */ static inline int normal_prio ( struct task_struct * p ) { undefined int prio ; if ( task_has_dl_policy ( p )) //SCHED_DEADLINE 新支持的即時行程排程策略 prio = MAX_DL_PRIO - 1 ; // MAX_DL_PRIO = -1

//判斷行程的排程策略policy是不是SCHED_FIFO和SCHED_RR中的一種,如果是則它是即時行程,返回true,反之則返回false。

else if ( task_has_rt_policy ( p )) prio = MAX_RT_PRIO - 1 - p -> rt_priority ; else prio = __normal_prio ( p ); return prio ; }

普通優先級需要根據普通行程和即時行程進行不同的計算。 __normal_prio的計算只適用於普通行程。而即時行程的普通優先級計算,則需要根據其rt_priority設定。由於更高的rt_priority值表示更高的即時優先級,內核內部優先級的表示剛好相反,越低的值表示的優先級越高。因此,即時行程在內核內部的優先級數值,正確的演算法是MAX_RT_PRIO - 1 - p->rt_priority。這一次請註意,與effective_prio相比,即時行程的檢測不再基於優先級數值,而是透過task_struct中設定的排程策略來檢測

MAX_RT_PRIO的值是100(也就是即時行程的優先級的最大數值加1),normal_prio()函數實際上就是了單位統一的過程。它的執行流程是這樣的:如果p是即時行程,那麽就返回99-rt_priority(rt_priority是值越大表示行程優先級越高,normal_priority反之,所以透過這個方式將rt_priority轉換為normal_priority),如果行程p是普通行程,不需要統一"單位",那麽直接返回它的靜態優先級static_prio。

/* * __normal_prio - return the priority that is based on the static prio */ static inline int __normal_prio ( struct task_struct * p ) { undefined return p -> static_prio ; }

為什麽內核在effective_prio中檢測即時行程是基於優先級數值,而非task_has_rt_policy?對於臨時提高至即時優先級的非即時行程來說,這是必要的,這種情況可能發生在

使用即時互斥量(RT-Mutex)時。

綜上:a) 因此對於即時行程來說:prio=effective_prio()=normal_prio。normal_prio=MAX_RT_PRIO-1-rt_priority

b) 對於優先級沒有提高的普通行程來說:prio=effective_prio()=normal_prio=static_prio

c) 對於優先級提高的普通行程來說:prio=effective_prio(),normal_prio=static_prio。prio的值被其他函數更改過,所以與初始時不同。

d) nice值

nice值也用來用來表示普通行程的優先等級,它介於[-20~19]之間,也是值越小優先級越高。之前講過普通行程的優先值範圍是[100~139],剛好和nice值一一對應起來:優先等級=nice值+120。nice值並不是表示行程優先級的一種新的機制,只是優先級的另一個表示而已。sys_nice()系統呼叫設定的是行程的靜態優先級static_prio.

3.計算負荷權重

行程的重要性不僅是由優先級指定的,而且還需要考慮保存在task_struct->se.load的負荷權重。 set_load_weight負責根據行程類別及其靜態優先級計算負荷權重。

在行程被排程的先後順序中,講到影響行程在就緒佇列中的參數是行程的權重值weight。而weight是由行程的靜態優先級static_prio決定的,靜態優先級越高(static_prio值越小)weight值越大。靜態優先級和weight是透過prio_to_weight陣列對應起來的。靜態優先級為100(nice值為-20)的行程,其weight值為prio_to_weight[0],靜態優先級為k的(nice值為k-120)的行程,weight值為prio_to_weight[k-100]。

普通行程的預設nice值為0,即預設靜態優先級為120,它的weight值為prio_to_weight[20],即1024。因此NICE_O_LOAD的值就是1024,NICE_0_LOAD的命名也就是這麽來的。

很重要的規定:nice值為0的行程虛擬執行時間(vruntime)行走速度和真實執行時間(runtime)行走的速度相同。

權重計算的程式碼也需要考慮行程類別。即時行程的權重是普通行程的兩倍。另一方面,SCHED_IDLE行程的權重總是非常小:

set_load_weight程式碼的實作:

static void set_load_weight ( struct task_struct * p ) { undefined int prio = p -> static_prio - MAX_RT_PRIO ; struct load_weight * load = & p -> se . load ; /* * SCHED_IDLE tasks get minimal weight: */ if ( p -> policy == SCHED_IDLE ) { undefined load -> weight = scale_load ( WEIGHT_IDLEPRIO ); load -> inv_weight = WMULT_IDLEPRIO ; return ; } //# define scale_load(w) (w) 內核不僅計算出權重本身,還儲存了用於除法的值。 load -> weight = scale_load ( prio_to_weight [ prio ]); load -> inv_weight = prio_to_wmult [ prio ]; }

不僅行程,而且就緒佇列也關聯到一個負荷權重。每次行程被加到就緒佇列時,內核會呼叫inc_nr_running。這不僅確保就緒佇列能夠跟蹤記錄有多少行程在執行,而且還將行程的權重添加到就緒佇列的權重中:

static void enqueue_task_fair ( struct rq * rq , struct task_struct * p , int flags ) { .... inc_nr_running ( rq ); } static inline void inc_nr_running ( struct rq * rq ) { undefined rq -> nr_running ++ ; //佇列上行程數統計 ..... } static inline void update_load_add ( struct load_weight * lw , unsigned long inc ) { undefined //inc 對應於呼叫函數入參 se->load.weight lw -> weight += inc ; lw -> inv_weight = 0 ; }

在 進 程 從 就 緒 隊 列 移 除 時 , 會 調 用 對 應 的 函 數 (dec_nr_running、 dec_nr_running、 update_load_sub)。

更多Linux內核源碼高階知識請加開發交流Q群篇【318652197】獲取,進群免費獲取相關資料,免費觀看公開課技術分享,入群不虧,快來加入我們吧~
前100名進群領取,額外贈送一份價值699的內核資料包(含影片教程、電子書、實戰專案及程式碼)

資源免費領

學習直通車