现在的位置: 首页 > 综合 > 正文

一个操作系统的实现03之五 3.2.4.3

2013年12月06日 ⁄ 综合 ⁄ 共 1228字 ⁄ 字号 评论关闭

回忆-----关于堆栈

长和短的jmp或call的区别:

对于jmp,仅仅是短跳转对应段内,长跳转对应段间。

而call指令会影响堆栈,并且长跳和短跳对堆栈的影响是不同的。

对于短调用来说,call指令执行时下一条的eip压栈,到ret指令执行时,这个eip将从堆栈中弹出。

对于长调用来说,除了将eip压栈外,还需要将调用者的cs,供ret时使用。

 

通过调用门进行有特权级变换的转移

call一个调用门也是长调用,所以call指令执行前后的堆栈已不再是同一个了。事实上CPU将调用者堆栈A的内容复制到被调用者的堆栈B中。

由于每个任务最多可能在4个特权级间转移,所以每个任务实际上需要4个堆栈。可是,我们只有一个ss和esp,那么发生堆栈切换时,我们该从那获得其余的ss和esp呢?

TSS(Task-State Stack),它是一个数据结构,用来解决这个问题。

图片

 从上图中可以看出,从偏移4到偏移27的3 个SS和3个ESP,当发生堆栈切换时,被调用者(内层,高特权级)的堆栈的SS和ESP就是从这里取得的。因为是从低特权级向高特权级转换,故TSS中没有最外层(最低特权级)的堆栈信息。比如:当前所在的是ring3,当转移至ring1时,堆栈将被自动切换到有ss1和esp1指定的位置。

 

下面来看看低特权级向高变换时,整个转移过程中cpu在整个过程中所作的工作:

1.根据目标代码段的DPL从TSS中选择应该切换至哪个ss和esp;

2.从TSS中读取新的ss和esp。在这个过程中,如果发现ss、esp或者TSS的界限错误都会导致无效的TSS异常(#TS);

3.对ss描述符进行检验,如果发生错误,同样产生#TS异常;

4.暂时性的保存当前ss和esp;

5.加载新的ss和esp,即被调用者堆栈

6.将刚刚保存起来的ss和esp的值压人新栈;

7.从调用者堆栈中将参数复制到被调用者堆栈中。复制参数的数目由调用门中的ParamCount决定;

8.将当前的cs和eip(调用者的)压栈;

9.加载调用门中指定的新的cs和eip,开始执行被调用者过程;

 

由被调用者到调用者的返回过程中,处理器的工作:

1.检查保存在cs中的RPL以判断返回时是否要变换特权级;

2.加载被调用者堆栈上的cs和eip(此时会进行代码段描述符、选择子类型和特权级检验);

3.如果ret指令含有参数,则增加esp的值以跳过参数,此时esp指向指向调用者ss和esp;

4.加载调用者ss和esp,切换到调用者堆栈。这里会进行ss描述符、esp和ss段描述符的检验;

5.如果ret含有参数,增加esp跳过参数(此时是在调用者堆栈中);

6.检查ds、es、fs、gs的值,如果其中哪一个寄存器指向的段的DPL小于CPL(不适用于一致代码段),那么一个空描述符会被加载到该寄存器。

 

综上所述,使用调用门的过程实际上分为两个部分:

一部分从低特权级到高特权级,通过调用门和call实现;

另一部分从高到低,通过ret来实现。

抱歉!评论已关闭.