zoukankan      html  css  js  c++  java
  • 操作系统--特权级切换

    有4个层次的特权级,从高到低依次是:0级、1级、2级、3级。切换特权级是指从0级转移到1级、或从1级转移到3级。总之,是指从一个特权级转移到了另外一个不同的特权级。

    学习特权级切换,关键知识点是:

    1. 两个指令calliret
    2. 一个CPU特性:特权级变换时会将一个特权级的堆栈复制到另外一个特权级堆栈。

    从低到高

    只有使用调用门才能从低特权级转移到高特权级,更具体地说,是使用语句call 门选择子

    门描述符

    门选择子的结构和段选择子一致,只不过它指向的是门描述符而不是段描述符。

    门描述符和段描述符占用的内存空间相同,都是8个字节,64个bit。可后者包含的元素是:

    1. 目标代码段选择子。
    2. 在目标代码段中的偏移量。
    3. ParamCount。这是什么?后面再解释。
    4. 门描述符的属性。

    代码

    使用调用门从低特权级转移到高特权级的代码是:

    push	ax
    push	bx
    ;SelectorGate 指向一个高特权级的目标代码段
    call	SelectorGate:0
    

    ParamCount

    入栈了2个元素,2就是上文提到的ParamCount。可以把它理解成函数的参数。在门描述符中,ParamCount占用5个bit位,能表示的最大值是2的5次方-1,即31。这意味着,使用一个调用门,最多能入栈31个元素。

    堆栈复制

    假定,上面的代码的能成功从低特权级代码段L转移到高特权级代码段H。L和H是不同的代码段,各自的堆栈也不同。这不是必须的。L和H共用一个堆栈,也不是不行。这样的话,在H中操作堆栈可能会破坏L中的堆栈(总之,存在这种可能)。所以,代码段拥有独立的堆栈更好。

    那么,问题来了。入栈操作发生在L中,被入栈元素存在于L的堆栈LS,在H中怎么从LS中获取数据呢?也许存在方法,但一定很繁琐。现实中,CPU会自动把LS中的元素复制到H的堆栈中。并非复制LS中的全部元素,而是从LS的栈顶开始,复制ParamCount个元素。这个复制操作发生在call执行的时候。

    示意图

    短调用是段内部的调用,长调用是段之间的调用。长调用才能从低特权级转移到高特权级。

    短调用和长调用示意图之间的差别,仅仅在于后者把cs入栈了。cs是调用者的代码段选择子。

    示意图中的堆栈,是H的堆栈HS。

    短调用
    call执行前----> 高地址
    ss
    esp <-----------堆栈
    参数二(ax)
    参数一(bx)
    call执行后----> eip
    长调用
    call执行前----> 高地址
    ss
    esp <-----------堆栈
    参数二(ax)
    参数一(bx)
    cs
    call执行后----> eip

    CPU工作流程--调用门

    调用门的运行过程,涉及LS和HS两个堆栈。上面的示意图只画出了HS的状态,不足以阐述调用门的整个流程,本小节再用文字详细说明调用门的运行过程。

    TSS

    先介绍一个新东西,TSS,任务状态寄存器,一个寄存器。CPU在切换任务的时候,能用TSS给切换下来的任务建立一个快照,这个快照包含一个任务的所有寄存器数据。不过,大部分操作系统嫌弃这种切换方式太消耗时间,并没有完全按照CPU厂商的意图使用TSS。Linux系统也是如此。

    在TSS中,包含ss0、esp0ss1、esp1ss2、esp2三组数据,正好对应0特权级、1特权级、2特权级三个层次的特权级。

    特权级不是有四个层次吗?为什么没有对应3特权级的那一组数据呢?TSS的作用是为低特权级向高特权级转移时提供高特权级的堆栈。3特权级是最低特权级,没有更低的特权级向它转移。

    假如,从3特权级向0特权级转移,CPU会从TSS中选择ss0、esp0作为0特权级代码段的堆栈。

    流程

    使用call SelectorGate:0实现低特权级向高特权级转移的流程如下(不叙述CPL等特权级检查流程,假设满足这些条件):

    1. 执行call语句时。
    2. 把当前代码段的堆栈LS的ss_old、esp_old临时保存起来。
    3. 从门选择子指向的目标代码段中获取DPL,根据DPL的值,在TSS中选择ss、esp,将堆栈指向的新堆栈。例如,DPL的值是0,选择ss0、esp0
    4. ss_old、esp_old中的入栈到新堆栈中。
    5. 把LS中的栈元素复制到新堆栈中。复制规则是:从LS的栈顶开始,复制ParamCount个元素。
    6. 依次入栈cs、eip

    小结

    从低特权级转移到高特权级的方法是,使用调用门,具体语句是call SelectorGate:0

    不能使用jmpjmp只能在实现短调用,在同一个特权级转移。因为jmp不会将下一条指令的地址存储到新特权级的堆栈中,这意味着,jmp是一个有去无回的指令。从低特权级转移到高特权级的场景,一般是用户进程求助操作系统完成某种功能,需要再次返回用户进程。

    从高到低

    电脑开机后,CPU的特权级是0,这是从BIOS那里寄存下来的。这种知识似乎无用,懒得多说。

    前文讲了从低特权级切换到高特权级的方法,可CPU从开始工作的那一刻起,一直都是在0特权级。这样说来,如果要动手实现从低特权级转移到高特权级,应该先实现从高特权级转移到高特权级。

    怎么实现?

    前文已经埋下了伏笔,call指令会将调用者(低特权级)的cs(选择子)eip(偏移量)入栈到被调用者(高特权级)的堆栈中。从堆栈中获取调用者(低特权级)的cs(选择子)eip(偏移量),就能从高特权级转移到低特权级。完成这项工作,只需一个指令而已,iretf

    代码

    ;特权级是3
    push ax
    push bx
    call SelectorGate:0
    
    mov ax, 5
    

    call SelectorGate:0调用下面的代码。

    ;特权级是0
    ;调用门选择子指向的目标代码段,高特权级代码段
    ;一些操作,示范,不必理会具体功能
    mov al, 'A'
    mov ah, 0Fh
    mov [gs:(80*20+20)*2], ax
    
    iretf
    

    iretf执行后,CPU会继续执行mov ax, 5mov ax, 5就是被调用者堆栈中的cs:eip指向的指令。

    示意图

    还是画两个和上面call指令类似的堆栈图吧。

    短调用返回

    ret执行后 高地址
    调用者ss
    调用者esp
    参数一
    参数二
    ret执行前 调用者eip

    长调用返回

    retf执行后 高地址
    调用者ss
    调用者esp
    参数一
    参数二
    调用者cs
    retf执行前 调用者eip

    工作流程--iretf

    长调用返回使用iretf指令将上面的堆栈S中的元素出栈。具体流程如下:

    1. 从S中获取调用者cs、调用eip,并加载到当前cs、eip中。
    2. iretf含有参数,esp增加参数个数跳过这些参数。
    3. 继续出栈,把调用者esp、调用者ss加载到当前espss中。此时会切换到调用者堆栈。
    4. iretf含有参数,增加esp的值以跳过参数(在call前,参数也压入了调用者堆栈中)。
    5. 最后执行cs:eip代码。

    iretf

    1. RET:可能是近返回,也可能是远返回。
    2. RETN:近返回指令。
    3. RETF:远返回指令。
    4. RET6:子程序返回后,(SP)←(SP) + 6。

    没有找到iretf的权威资料,上面的资料也没有验证。先搁置吧。

    疑问

    不使用调用门能不能转移特权级?

    使用jmp只能在同特权级跳转。

    一致代码段,call只能转移到比当前特权级高或相等的特权级。

    非一致代码段,call只能在相同特权级跳转。

    什么时候检查特权级

    没有弄明白。好像不重要。

    求道之人,不问寒暑。
  • 相关阅读:
    SQL Server 之 内部连接
    SQL Server 之 子查询与嵌套查询
    ASP.NET MVC5高级编程 之 视图
    ASP.NET MVC5高级编程 之 路由
    jQuery返回顶部实用插件YesTop
    jQuery照片墙相册
    js功能实现的特效--距离新年还有多少天
    圣杯布局小结
    等高分栏布局小结
    jQuery设置内容和属性方
  • 原文地址:https://www.cnblogs.com/chuganghong/p/14471990.html
Copyright © 2011-2022 走看看