進(jìn)程調(diào)度的時(shí)機(jī)
明確一點(diǎn):一般來說,進(jìn)程調(diào)度都是發(fā)生在進(jìn)程外的(即進(jìn)程運(yùn)行的時(shí)候會(huì)持續(xù)執(zhí)行代碼),當(dāng)執(zhí)行代碼中斷跳到其他代碼段(系統(tǒng)調(diào)用函數(shù),中斷處理函數(shù)等)時(shí)會(huì)觸發(fā)進(jìn)程調(diào)度函數(shù)(schedule)使得進(jìn)程(此時(shí)在內(nèi)核態(tài))得以切換,切換的實(shí)際是兩個(gè)進(jìn)程的內(nèi)核堆棧的切換。
一般有四個(gè)進(jìn)程調(diào)度的時(shí)機(jī)
1. 用戶調(diào)用特點(diǎn)系統(tǒng)調(diào)用主動(dòng)讓出CPU
2. 中斷處理函數(shù)返回用戶態(tài)時(shí)固定時(shí)機(jī)點(diǎn)
3. 內(nèi)核線程主動(dòng)調(diào)用schedule
4. 中斷處理函數(shù)主動(dòng)調(diào)用schedule
中斷類型
- 硬中斷(Interrupt)
CPU兩個(gè)引腳(可屏蔽中斷,不可屏蔽中斷),高電平表示有中斷。類似時(shí)鐘,鍵盤等會(huì)使用硬中斷方式。 - 軟中斷(Exception)
- 故障(Fault)
- 退出(Abort)
- 陷阱(Trap)
系統(tǒng)調(diào)用屬于該類軟中斷
進(jìn)程上下文切換
進(jìn)程上下文包括
- 用戶地址空間:程序代碼段,數(shù)據(jù)段,用戶堆棧等內(nèi)存信息
- 控制信息:進(jìn)程描述符、內(nèi)核堆棧
- 硬件上下文:寄存器
關(guān)鍵寄存器切換
- CR3 地址空間寄存器
- ESP
- EIP
thread_struct thread
每個(gè)進(jìn)程描述符都有一個(gè)類型thread_stuct的thread字段,該字段在進(jìn)程被切出去的時(shí)候保存了當(dāng)時(shí)硬件上下文信息
切換步驟
- 切換CR3,切換后兩個(gè)進(jìn)程相同的虛擬地址對(duì)應(yīng)了不物理地址
- 切換內(nèi)核堆棧和硬件上下文信息
代碼分析
schedele函數(shù)調(diào)用context_switch進(jìn)行上下文切換
context_switch(struct rq *rq, struct task_struct *prev, struct task_struct *next)
{
switch_mm(oldmm,mm,next) //內(nèi)存CR3寄存器切換
switch_to(prev, next, prev) //內(nèi)核堆棧和硬件上下文切換
}
switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk)
{
load_cr3(newxt->pgd);
}
//簡(jiǎn)化代碼
switch_to(prev,next last)
{
pushfl
pushl %ebp //0
prev->thread.sp = %esp //1
%esp = next->thread.sp //2
prev->thread.ip = $1f //3
push next->thread.ip //4
jmp _switch_to //5
1f:
popl %ebp //6
popfl
}
重點(diǎn)講解switch_to函數(shù)
0
將當(dāng)前進(jìn)程(prev)的硬件上下文和內(nèi)核堆棧地址保存在當(dāng)前進(jìn)程(prev)內(nèi)核堆棧中,當(dāng)當(dāng)前進(jìn)程(prev)再次被調(diào)度的時(shí)候進(jìn)行恢復(fù)
1
保存內(nèi)核堆棧地址到當(dāng)前進(jìn)程(prev)的thread結(jié)構(gòu)體中,上面有提到
2
將當(dāng)前CPU的ESP寄存器(堆棧寄存器)切換為下一個(gè)進(jìn)程(next)的內(nèi)核堆棧地址。由于進(jìn)程的內(nèi)核堆棧和進(jìn)程控制塊(PCB,即進(jìn)程描述符)保存在連續(xù)的8K內(nèi)存空間,因此當(dāng)CPU的ESP寄存器切換到下一個(gè)進(jìn)程(next)的內(nèi)核堆棧地址時(shí),可以理解為當(dāng)前已經(jīng)切換到了next進(jìn)程。
3
將上一個(gè)進(jìn)程(prev)的thread.ip設(shè)置為1f的地址,這是當(dāng)該進(jìn)程再次被調(diào)度時(shí)開始執(zhí)行的代碼行。
4&5
在next進(jìn)程的內(nèi)核堆棧中壓入thread.ip的代碼段地址(即該進(jìn)程在上次被調(diào)度出去在第三步保存的地址),然后使用jmp _switch_to來控制CPU的EIP寄存器跳轉(zhuǎn)到1f的代碼段地址??梢詫⑦@兩步(4、5)理解為將地址裝載然后控制EIP跳轉(zhuǎn)的功能。
6
此時(shí)進(jìn)入進(jìn)程恢復(fù)階段,對(duì)應(yīng)第0步?;謴?fù)進(jìn)程的堆棧,硬件上下文信息。