资料来自于哈工大李治军老师操作系统课程
进程=资源+指令执行序列
进程切换需要切换资源以及指令序列,当我们只切换指令序列,而资源公用的话,可以避免进程金环的代价且保留了并发的优点。
用户级线程
实际上就是我们平时所用的协程。调用点由用户进行设置,对于内核来说,不知道存在多个用户级线程,因此,在某一个线程阻塞时,内核无法切换用户级线程,只能切换到另一个进程。
在线程创建以及切换时,只需要保存当时切换的状态就能顺利完成切换工作。每个线程都有各自独立的栈,使用TCB来保存其栈地址,调用Yield时,线程进行切换,将当前esp保存,将要切换的栈地址赋值给esp,弹栈,即可完成线程的切换。
内核级线程
内核管理TCB,负责切换线程,每一个线程都拥有一个用户栈,一个内核栈。
线程进行切换时,TCB切换,内核栈与用户栈都进行切换。
当线程用int中断进入内核,内核栈中压入用户栈的地址,以及CS:IP等信息,如果进行了阻塞,需要进行调度处理,此时进行TCB的切换,内核栈跟着切换,切换到下一个线程运行。
切换过程
-
当调用fork时,会执行INT中断,硬件将SS:SP压入内核栈中,将返回地址压入内核栈中,执行系统调用->system_call
-
将用户态中的所有寄存器压入内核栈中,调用sys_fork,执行完后查看是否能进行调度,如果需要,将ret_from_sys_call压栈,进行调度处理。
-
调度完后,需要执行中断返回,调用ret_from_sys_call, 弹出所有的寄存器,返回继续执行。
-
切换的过程有两种,一种是使用TSS将所有的信息保存下来,及寄存器等一系列的数据,进行切换的时候将TSS的内容给“盖”到CPU上就完成了切换,不过linux和windows都没这么用。第二种是将寄存器的内容放到内核栈中,通过弹栈来完成寄存器的赋值,使用TSS来保存栈段描述符和栈顶指针,在进行切换的时候通过找到对应的TSS来完成栈的切换。
sys_fork
sys_fork实际上是调用copy_process,其参数来自于当前内核栈中压入的信息。
copy_process首先申请一段内存空间作为PCB,然后设置tss相应的内容。内核栈需要申请,用户栈和父进程公用(写时复制)