Linux 上下文切換分析筆記 (MIPS)


1. 內核棧切換 (MIPS)

調度切換至一個進程時,根據 task_struct->thread_info 的值設置 *kernelsp(當前正在運行進程之內核棧棧底),其值爲 thread_info + THREAD_SIZE - 32(MIPS 下,使用 set_saved_sp 宏)。


2. 異常、中斷寄存器的保存 (MIPS)

使用SAVE_SOME 保存上下文時,如發現從用戶態切入核心態,則首先用 get_saved_sp 宏,將*kernelsp 置入sp。然後在內核棧上分配 PT_SIZE(=sizeof(struct pt_regs)) 大小的空間,作爲上下文的保存空間。保存時所有數據精心組織,最後就是一個 struct pt_regs 結構。

若是用戶態 --> 內核態,則 k0 = sp, sp = *kernelsp - PT_SIZE,store k0, PT_R29(sp),保存其它寄存器。

若是內核態 --> 內核態,直接 k0 = sp, sp = sp - PT_SIZE,store k0, PT_R29(sp),然後保存其它寄存器。


3. 任務切換上下文的保存 (MIPS)

時鐘中斷後使用 SAVE_SOME 在內核棧/用戶棧(取決於當時所在模式)上保存 $0, $2, $3, $4~$7, $8~$9(64bit), $25, $28, $29, $31, STATUS, CAUSE, EPC。

後在 switch_to 中保存正在運行任務的上下文:

保存 STATUS,使用 cpu_save_nonscratch 保存$16~$23, $29(sp), $30,以及 $31, 有FPU還要 fpu_save_double 保存FPU的寄存器。所有都保存於thread_struct 結構中,該結構爲 task_struct 的一部分。

這些保存的是 switch_to 前後的上下文


然後將將要運行的任務上下文加載:

$28 <---- &thread_info
cpu_restore_nonscratch 恢復 $16~$23, $29(sp), $30
*(kernelsp) <---- &thread_info + THREAD_SIZE - 32
恢復 thread_struct 中保存的 STATUS(bit 0, bit 8~15 用當前STATUS值替換)

現在恢復時也在 switch_to 前後,神不知鬼不覺的替換了,所有操作都是由switch_to調用葉函數resume完成。

do_IRQ 返回後,sp恢復(減多少,對稱的加多少,因此與初值無關,最終指向新進程的 pt_regs 結構)ref_from_irq 則時鐘中斷返回(當時被中斷時的環境),然後 eret 跳回到用戶態(或者被時鐘中斷的核心態)繼續運行。


4. switch_to 爲何不需保存$0~$15 $24~$27 (MIPS)

假如內核要從進程A切換到進程B,流程大概是這樣:

進程A --> 時鐘中斷 --> schedule --> switch_to(resume) --> schedule 返回 --> ret_from_irq --> 進程B

switch_to 保存於 A task_struct->thread_struct 中的狀態是整個調用鏈中的 switch_to 宏附近的處理器狀態

因此將 sp 指向保存於 B task_struct->thread_struct 中的 sp 時,實際上就相當於恢復到當時進程B在switch_to前後的狀態:

進程B --> 時鐘中斷 --> schedule --> switch_to

switch_to 是一個宏,其中調用了,位於 arch/mips/kernel/r4k_switch.S 中的一個葉函數(不改變靜態寄存器的值,不用壓棧、出棧)resume,因此進入 resume 前,ABI 規定的一些非靜態寄存器的值就再也不用了,故這些非靜態值無需保存。

至於靜態寄存器的值,函數用之前都會保存於棧上,最後恢復之,子函數調用不會改變其值。因此靜態寄存器保存的是當時運行狀態的一部分。如這種情況:

schedule 中編譯器用 s0 保存一個重要的狀態變量,因此進入schedule首先保存s0的值,使用 s0 參與運算,switch_to 後,又要根據 s0 判斷進一步的動作。

這個時候就要將 s0 恢復爲進程B當時在此點的值。總之注意,switch_to 後所有操作延續的是進程B的:

schedule 返回 --> ret_from_irq --> 進程B


5. 中斷處理時可否睡眠問題

Linux 設計中,中斷處理時不能睡眠,這個內核中有很多保護措施,一旦檢測到內核會異常。

當一個進程A因爲中斷被打斷時,中斷處理程序會使用 A 的內核棧來保存上下文,因爲是“搶”的 A 的CPU,而且用了 A 的內核棧,因此中斷應該儘可能快的結束。如果 do_IRQ 時又被時鐘中斷打斷,則繼續在 A 的內核棧上保存中斷上下文,如果發生調度,則 schedule 進 switch_to,又會在 A 的 task_struct->thread_struct 裏保存此時時種中斷的上下文。

假如其是在睡眠時被時鐘中斷打斷,並 schedule 的話,假如選中了進程 A,並 switch_to 過去,時鐘中斷返回後則又是位於原中斷睡眠時的狀態,拋開其擾亂了與其無關的進程A的運行不說,這裏的問題就是:該如何喚醒之呢??

另外,和該中斷共享中斷號的中斷也會受到影響。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章