Linux 进程切换 0.11与2.4的比较

0.11的进程模型与2.4不同。0.11中每个进程都有一个tss结构,用于保存/恢复进程切换时的现场(主要都是一些寄存器);而2.4中每个cpu使用一个tss结构,多个进程将共用一个tss。因此,0.11的进程切换使用了CPU的硬件特性;而2.4的切换则是一种软切换,从CPU的角度来看,实际上没有进程切换,只不过是修改了若干寄存器而已。

看进程切换的代码必须注意一点:内核空间和用户空间。所有进程的内核空间是共享的,即内核空间线性地址及对应的物理地址是相同的;而用户空间是各自独立的。此外,各个进程的内核态堆栈是各自独立的。

进程切换必须得从中断说起。进程创建涉及系统调用,系统调用实际也是一种中断。进程切换涉及时钟中断。一般来说,进入中断程序(不论哪种中断)后首先需要保存一些寄存器(即现场保护),在退出中断程序之前需要恢复一些寄存器(即现场恢复),这两个过程在0.11和2.4内核中都是一样的。

0.11内核和2.4内核代码里均有一个方法——switch_to,此方法负责进程切换。0.11内核的代码相对简单,因为她使用了CPU的硬件特性,因此只需通过一个长跳转指令,跳转的新进程的tss即可。剩下的工作就交给CPU了,CPU首先需要将执行跳转指令时的寄存器数据保存到原进程的tss中,然后将用新进程的tss恢复寄存器数据,之后就接着执行eip所指向的下一条指令即可。

2.4内核的切换过程稍微复杂一些,实际上只是看上去代码量多些,但之际执行效率可能会更好。假设系统只有一个CPU,则2.4内核所有进程就共用一个tss。因此在切换时需要先保存原进程的几个寄存器值。从代码来看,也就保存了eip、esp,这两个寄存器太重要了。其中eip值存储的是原进程下次被切换执行时的下一条指令地址。之后,就是用新进程任务数据结构中的的esp值来恢复esp寄存器。到此基本就完成切换了。看起来代码很多,但实际涉及的数据保存恢复很少,原因就在于每个进程在被中断时已经保存了现场,而中断返回又恢复了现场。所以进程的切换重点就是对eip和esp等关键寄存器的操作。

0.11内核看似代码简单,但由于它也涉及中断现场保存和回复,此外还要进行tss的保存和恢复,因此实际性能反而不如2.4的软切换方式。由此可见,tss中的那一堆寄存器存储恢复操作显得很多余,实际上比较有用的寄存器也就是esp、eip以及标志寄存器等。2.4采用一个CPU一个tss的方式非常明智(tss是不能省略的,要不就不能进行用户态和内核态的切换了)。

0.11与2.4的进程创建过程也不一样。0.11在创建新进程时需要设置新进程任务数据结构中的tss。之后就完事了,新进程在新建之后其内核态堆栈是空的,当新进程第一次被切换执行时,实际上直接就切换到用户态了,因为tss里存储的是父进程执行fork时的现场状态。
2.4内核就不太一样了,因为所有进程都共用一个tss,所以不可能把父进程的现场状态存储在tss中。2.4所采用的方法是将父进程执行fork时的现场状态保存到新进程的内核态堆栈上( 见arch/i386/kernel/process.c中的copy_thread方法。每个进程的内核态堆栈是独立的)。当新进程第一次被切换执行时并不立即进入用户态,而是跳转到中断返回的代码,将内核态堆栈上的数据恢复到寄存器之后执行中断返回。

你可能感兴趣的:(Linux 进程切换 0.11与2.4的比较)