学习笔记

《真象》第四章 保护模式入门

Posted on

回过头来看以前写的代码居然有点看不懂了,还是应该写一下笔记啊……顺便吧第四章复习一下。
《真象》是指《操作系统真象还原》这是第四章笔记,保护模式入门。

  1. 保护模式起于80286,为16位的CPU,兴起于80386,32位的CPU。保护模式下一般都是32位及以上。
  2. CPU有三种模式:实模式,虚拟8086模式,保护模式。严格意义上32位CPU无实模式。
  3. 制定运行模式编译:使用法 [bits] 16/32
  4. 运行模式反转:指运行模式在16位实模式,操作数为32位;或者当前运行模式是32为保护模式,操作数为16位。操作系统兼容两种运行模式,所以支持两种操作,一般临时的反转会在指令前自动加上'66'或者'67'。(不过这实际上是编译器的工作,我们只需要知道就好了
  5. 在保护模式下,必须用ecx表示循环次数,其他都兼容。
  6. 对于段寄存器的入栈,无论指定在哪种模式下运行,都是按照当前模式的默认操作数大小压入的。而对于通用寄存器和内存,入栈是sp移动大小有操作数决定。
  7. 段描述符,8字节大小。保存着段基址和段界限和各种保护信息,用于描述一个段(代码段,栈段等),具体格式见 P151。
  8. 全局描述符表GDT,局部描述符表LDT。通俗的说,描述符表就是一个段描述符数组,所谓选择子就是数组下标。GDT是全局数组,LDT为局部数组。通过选择子在描述符表中找出段描述符,段描述符中又记录中段基址和段界限,即可得到一个段。
  9. GDT Register。全局表述符表保存在内存中,而GDT寄存器保存着这个表的位置与大小。寄存器大小为 6 字节。前2个字节为GDT界限,后4个字节为GDT内存其实地址。赋值格式 lgdt 48位数据。而 LDT Register的格式为 lldt 16位寄存器/16位内存
  10. 选择子结构。前2位用于表示选择子请求者的当前特权级别RPL,2位4个等级。第3位为类型标识,0为GDT,1为LDT。一个选择子初始化为全0。初始选择子会访问GDT的第一个段描述符,为了保护作用,GDT的第一个段空间不可用,当访问第一个段空间的时候会报错。
  11. A20Gate。在实模式下的wrap-around,当地址超过20位的时候就会发生回绕。保护模式下为了兼容实模式,有了A20Gate。A20Gate被禁止并访问20位以上时,会发生地址回绕,否则不会,也就在寻址上进入了保护模式。
  12. 保护模式的开关,CR0寄存器的PE位。将其置为1后,将在真正意义上进入保护模式。一般或1即可。
  13. 由实模式进入保护模式的三个步骤:
    1. 打开A20Gate
    2. gdtr寄存器中加载GDT的地址会界限
    3. 将CR0寄存器的pe位置1
  14. 流水线。将一个目标指令具体化为多个指令,在已知(或预测)指令的情况下,CPU并行处理多个指令,因此某段时间后的每一个时间周期都会执行完一个目标指令。当遇到jmp指令的时候,流水线会清空。
  15. 对流水线的优化有无先后逻辑的指令有乱序执行和缓存等。
  16. 在实模式进入保护模式之后,应优先清空流水线。因为实模式下的指令存储与保护模式不同,实模式会先把段基址左移后在加入缓存,这将与保护模式的指令格式不符,导致错误。
  17. 分支预测。最简单的分支预测算法是 2位预测法,用2位bit的计数器来记录跳转状态,没跳转一次加1,达到3则不再增加,否则减1,直到0则不再减了。优先计数大的,否则随机。
  18. 加载选择子时的保护:
    1. 验证段描述符是否越界。
    2. 检查段的类型。不同段的不同type允许被加载的寄存器不同。详细见P174。
    3. 检查段是否存在。CPU通过段描述符的P位来确认内存段是否存在。
  19. 代码段与数据段的保护。CPU每访问一个地址,都要确认该地址不能超过该内存段范围。确认方式为 (描述符中段界限+1) × (段界限的粒度大小:4k或者1) - 1,化简可得
    • 指令: EIP的偏移地址+指令长度-1 <= 实际界限大小
    • 数据: 偏移地址+数据长度-1 <= 实际界限大小
  20. 栈段并不一定都是向下扩展的。(这里有地方不是很明白,P175