Linux 上下文切換分析筆記 (MIPS)
阿新 • • 發佈:2019-02-19
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的執行不說,這裡的問題就是:該如何喚醒之呢??
另外,和該中斷共享中斷號的中斷也會受到影響。
排程切換至一個程序時,根據 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的執行不說,這裡的問題就是:該如何喚醒之呢??
另外,和該中斷共享中斷號的中斷也會受到影響。