四種任務優先度 (prio、static_prio、rt_priority 與 normal_prio) 之間的差異 === ###### tags: `kernel` :::info 我的 kernel 版本為 4.19 ::: [TOC] ## 結論寫前面 \(\*´艸`\*\) 優先級有兩種,一種是`nice`(user space),一種是`prio` (kernel space)。調度程序使用的優先級為`prio`,該數值越低優先級越高。 透過`nice()`、`renice()`、`sched_setscheduler()`可以控制`nice`值,使得`prio`有先變化。 如果一個程序是.. * `fair` (一般等級):優先級為`static_prio`,即`nice + 120`,範圍 \[100, 139\]。 * `realtime`:優先級為`99 - rt_priority`,範圍 \[0, 99]。 * `deadline`:優先級為`-1` ## Task 優先級 在 linux 中,`struct task_struct`描述了一個任務 (task) 所需要的資訊,該結構定義在`include/linux/sched.h`中,包含與優先級有關的成員︰`prio`、`static_prio`與`normal_prio`,注意到`nice`並非作其一。 ```cpp struct task_struct { ... int prio; int static_prio; int normal_prio; unsigned int rt_priority; ... } ``` ## Priority 的計算 從`task_struct`中,我們理解到其實有四種不同的優先度,他們都與**核心的調度程序**有相關,在核心調度各個任務時,會已這些優先度作為考量,因此我們將會針對其程式碼使用的位置與計算做說明,方便我們未來快速回憶 XD ### 一些 #define C 中常使用`#define`來處理一些固定常數,當然這裡也不例外,關於優先級也有一些前處理定義。相關檔案在`include/linux/sched/prio.h`中。 #### nice 的範圍 首先是關於`nice`的部份,其範圍為 \(19, -20\),其中 **-20 為最高,19 為最低優先度**,並定義了寬度為 40,`nice`數值決定了 user space 可以直接更改的優先度。 :::warning 若一個程序為 realtime 等級,`nice`並不會發揮作用 ::: ```c #define MAX_NICE 19 #define MIN_NICE -20 #define NICE_WIDTH (MAX_NICE - MIN_NICE + 1) ``` #### Priority 的範圍 因應 realtime 任務的出現,既`nice`後推出了`priority`的概念,定義了使用者可以設定的 realtime priority,範圍為 \[0, 100\) ```c #define MAX_USER_RT_PRIO 100 #define MAX_RT_PRIO MAX_USER_RT_PRIO ``` 繼續往下定義了任務最大數值的優先度 (139),定義了常規任務的優先級數值為 120,並規定了範圍為 \(100, 139\) ```c #define MAX_PRIO (MAX_RT_PRIO + NICE_WIDTH) #define DEFAULT_PRIO (MAX_RT_PRIO + NICE_WIDTH / 2) ``` #### Nice 與 priority 數值之間的關聯 注意到這個前置定義,`nice`與`prio`是可以互相轉換的。 ```c // nice 轉成 prio 就直接是 nice + 120 // 也就是說如果 nice = 19,priority 就會是 120 + 19 = 139 #define NICE_TO_PRIO(nice) ((nice) + DEFAULT_PRIO) // prio 轉回 nice 就是反向計算即可 #define PRIO_TO_NICE(prio) ((prio) - DEFAULT_PRIO) ``` ### Static_prio 在說明其餘的優先級以前,必須先說明`static priority`,這個優先級是在一個程序被創造時,執行`sched_fork()`時給出,並可以透過`nice()`或是`sched_setscheduler()`來修改 (`nice`數值可映射回`priority`),否則該值在程序運行的過程中皆保持恆定。 這個優先級有幾個特點: * **數值越小,優先度越高** * 默認值為 120,對應的`nice`為 0 * 會繼承 parent 的優先級 ```c int sched_fork(unsigned long clone_flags, struct task_struct *p) { ... // 繼承 parent 優先級 p->prio = current->normal_prio; ... } ``` ### Rt_priority 此優先級代表著一個程序的 realtime 的優先級,對於一般等級的程序無效,範圍為 \[0, 99\] ### Normal_prio 此優先級考慮了`static_prio`與程序使用的調度策略,並將其正規化成數值基準來比較高低。 `normal_prio()`在一個程序為一般調度等級時,會回傳`static_prio`優先級。若是`deadline`或是`realtime`等級,則會回傳相對應的優先級。 因`deadline`優先級高於`realtime`,因此其正規化後優先級為 -1 (最優先),`realtime`次之,範圍為 \[0, 99\],這邊需要特別注意的是他翻轉 realtime 原先設定的優先度,也就是說,若一個`realtime`程序優先度 99,則其`normal_prio`為 0 特色: * 數值越小,優先級越大 * 考慮的是`scheduler`的策略 ```c static inline int normal_prio(struct task_struct *p) { int prio; if (task_has_dl_policy(p)) prio = MAX_DL_PRIO-1; // -1 else if (task_has_rt_policy(p)) prio = MAX_RT_PRIO-1 - p->rt_priority; // 99 - rt_priority else prio = __normal_prio(p); // static_prio return prio; } static inline int __normal_prio(struct task_struct *p) { return p->static_prio; } ``` ### Prio 這個優先值才是調度器真正使用的優先度,他的初始數值由`sched_fork()`決定,或是由`effective_prio()`中計算,該函數考慮了一個程序處於`deadline`或是`realtime`時的情況,則回傳對應的正規化優先度 (不計算`nice`),否則就回傳`static_prio`。 ```c static int effective_prio(struct task_struct *p) { p->normal_prio = normal_prio(p); if (!rt_prio(p->prio)) return p->normal_prio; return p->prio; } void set_user_nice(struct task_struct *p, long nice) { ... p->prio = effective_prio(p); ... } ```