為什么要調度?為了最大限度的利用CPU。
調度相關結構體
task_struct
我們先把task_struct中和調度相關的結構拎出來:struct task_struct {
......
/*
*調度類。用 sched_class 對調度器進行抽象
*Stop調度器:stop_sched_class
*Deadline調度器:dl_sched_class
*RT調度器:rt_sched_class
*CFS調度器:cfs_sched_class
*IDLE-Task調度器:idle_sched_class
*/
const struct sched_class *sched_class;
//CFS調度實體
struct sched_entity se;
//RT調度實體
struct sched_rt_entity rt;
......
#ifdef CONFIG_CGROUP_SCHED
//任務組(在每個CPU上都會維護一個CFS調度實體、CFS運行隊列; RT調度實體,RT運行隊列)
struct task_group *sched_task_group;
#endif
//DL調度實體
struct sched_dl_entity dl;
......
/*
*進程的調度策略,有6種。
*限期進程調度策略:SCHED_DEADLINE。DL調度器
*實時進程調度策略:SCHED_FIFO,SCHED_RR。RT調度器
*普通進程調度策略:SCHED_NORMAL,SCHED_BATCH,SCHED_IDLE。CFS調度器
*/
unsigned int policy;
......
}
- struct sched_class 對調度器進行抽象,一共分為5類:
- Stop調度器:優(yōu)先級最高的調度類,可以搶占其他所有進程,不能被其他進程搶占;
- Deadline調度器:使用紅黑樹,把進程按照絕對截止期限進行排序,選擇最小進程進行調度運行;
- RT調度器:為每個優(yōu)先級維護一個隊列;
- CFS調度器:采用完全公平調度算法,引入虛擬運行時間概念;
- IDLE-Task調度器:每個CPU都會有一個idle線程,當沒有其他進程可以調度時,調度運行idle線程;
- unsigned int policy 進程的調度策略有6種,用戶可以調用調度器里的不同調度策略:
- SCHED_DEADLINE:使task選擇Deadline調度器來調度運行
- SCHED_RR:時間片輪轉,進程用完時間片后加入優(yōu)先級對應運行隊列的尾部,把CPU讓給同優(yōu)先級的其他進程;
- SCHED_FIFO:先進先出調度沒有時間片,沒有更高優(yōu)先級的情況下,只能等待主動讓出CPU;
- SCHED_NORMAL:使task選擇CFS調度器來調度運行;
- SCHED_BATCH:批量處理,使task選擇CFS調度器來調度運行;
- SCHED_IDLE:使task以最低優(yōu)先級選擇CFS調度器來調度運行;
- struct sched_entity se;采用CFS算法調度的普通非實時進程的調度實體
- struct sched_rt_entity rt;采用Roound-Robin或者FIFO算法調度的實時調度實體
- struct sched_dl_entity dl; 采用EDF算法調度的實時調度實體
分配給CPU的task,作為調度實體加入到運行隊列中
runqueue 運行隊列 struct rq {
......
//三個調度隊列:CFS調度,RT調度,DL調度
struct cfs_rq cfs;
struct rt_rq rt;
struct dl_rq dl;
......
//idle指向空閑內核線程, stop指向遷移內核線程
struct task_struct *curr, *idle, *stop;
......
}
三個調度隊列:
- struct cfs_rq cfs; CFS調度隊列
- struct rt_rq rt; RT調度隊列
- struct dl_rq dl; DL調度隊列
調度流程
調度的本質就是選擇下一個進程來運行,調度的過程分為兩步:-
1. 設置調度標記
那么,什么時候設置TIF_NEED_RESCHED呢 ?
- scheduler_tick 時鐘中斷
- wake_up_process 喚醒進程的時候
- do_fork 創(chuàng)建新進程的時候
- smp_send_reschedule 負載均衡的時候
- set_user_nice 修改進程nice值的時候
關于是否需要設置TIF_NEED_RESCHED的依據(jù)涉及到具體的調度算法,等我們講到具體調度器時再詳細講。
-
2. 執(zhí)行調度
- 用戶態(tài)搶占
- 內核態(tài)搶占
進程切換上下文 context_switch
通過上面我們知道執(zhí)行調度的時候發(fā)生在 _schedule 函數(shù)里。重點是其中的兩個函數(shù),一個是選擇需要切換任務的 pick_next_task,另外一個是完成進程上下文切換 context_switch。
關于選擇task的策略涉及到不同的調度類,等我們講到具體調度器的時候再展開,這里重點講下上下文切換的函數(shù) context_switch,進程上下文切換主要涉及到兩部分主要過程:進程地址空間切換和處理器狀態(tài)切換:
- 進程的地址空間切換
- 寄存器狀態(tài)切換