MIPS Linux 上下文切换分析笔记

来自Jack's Lab
2014年11月11日 (二) 07:47Comcat (讨论 | 贡献)的版本

(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳转到: 导航, 搜索
1. 内核栈切换

调度切换至一个进程时,根据 task_struct->thread_info 的值设置 *kernelsp(当前正在运行进程之内核栈栈底),其值为:

 thread_info + THREAD_SIZE - 32(MIPS 下,使用 set_saved_sp 宏)



2. 异常、中断寄存器的保存

使用 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. 任务切换上下文的保存

时钟中断后使用 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

假如内核要从进程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的运行不说,这里的问题就是:该如何唤醒之呢??

另外,和该中断共享中断号的中断也会受到影响。


















个人工具
名字空间

变换
操作
导航
工具箱