zoukankan      html  css  js  c++  java
  • 「学习笔记」对实模式/保护模式的三种访问内存机制的理解

      假设x86处理器执行以下的指令

    1 mov ds, cx    ;其中假设 (cx) = 0x0010

      主要讨论实模式和保护模式:这包括16位模式即实模式,32位模式即保护模式,以及32位模式下的16位兼容模式(首先需要明确的是保护模式有着不同的内存访问机制,简单地说就是传统的涉及段的几个寄存器如CS,DS,ES,SS等被解释为不同的内容——实模式解释为段寄存器,保护模式解释为段选择子,可以先理解为换一种叫法,但要注意它们保存的内容也会有着不同的处理方式)

     

      (在讨论之前我想先说说下面我用红字标出来的内容是相对于其他模式寻址机制不同的地方,其次我会用 “斜体的引用” 注解新引入的概念)

      1、16位模式/实模式:

        执行 mov ds, cx 时,DS解释为段寄存器,0x0010作为段基址

        先直接将0x0010赋给DS

        然后当访问涉及内存的指令如 mov [0x00], al 时,处理器自动完成段基址0x0010左移4位再加上偏移地址(这个例子是0x00)

        最后访问该过程得到的20位物理地址(这个例子是0x00100)便是该模式访问内存的过程

      2、32位下的(兼容)实模式——比如保护模式下运行8086程序,或者说32位模式下运行16位程序

        执行 mov ds, cx 时,DS仍然解释为段寄存器,0x0010也仍然作为段基址

        先直接将0x0010赋给DS

        然后当访问涉及内存的指令如 mov [0x00], al 时,处理器也是自动完成段基址0x0010左移4位,不过之后处理器会把左移4位后产生的地址传送到描述符高速缓存器(此时描述符高速缓存器的内容是0x100),再加上偏移地址(该例子中是0x00)

        最后访问该过程得到的20位物理地址(这个例子是0x00100)便是该模式访问内存的过程

    这里简单解释一下“描述符高速缓存器”:

      32位模式下寻址空间必然会增大,这就使得原来实模式(16位)寻址机制需要扩展,相应地段寄存器也需要扩展。简单地说就是在32位模式下,段寄存器除了包括原来的CS,DS这一类传统的段相关的寄存器,还包括一个对程序员来说不可见的部分——这个不可见部分就是描述符高速缓存器,它的作用就是缓存段基址(你可以能会疑惑为什么保护模式比实模式要多一步缓存,这个缓存的意义是什么?别急,在说完下面保护模式的寻址之后,我会在本博文最后附上我的理解)。

      3、32位模式/保护模式:

        执行 mov ds, cx 时,DS此时解释为段选择器,0x0010此时作为段选择子

    这里也简单解释一下“段选择子”:

      段选择子:段描述符段描述符表中的索引号(可以先认为表项在表中的项号,但是实质上段选择子除了索引号还有其他组成部分)

     

    那段描述符和段描述符表又是什么呢?

      这得先从保护模式是什么讲起。一个程序在实模式下设置的数据段是任意的,也就是说可以设置在1MB范围内的随意一个区域——这就使得程序可以在即使不属于它自己的内存单元的访问也没有一点障碍,这是非常不安全也是不合理的。

      而保护模式对实模式一个很重要的改进就是,保护模式让每个程序都先定义它自己能够访问的内存区域,这样每当一个程序想要非法地访问不属于它自己的区域时能有依据地被制止。而每个程序能够访问的内存区域实质上是其定义的数据段、代码段、栈段等,这些段的定义信息就被保存于内存的某个区域内

      这个区域是一堆这样的“段定义信息”,这个区域便是段描述符表(Global Descriptor Table, GDT),而这一个个组成整个表的表项就是段描述符(Segment Descriptor)

        此时处理器会先将段选择子(段选择子可以指示索引号)乘以8作为偏移地址,再同GDTR(Global Descriptor Table Register,段描述符表寄存器)中提供的GDT表的基地址相加,求得结果指向GDT表中某个表项(即某个段描述符),并加载表项中的该段的基地址到描述符高速缓存器

    这里也简单解释一下为什么要“段选择子乘以8”,以及“GDTR”是什么

      段选择子乘以8:在段描述符表中,每个表项长8字节,每个表项从0开始编排索引号。所以当我们访问GDT的时候,先读取GDT表头地址(也就是索引号#0的地址),这样每个表项的地址便可用相对GDT表头地址来表示——比如我们读取到GDT表头地址为0xXXXX_XXX0,则索引号#0地址为0xXXXX_XXX0,索引号#1地址为0xXXXX_XXX8(前面是索引号#0,长8字节)...——这样便可以用段选择子(段选择子可以指示索引号)乘以8来找出段描述符的地址,这个地址便是段描述符相对于GDT表头的地址

      GDTR(Global Descriptor Table Register,段描述符表寄存器):主要作用是用来找到GDT,该寄存器保存有GDT在内存中的起始地址

        然后当访问涉及内存的指令如 mov [0x00], al (32位模式也可用8位操作数)时,处理器将描述符高速缓存器中的段基地址加上该指令中给出的偏移地址

        最后访问该过程得到的32位物理地址(这个例子是0x00000100)便是该模式访问内存的过程

      最后我想注解一下第一个引用结尾提出来的问题:为什么保护模式比实模式要多一步缓存,这个缓存的意义是什么?

      经过保护模式寻址的讨论,我们可以知道保护模式的寻址最简单地可以理解为多增加了一步——查表,为什么需要查表?因为实模式无限制的寻址并不安全,所以保护模式需要先在表中定义每个段所能访问的地址空间(这是最简单的理解)。而查表会带来开销,对于指令给出一个偏移地址。思考实模式的寻址机制,实模式是每给一个偏移地址就进行左移4位再相加的动作;而到了保护模式,如果也像实模式这样每给出一个偏移地址就查表相加,这一步是存在开销的,所以此时引入的缓存机制,可以避免每次访问内存的操作时都进行一次查表操作。这便是我的粗略理解。

  • 相关阅读:
    php 对象转数组
    一张图解析FastAdmin中的表格列表的功能
    tp中打印sql,查看语句信息
    fastadmin 增加外键表搜索
    fastadmin 后台管理中,权限设置,不同管理员,显示不同的数据
    Python exec 函数
    Python eval() 函数
    Python next() 函数
    Python iter() 函数
    Python range() 函数
  • 原文地址:https://www.cnblogs.com/bEngi1/p/12173719.html
Copyright © 2011-2022 走看看