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