Linux 0.11实验笔记之基于内核栈切换的进程切换

OS需要为进程切换做的那些东西

  在linux 0.11中,进程切换非常简单!只需要一句ljmp就能够做到,因为其中需要做的所有东西intel都已经让cpu帮你做好了,但不幸的是,这一条指令非常耗时,大概需要两百个时钟周期(约0.2秒)。

  让我们先弄清楚哪些东西改变了才算是完成了进程切换后再谈谈CPU帮我们做了什么,因为这样子才比较顺理成章。

进程切换究竟做了些什么?

  先来看看ljmp指令中cpu做了些什么。
Linux 0.11实验笔记之基于内核栈切换的进程切换_第1张图片
  CPU会根据TR任务寄存器找到当前任务TSS所在的内存空间,然后把当前CPU上的东西全部都复制到那一片内存,这就叫进程运行环境的保存。然后CPU会把新的tss地址也就是刚才ljmp相对应的地址加载到TR中,CPU就能通过TR找到新进程的TSS,然后将新地址中TSS的内容再扣到CPU中,这就完成了进程运行环境的恢复,可以说是非常简单的。

  然而我们要把上面的进程切换换成内核栈切换,需要做的操作有下面几个需要注意:
1.current指针要指向新的进程PCB
  这个很容易理解就不解释了。

2.内核栈需要切换
  每个进程都需要一个属于自己的内核栈来存放数据,所以我们必须在切换进程的时候将内核栈切换才能让新进程找到自己之前保存过的数据。

3.修改TSS0的内核栈指针
  这个比较难理解所以我会具体讲一下
  当程序在用户态执行int指令的时候会转入内核中去工作。我们知道,一个进程的用户数据和内核数据是分开的,用户数据有存在于用户空间的数据栈,内核数据有存在于内核空间数据栈。那么当系统从用户跳入内核中去执行的时候该如何找到存在于内核中的数据栈呢?
  答案就是TR寄存器指向的TSS字段中保存的ss与esp0字段。这两个字段标识了该进程处于内核中的数据栈位置。对于我们人来说,如何区别哪个进程在运行利用的是current指针,但是cpu并不懂这些,所以它利用的是TR寄存器来获知现在执行的进程是哪个。也就是说,TR寄存器指向哪个进程它就认为哪个进程正在运行。当CPU从用户态跳入到内核态的时候也会和我们一样碰到内核栈数据在哪里的问题,但它知道该怎么做,就是去往TR指向的TSS段中的数据进行寻找,因为它认为的当前进程的数据都保存在那里,找到相应的字段后就把它们放入相应的寄存器中。而我们在做基于内核栈切换的进程切换的时候并没有对TR寄存器做任何的修改,所以为了达到让CPU能够找到我们指定的内核栈位置的目的就不得不“欺骗”一下CPU,把位于TSS0中内核栈字段的位置改为新进程内核栈位置,这样它就能乖乖地把我们想要的栈载入到寄存器中了!

4.加载新进程的ldt
  每个进程都有各自不同的ldt,把新的加载进来后系统才能找到属于那个进程中的各种代码和数据

5.加载用户空间的数据段地址
movl $0x17,%ecx
mov %cx,%fs
  其实仔细观察一下就会发现,所有用户空间的数据段地址都是0x17,那么我们为什么要把重复的0x17不嫌麻烦地再加载进去一遍呢?
  我在第一次接触汇编看到段寄存器上一个个值的时候兴奋异常,因为当时的我以为自己终于能直接和物理内存打交道了,以为寄存器上显示的值就是内存中的物理地址位置,直到这次的实验。。。
  实际上,段寄存器给我们看到的数值并不是真的物理地址,而是逻辑地址,更确切地说是段选择符。一个段寄存器分为两个部分,分别是可见部分和隐藏部分,可见部分顾名思义就是我们看到的东西,它存储的是段选择符,而后面的隐藏部分存储的东西即为段选择符指向的段描述符。真正用于找到实际物理地址的是段描述符(实际上在找到物理地址之前还要进行段页的转换)。
  了解了这个原理之后我们就不难知道为什么要重复加载0x70这个地址了吧,为的就是更新段寄存器后面的隐藏部分!
Linux 0.11实验笔记之基于内核栈切换的进程切换_第2张图片
  好了我们现在已经把几乎所有新进程需要的东西都设置好了,可是还漏了一个最重要的的东西,CS和IP!不设置这两个东西怎么可能能让CPU跑到我们希望它去到的地方继续取值执行呢!?
  实际上我们并不需要写有关CS:IP的任何代码,而是依靠在函数结束的时候系统自动从栈中把CS:IP弹出来,所以我们只要提前把需要运行地方的CS:IP放入栈中即可。这里说起来很复杂最好亲自去做一下实验才能有所体会。

最后的梳理

  最后我们简单做一下梳理。当系统处于用户态进行int中断的时候CPU(标记1)会通过TR寄存器找到相应的TSS,然后从中取出内核栈地址加载进相应寄存器,这样我们就算成功加载了内核栈。

  标记2表示的是基于内核栈的切换。

  标记3表示使用了iret指令从内核态回到用户态,同时将下图除了出错码以外的东西全部弹出,它们都是在int中断的时候压入的。压入这些是为了保存系统处于用户态时的环境,现在使用iret弹出这些就类似于恢复用户态运行环境。
Linux 0.11实验笔记之基于内核栈切换的进程切换_第3张图片

你可能感兴趣的:(Linux 0.11实验笔记之基于内核栈切换的进程切换)