进程切换时的现场保护问题

进程切换流程

进程的切换只会发生在内核精心定义的点上:schedule()函数。从本质上来说进程的切换由两个步骤完成,或两个标志性动作:

  • 1.切换页全局目录来安装一个新的地址空间,cr3寄存器。
    这里请考虑这样一个问题,一旦页全局目录被切换成其他进程的,那么eip在取下一条指令时通过页表映射的方式还能正常的取到当前执行流的下一条指令吗?毕竟两个进程的页全局目录、页表大部分情况肯定会不一样。
    答案是:进程切换发生在内核态,所有代码的引用的地址空间大于0xC0000000,这部分页表属于主内核页表,而所有进程具有相同的主内核页表(至于为何具有相同主内核也表,见下一节),所以指令可以正常取并执行。
  • 2.切换内核态堆栈与硬件上下文。
    那么切换时的现场保护问题,尤其是SS段寄存器和esp寄存器的保护 。由于进程的切换发生在内核态,通常在进入内核态中断发生时,硬件上下文会被自动保存。

切换问题

一直有一个疑问,在中断发生时上下文切换的时候会发生栈的切换,并且把原来的栈段寄存器SS和esp寄存器的值一起保存到新的内核栈段寄存器SS和esp寄存器指向的内核栈,而且这个时候还不能使用任何的其他寄存器辅助(因为硬件上下文还没有保存,任何对寄存器的引用都会破坏进程的上下文),并且任何的push操作都会改变当前的esp,这个切换过程究竟是如何实现的?

答案

MDD,硬件上下文的切换是硬件自动完成的,不需要软件做。软件做的只是提供好中断号、idtr寄存器、gdtr寄存器、tr寄存器的值。
中断发生时,硬件做了一系列工作,包含获取中断号,从idtr寄存器根据中断号找到中断处理程序入口地址CS:EIP,根据CS及gdtr寄存器找到中断入口代码的段基址及特权位,判断如果特权位发生变化则需要切换到内核栈,切换的过程是从tr寄存器找到当前进程的TSS段,然后把TSS段里保存的当前进程的内核栈SS:ESP放入当前SS:ESP中,并将原来的SS:ESP值push到当前栈中,然后push CS,EIP,EFLAG等寄存器。注意这些都是硬件完成的。而通用寄存器的保护工作应该是由中断处理程序ISR做的。

参见《深入理解Linux内核》