【原創】(一)Linux程序排程器-基礎
背景
Read the fucking source code!
--By 魯迅A picture is worth a thousand words.
--By 高爾基
說明:
- Kernel版本:4.14
- ARM64處理器,Contex-A53,雙核
- 使用工具:Source Insight 3.5, Visio
1. 概述
從這篇文章開始,將開始Linux排程器的系列研究了。
本文也會從一些基礎的概念及資料結構入手,先打造一個粗略的輪廓,後續的文章將逐漸深入。
2. 概念
2.1 程序
- 從教科書上,我們都能知道:程序是資源分配的最小單位,而執行緒是CPU排程的的最小單位。
- 程序不僅包括可執行程式的程式碼段,還包括一系列的資源,比如:開啟的檔案、記憶體、CPU時間、訊號量、多個執行執行緒流等等。而執行緒可以共享程序內的資源空間。
- 在Linux核心中,程序和執行緒都使用
struct task_struct
結構來進行抽象描述。 - 程序的虛擬地址空間分為使用者虛擬地址空間和核心虛擬地址空間,所有程序共享核心虛擬地址空間,沒有使用者虛擬地址空間的程序稱為核心執行緒。
Linux核心使用task_struct
結構來抽象,該結構包含了程序的各類資訊及所擁有的資源,比如程序的狀態、開啟的檔案、地址空間資訊、訊號資源等等。task_struct
結構很複雜,下邊只針對與排程相關的某些欄位進行介紹。
struct task_struct { /* ... */ /* 程序狀態 */ volatile long state; /* 排程優先順序相關,策略相關 */ int prio; int static_prio; int normal_prio; unsigned int rt_priority; unsigned int policy; /* 排程類,排程實體相關,任務組相關等 */ const struct sched_class *sched_class; struct sched_entity se; struct sched_rt_entity rt; #ifdef CONFIG_CGROUP_SCHED struct task_group *sched_task_group; #endif struct sched_dl_entity dl; /* 程序之間的關係相關 */ /* Real parent process: */ struct task_struct __rcu *real_parent; /* Recipient of SIGCHLD, wait4() reports: */ struct task_struct __rcu *parent; /* * Children/sibling form the list of natural children: */ struct list_head children; struct list_head sibling; struct task_struct *group_leader; /* ... */ }
2.2 程序狀態
- 上圖中左側為作業系統中通俗的程序三狀態模型,右側為Linux對應的程序狀態切換。每一個標誌描述了程序的當前狀態,這些狀態都是互斥的;
- Linux中的
就緒態
和執行態
對應的都是TASK_RUNNING
標誌位,就緒態
表示程序正處在佇列中,尚未被排程;執行態
則表示程序正在CPU上執行;
核心中主要的狀態欄位定義如下
/* Used in tsk->state: */ #define TASK_RUNNING 0x0000 #define TASK_INTERRUPTIBLE 0x0001 #define TASK_UNINTERRUPTIBLE 0x0002 /* Used in tsk->exit_state: */ #define EXIT_DEAD 0x0010 #define EXIT_ZOMBIE 0x0020 #define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD) /* Used in tsk->state again: */ #define TASK_PARKED 0x0040 #define TASK_DEAD 0x0080 #define TASK_WAKEKILL 0x0100 #define TASK_WAKING 0x0200 #define TASK_NOLOAD 0x0400 #define TASK_NEW 0x0800 #define TASK_STATE_MAX 0x1000 /* Convenience macros for the sake of set_current_state: */ #define TASK_KILLABLE (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE) #define TASK_STOPPED (TASK_WAKEKILL | __TASK_STOPPED) #define TASK_TRACED (TASK_WAKEKILL | __TASK_TRACED) #define TASK_IDLE (TASK_UNINTERRUPTIBLE | TASK_NOLOAD)
2.3 scheduler 排程器
- 所謂排程,就是按照某種排程的演算法,從程序的就緒佇列中選取程序分配CPU,主要是協調對CPU等的資源使用。程序排程的目標是最大限度利用CPU時間。
核心預設提供了5個排程器,Linux核心使用struct sched_class
來對排程器進行抽象:
Stop排程器, stop_sched_class
:優先順序最高的排程類,可以搶佔其他所有程序,不能被其他程序搶佔;Deadline排程器, dl_sched_class
:使用紅黑樹,把程序按照絕對截止期限進行排序,選擇最小程序進行排程執行;RT排程器, rt_sched_class
:實時排程器,為每個優先順序維護一個佇列;CFS排程器, cfs_sched_class
:完全公平排程器,採用完全公平排程演算法,引入虛擬執行時間概念;IDLE-Task排程器, idle_sched_class
:空閒排程器,每個CPU都會有一個idle執行緒,當沒有其他程序可以排程時,排程執行idle執行緒;
Linux核心提供了一些排程策略供使用者程式來選擇排程器,其中Stop排程器
和IDLE-Task排程器
,僅由核心使用,使用者無法進行選擇:
SCHED_DEADLINE
:限期程序排程策略,使task選擇Deadline排程器
來排程執行;SCHED_RR
:實時程序排程策略,時間片輪轉,程序用完時間片後加入優先順序對應執行佇列的尾部,把CPU讓給同優先順序的其他程序;SCHED_FIFO
:實時程序排程策略,先進先出排程沒有時間片,沒有更高優先順序的情況下,只能等待主動讓出CPU;SCHED_NORMAL
:普通程序排程策略,使task選擇CFS排程器
來排程執行;SCHED_BATCH
:普通程序排程策略,批量處理,使task選擇CFS排程器
來排程執行;SCHED_IDLE
:普通程序排程策略,使task以最低優先順序選擇CFS排程器
來排程執行;
2.4 runqueue 執行佇列
- 每個CPU都有一個執行佇列,每個排程器都作用於執行佇列;
- 分配給CPU的task,作為排程實體加入到執行佇列中;
- task首次執行時,如果可能,儘量將它加入到父task所在的執行佇列中(分配給相同的CPU,快取affinity會更高,效能會有改善);
Linux核心使用struct rq
結構來描述執行佇列,關鍵欄位如下:
/*
* This is the main, per-CPU runqueue data structure.
*
* Locking rule: those places that want to lock multiple runqueues
* (such as the load balancing or the thread migration code), lock
* acquire operations must be ordered by ascending &runqueue.
*/
struct rq {
/* runqueue lock: */
raw_spinlock_t lock;
/*
* nr_running and cpu_load should be in the same cacheline because
* remote CPUs use both these fields when doing load calculation.
*/
unsigned int nr_running;
/* 三個排程佇列:CFS排程,RT排程,DL排程 */
struct cfs_rq cfs;
struct rt_rq rt;
struct dl_rq dl;
/* stop指向遷移核心執行緒, idle指向空閒核心執行緒 */
struct task_struct *curr, *idle, *stop;
/* ... */
}
2.5 task_group 任務分組
- 利用任務分組的機制,可以設定或限制任務組對CPU的利用率,比如將某些任務限制在某個區間內,從而不去影響其他任務的執行效率;
- 引入
task_group
後,排程器的排程物件不僅僅是程序了,Linux核心抽象出了sched_entity/sched_rt_entity/sched_dl_entity
描述排程實體,排程實體可以是程序或task_group
; - 使用資料結構
struct task_group
來描述任務組,任務組在每個CPU上都會維護一個CFS排程實體、CFS執行佇列,RT排程實體,RT執行佇列
;
Linux核心使用struct task_group
來描述任務組,關鍵的欄位如下:
/* task group related information */
struct task_group {
/* ... */
/* 為每個CPU都分配一個CFS排程實體和CFS執行佇列 */
#ifdef CONFIG_FAIR_GROUP_SCHED
/* schedulable entities of this group on each cpu */
struct sched_entity **se;
/* runqueue "owned" by this group on each cpu */
struct cfs_rq **cfs_rq;
unsigned long shares;
#endif
/* 為每個CPU都分配一個RT排程實體和RT執行佇列 */
#ifdef CONFIG_RT_GROUP_SCHED
struct sched_rt_entity **rt_se;
struct rt_rq **rt_rq;
struct rt_bandwidth rt_bandwidth;
#endif
/* task_group之間的組織關係 */
struct rcu_head rcu;
struct list_head list;
struct task_group *parent;
struct list_head siblings;
struct list_head children;
/* ... */
};
3. 排程程式
排程程式依靠幾個函式來完成排程工作的,下邊將介紹幾個關鍵的函式。
- 主動排程 -
schedule()
schedule()
函式,是程序排程的核心函式,大體的流程如上圖所示。- 核心的邏輯:選擇另外一個程序來替換掉當前執行的程序。程序的選擇是通過程序所使用的排程器中的
pick_next_task
函式來實現的,不同的排程器實現的方法不一樣;程序的替換是通過context_switch()
來完成切換的,具體的細節後續的文章再深入分析。
- 週期排程 -
schedule_tick()
- 時鐘中斷處理程式中,呼叫
schedule_tick()
函式; - 時鐘中斷是排程器的脈搏,核心依靠週期性的時鐘來處理器CPU的控制權;
- 時鐘中斷處理程式,檢查當前程序的執行時間是否超額,如果超額則設定重新排程標誌(
_TIF_NEED_RESCHED
); - 時鐘中斷處理函式返回時,被中斷的程序如果在使用者模式下執行,需要檢查是否有重新排程標誌,設定了則呼叫
schedule()
排程;
- 高精度時鐘排程 -
hrtick()
- 高精度時鐘排程,與週期性排程類似,不同點在於週期排程的精度為ms級別,而高精度排程的精度為ns級別;
- 高精度時鐘排程,需要有對應的硬體支援;
- 程序喚醒時排程 -
wake_up_process()
- 喚醒程序時呼叫
wake_up_process()
函式,被喚醒的程序可能搶佔當前的程序;
上述講到的幾個函式都是常用於排程時呼叫。此外,在建立新程序時,或是在核心搶佔時,也會出現一些排程點。
本文只是粗略的介紹了一個大概,後續將針對某些模組進行更加深入的分析,敬請期待。
相關推薦
【原創】(一)Linux程序排程器-基礎
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(二)Linux程序排程器-CPU負載
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(三)Linux程序排程器-程序切換
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(四)Linux程序排程-組排程及頻寬控制
# 背景 - `Read the fucking source code!` --By 魯迅 - `A picture is worth a thousand words.` --By 高爾基 說明: 1. Kernel版本:4.14 2. ARM64處理器,Contex-A53,雙核 3. 使用工具:S
【原創】(五)Linux程序排程-CFS排程器
# 背景 - `Read the fucking source code!` --By 魯迅 - `A picture is worth a thousand words.` --By 高爾基 說明: 1. Kernel版本:4.14 2. ARM64處理器,Contex-A53,雙核 3. 使用工具:S
【原創】(六)Linux程序排程-實時排程器
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(三)Linux paging_init解析
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(四)Linux記憶體模型之Sparse Memory Model
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(六)Linux記憶體管理 - zoned page frame allocator - 1
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(七)Linux記憶體管理 - zoned page frame allocator - 2
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(八)Linux記憶體管理 - zoned page frame allocator - 3
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(九)Linux記憶體管理 - zoned page frame allocator - 4
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(十三)Linux記憶體管理之vma/malloc/mmap
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【ElasticSearch】(一)初識ES
接觸ElasticSearch一段時間了,前期調研、專案接入、資料同步、列表查詢這些階段都已經經歷了,但是ES在腦海中的整體映像還是模模糊糊,做個梳理,認真地思考一下ElasticSearch中模糊的思維。 &nb
【BFS】(一)抓住那頭牛(poj 3278)
思路 open表 用queue模板 closed表 用MAXN長的一維陣列即可 人的位置是起點,牛的位置是終點。走一步形成雙向邊,跳著走(乘2)形成單向邊,畫出圖。(需要查重。)然後愉快的
C入門【總結】(一)
題記:重新認真學習C入門這段時間,發現自己之前的不足與許多能力裡不達標的地方。通過不斷地練習,今天總結一下前段時間學習內容。 ——————————————————————————————————————————————— 分子與迴圈語句 這部分的內容在C整個部分扮演者重要的角色使用頻
C入門【五】(一)
內容核心 鞏固練習C語言入門語法練習 1.編寫一個程式,可以一直接收鍵盤字元 詳細/* 如果是小寫字元就輸出對應的大寫字元, 如果接收的是大寫字元,就輸出對應的小寫字元, 如果是數字不輸出。 */ 個人看法:這道題我在練習時有兩種思路 1.將輸入的字元先轉化為int型 然後根據 AS
【資訊學奧賽】【C++】(一)賦值語句
一、基本知識 在C/C++中,“=” 在語言中的作用並非是數學意義上的“等於號”,也不表示判斷。 “=”在這裡的意思是賦值:表示把它右邊的值賦給左邊。 一般形式為:變數=表示式 有的時候編譯器會提示不
【Shiro】(一)Shiro初瞭解
在ITOO裡面登入用到了Shrio,一直沒有好好的理解和學習,下面我們就來看一下Shiro到底是什麼東西。 (一)Shiro介紹 shiro是apache的一個開源框架,是一個許可權管理的框
【VC++】(一):MFC在Picture control控制元件中顯示Bitmap
今天在《VC++指紋模式識別系統及演算法概述》一書中,看到有一段程式碼——在Picture Control中顯示Bitmap。把它的程式碼和顯示結果摘出來,作為今天的小小學習成果,鼓勵一下自己。程式碼