以前写博客都是为了自己能记住,这次写希望别人也能看的懂 写的自己都觉得晦涩了,请多指教
进程切换的原因
1 时钟节拍或者其他进程中断到达并且调度器选择了其他进程运行
2进程发出某些系统调用而操作系统暂时无法完成,如read阅读的页不在页缓存
在多核CPU中,内核内存空间是共享的,每个核心上的CR3寄存器是指向正在这个核上运行的进程的整个虚拟地址空间。 画个图吧
首先说CR3寄存器,页表寄存器指向正在运行进程空间的页表,如果是内核进程就会借用上一个正在运行进程的内核空间(指的是task_struct中的mm和active_mm字段,不懂的要自己看这个内容)也不用进行CR3寄存器的转换(CR3寄存器转换源码目录 \linux-4.15.1\arch\x86\mm\tlb.c)。
为什么进程切换回涉及到三个进程?
看源码
主要是这个宏定义,进入__switch_to_asm函数
在__switch_to_asm函数有三个参数分别是进程结构体prev next prev。上面我们放了重要的__switch_to_asm函数代码行,在__switch_to_asm函数中对prev进程(此时正在运行的进程)进行了内核栈切换。内核进程调用函数是内核的代码段,是被所有进程所共享且相同的。当prev进程在__switch_to_asm寒素中进行了内核栈切换后,又执行了函数返回return prev_p。仔细想想这个返回的prev_p是什么,首先prev_p是局部变量,执行内核代码局部变量存储在内核栈上,return prev_p就是把此时内核栈上的prev_p变量储存在rax中返回。但是重点来了,此时的内核栈已经从进程prev的内核栈经过内核函数update_sp0切换成了next进程的内核栈了,此时return返回取到的prev_p是进程next内核栈上的prev_p,和进程prev内核栈上的prev_p不是一个值。
再仔细想想,内核代码是共享的,__switch_to_asm函数的参数分别是prev_p和next_p这两个参数都是放在当前进程内核栈中的
这么理解进程A切换到B进程,运行到__switch_to_asm函数的return prev_p 此时A的内核栈中存放的是A和B进程 B进程内核栈中存放的是 B进程和 (曾经)把B进程切换出CPU的进程。这是目前A进程 和 B进程内核栈情况。
A是当前进程prev_p B是下一个要运行的进程next_p 在调度函数中执行update_sp0后此时内核栈由A转换成B进程内核栈了,因此return prev_p返回的是B进程内核栈中的prev_p 也就是(曾经)把B进程切换出CPU的进程(这个进程可能是 A C D等等)。
也就是说从switch_to宏(里面会调用__switch_to_asm函数)执行到下一条指令时就已经是进程B在运行了,此时内存已经转换完成,一些寄存器也已经转换完成,sp0也已经转换,此时用的内核栈也是B的内核栈(用户程序的执行断点也保存在内核栈用)。 当下一次切换到A进程运行时,内核层也是,切换到A进程内核栈时switch_to宏的下一条指令开始执行,用户层则是在回到用户层时pop内核栈恢复原来PC的值。
当从A进程切换到B进程时,当回到用户层时,B的内核栈出栈,恢复寄存器的值,也会恢复用户层的指令执行断点。