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