• 内核保护模式之分段机制


     

    CPU的三种模式

    1982年,intel推出了80286处理器,第一次提出了保护模式,在保护模式下,段寄存器中存储的不再是段基址,而是段选择子。

    真正的段基址存储在描述符高速缓存中,80286处理器访问内存,不需要段寄存器左移加上偏移。

    在x86体系的CPU下,支持三种模式

    1. 实模式:兼容16位CPU的模式,当前的PC系统处于实模式(16位模式)运行状态,在这种状态下软件可访问的物理内存空间不能超过1MB,且无法发挥Intel 80386以上级别的32位CPU的4GB内存管理能力。实模式将整个物理内存看成分段的区域,程序代码和数据位于不同区域,操作系统和用户程序并没有区别对待,而且每一个指针都是指向实际的物理地址。这 样,用户程序的一个指针如果指向了操作系统区域或其他用户程序区域,并修改了内容,那么其后果就很可能是灾难性的。

    2. 保护模式:操作系统所在模式,只有在保护模式下,80386的32根地址线有效,可以寻址高达4G字节的线性内存空间和物理内存空间,可访问64TB的逻辑地址空间(有214个段,每个段最大空间为232个字节),可采用分段管理存储机制分页管理存储机制。这不仅为存储共享和保护提供了硬件支持,而且为实现虚拟存储提供了硬件支持。通过提供4个特权级(R0 ~ R3)和完善的特权级检查制,既能实现资源共享又能保证代码数据的安全及任务的隔离。保护模式下有两个段表:GDT和LDT

    1. 虚拟8086模式:可以模拟多个8086执行多任务

    保护模式的分段机制

    保护模式部分内容:

    保护模式下的分段机制

    保护模式下的分页机制

    CS段寄存器的值,决定特权等级R0 ~ R3

    8086寄存器主要是16位的,共4个CS DS ES SS

    32位处理器内,增加了两个:FS GS

     

    32下的段寄存器又分为两部分,16位可见部分,每一个寄存器还有一个不可见部份,称之为描述符高速缓存器,用来存放段的基地址,范围和属性。

    段选择子概述

    什么是段选择子?

    段寄存器可见部分存储的值我们称之为段选择子,共16位

    13位描述符表索引 1位:T1 TI = 0 GDT TI = 1 LDT 2位:RPL
         

    段寄存器中不可见部分的值,来自于一个叫做描述符表的数组,可见部分的高13位是数组的下标。

    当段寄存器被赋值的时候,实际上会从描述符表中的一个描述符中读取数据,将数据加载至段寄存器不可见部分。

    TI位表明的是查找全局描述表GDT(Global Descriptor Table),还是局部描述表LDT(L**ocal Descriptor Table)。Windows系统并没有使用局部描述符表LDT。

    段描述符

    段描述符的结构:

    typedef struct Descriptor{
       unsigned int base;  // 段基址
       unsigned int limit; // 段限长
       unsigned short attribute; // 段属性
    }

    在保护模式下,增加了很多机制,使得段产生了不少种类:

    • 数据段(用于存储程序,供程序读写)

    • 代码段(用于执行代码)

    • 系统段(用于操作系统提供特殊功能)

    每个段描述能够描述出一段内存从哪里开始,到哪里结束,还能描述这个段是什么类型(代码段、数据段、系统段),当然,也能描述这个段是否可读,是否可写,是否可执行,甚至还能描述这个段的权限是什么,在什么权限下才能使用这一段内存。

    通用断描述符

     

    • 描述符有效位P位: 0无效 1有效

    • 段限长Limit:处理器会把段描述符。两个段限长字段组合成一个20位的值,并根据颗粒度标志G来指定段限长Limit值的实际含义

    • 粒度位G:

      G = 0;Limit的单位是字节,段的大小范围为1字节到1Mb(0~0xFFFFF) , 每个单位为1字节。

      G = 1;Limit的单位是4KB,段的大小范围是1字节到4Gb(0~0xFFFFFFFF), 每个单位为4Kb。

    • 基地址字段Base:描述了一个段的起始位置,由三个部分组成,一共是32位,段基地址可以是0~4GB范围内的任意地址(这同实模式不同,实模式下段基地址要求16字节对齐)。但是,为了让这个程序具有最佳性能,还是建议段基地址对齐16字节边界。

    • S与TYPE:应用程序有数据段和代码段,CPU还有系统段和门描述符,他们用来管理事物、异常和中断。并非所有的描述符都定义一个段,门描述符中存放着一个指向过程入口点的指针,S和Type字段表明了描述符的类型信息。

    S位 S = 1代表是一个代码段或者数据段,S = 0代表是一个系统段
    Type域 一共4位,S为不同代表的含义不同
    • L位:是64位代码段标志,保留此位给64位处理器使用

    • AVL:是软件可以使用的位,通常由操作系统来用,处理器并不使用它

    • DPL:存在于段描述中,描述了访问本段的内存所需要的权限

    数据段描述符

    • B位:

    B = 1,内存寻址是32位,堆栈使用ESP,段的内存最大大小为4GB

    B = 0,内存寻址是16位,堆栈使用SP,段的最大大小位64KB

    • A访问位

    表示该位最后一次被操作系统清零后,该段是否被访问过,每当处理器将该段选择符置入某个段寄存器时,就将该位置1

    • W是否可写

      • 指示段的读写属性

      • W = 0;段不允许写入,否则会引发处理器异常中断

      • W = 1;段允许写入

    • 扩展方向

      • E = 0;表示向上扩展的段(上扩段),逻辑地址中的偏移范围可以从0到Limit

      • E = 1;表示向下扩展的段(下扩段,通常是栈段),逻辑地址中的偏移范围可以从Limit到0xFFFF(B = 0)或者0xFFFF_FFFF(当B = 1)时。

    代码段描述符

     

    • B位:

    B = 1,内存寻址是32位,堆栈使用ESP,段的内存最大大小为4GB

    B = 0,内存寻址是16位,堆栈使用SP,段的最大大小位64KB

    • A访问位

    表示该位最后一次被操作系统清零后,该段是否被访问过,每当处理器将该段选择符置入某个段寄存器时,就将该位置1

    • W是否可写

      • 指示段的读写属性

      • W = 0;段不允许写入,否则会引发处理器异常中断

      • W = 1;段允许写入

    • 扩展方向

      • E = 0;表示向上扩展的段(上扩段),逻辑地址中的偏移范围可以从0到Limit

      • E = 1;表示向下扩展的段(下扩段,通常是栈段),逻辑地址中的偏移范围可以从Limit到0xFFFF(B = 0)或者0xFFFF_FFFF(当B = 1)时。

    代码段描述符

     

    • D位:

    D = 1,默认值是32位的地址和32位或者8位的操作数

    D = 0,默认值是16位的地址和16位或者8位的操作数

    • A(已访问)

    表示该位最后一次被操作系统清零后,该段是否被访问过,每当处理器将该段选择符置入某个段寄存器时,就将该位置1

    • R位(可读)

      • 堆栈必须R = 0,代码段不可读,只能执行

      • R = 1;代码段可读,可执行

      • 在保护模式下,代码段是不可写的

    • C位(一致性

      • C = 0;表示非一致性代码段,这样的代码段可以被同级代码段调用,或者通过门调用。

      • C = 1;表示一致性代码段,可以从低特权级的程序转移到该段执行(但是低特权级的程序仍然保持自身的特权级)

     

    段类型检查

    • 加载段选择符进入段寄存器的时候

      • CS寄存器只能存放可执行段的选择符

      • 不可读可执行段的选择符不能被加载进入数据段寄存器(因为数据段都是可读的)

    • 指令访问一个段,段描述符已经被加载到段寄存器,指令只能用某些预定义的方法来访问某些段

      • 不能写可执行段(代码段不可写)

      • 不能写一个可写位没有设置的数据段

      • 不能读可读位没有设置的可执行段

    段权限检查

    当我们给段寄存器赋值的时候,实际上是从GDT中获取相应的段描述符加载到段寄存器的不可见部分。

    在这个过程中有一个权限检查的问题,请注意,在你给段寄存器赋值的时候就已经进行段权限检查了,如果成功,就说明可以访问这个段

    权限检查的三个概念:

    • CPL:当前代码的执行权限,CS段的B0和B1

    • DPL:存在于段描述符中,描述了访问本段的内存所需要的权限

    • RPL:存在于段寄存器加载时的段选择子中,描述了访问者使用什么样的权限对目标进行访问

     

     

    全局描述符表

    在一个系统中, 描述符的种类有多个, 分别有数据段,代码段, 系统段. 系统段又分为多种,有调用门,中断门,陷阱门,任务门. 因此, 在一个系统中, 描述符是存在多个的. 这些描述符被统一打包存储在内存中, 它们所形成的一个数组被称之为全局描述符表.

    全局描述符的小标则保存于16位的段寄存器中.

    一个16位的段寄存器实际由一下部分组成:

     

    段寄存器实际的长度为96位, 16位的值, 只是寄存器的可见部分, 段寄存器还有80位是隐藏部分 , 这个隐藏部分只能被CPU所操作,无法通过任何指令来操作它.

    这可见部分的16位的值也并非全部用于保存全局描述符的下标, 它被划分为以下格式:

     

    也就是说, 只有13位是用于保存全局描述符表的下标.

    T1 - 用于记录,保存的下标是GDT(全局描述符表)的还是LDT(本地描述符表)的(windows操作系统没有LDT)

    RPL - 当前请求级别 , 用作权限检查. 一共有4个值: 0~3 , 数值越小,权限越大, 0代表最高权限.

    由于段寄存器用于保存段选择子, 因此, 给一个段寄存器赋值,就不单单是赋值一个数字了,例如:

     

    mov ax,2Bh
    mov ds,ax

    这条指令可看成将0x2B赋值给ds寄存器, 实际不是.

    将0x2b的二进制展开: 0000 0000 0010 1011 , 段选择子的格式为: 13 : 1 : 3.

    那么在0x2b这个数中, 描述符表索引,T1位,RPL分别为:

     

    也就说, 0x2b这个数代表的是GDT表中第5个段描述符. 当前请求级别为最低权限的2。

    mov ds,ax这条指令执行之后做了什么?

    CPU执行这条指令后, 会将GDT表中第5个段描述符存储在段寄存器隐藏部分, 将段选择子存储到16位可见部分. 当然, 在做这些之前, CPU还需要做权限检查。

  • 相关阅读:
    9 与python2交互
    8 内置函数(未完成)
    7 事务
    [转]jquery.vTicker(垂直滚动)
    2015年工作总结(2016-02-02)
    jQuery plugin : jqTransform
    JQuery知识总结
    JQuery事件处理的注意事项
    JQuery基本选择器
    [二]JQueryMobile常用的组件介绍
  • 原文地址:https://www.cnblogs.com/TJTO/p/11414726.html
Copyright © 2020-2023  润新知