作者: comcat 发表日期: 2007-09-24 16:29 http://comcat.blog.openrays.org/blog.php?do=showone&tid=455
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的运行不说,这里的问题就是:该如何唤醒之呢??
另外,和该中断共享中断号的中断也会受到影响。