从零开始操作系统------探析保护模式

本文基于郑纲的《操作系统还原》,仅为个人学习笔记,前期的虚拟机配置等不再详细记录,其中不理解或者出错的地方还望提出意见!

从零开始操作系统------MBR直操硬盘、内核加载器

为什么有保护模式

(1)实模式下操作系统和用户程序属于同一特权级,没有区别对待;
(2)实模式下用户所引用的地址都是真实的物理地址;
(3)实模式下用户程序可以自由修改段基址,可以访问所有内存;
(4)实模式下共 20 条地址线,最大可用内存为 1MB;

寄存器扩展

CPU 发展到 32 位后,地址总线和数据总线也发展到 32 位,其寻址空间更达到了 2 的 32 次方, 4GB。除段寄存器外,通用寄存器、指令指针寄存器、标志寄存器都由原来的 16 位扩展到了 32 位。
在这里插入图片描述
寄存器中低 16 位的部分是为了兼容实模式,可以单独使用。高 16 位没办法单独使用,只能在用 32位寄存器时才有机会用到它们。

寻址方式与实模式下一样:“段基址:段内偏移地址”。其中段基址不再只是一个地址,还增加了许多对内存段的描述信息(安全性的体现),为此专门找了个数据结构—全局描述符表。既然叫表,就说明里面有表项,表中至少有一个表项,其中每一个表项称为段描述符,其大小为 64 字节,用来描述各个内存段的起始地址、大小、权限等信息。

这样,段寄存器中保存的再也不是段基址了,里面保存的内容叫“选择子”,selector,该选择子其实就是个数,用这个数来索引全局描述符表中的段描述符,把全局描述符表当成数组,选择子就像数组下标一样。

另外由于段描述符是保存在内存中,读取效率是十分低的,因此对段寄存器率先应用了缓存技术,将段信息用一个寄存器来缓存,这就是段描述符缓冲寄存器(Descriptor Cache Registers)。

寻址扩展

保护模式下,同样是内存寻址中,基址寄存器不再只是 bx、bp,而是所有 32 位的通用寄存器,变址寄存器也是一样,不再只是 si、 di,而是除 esp 之外的所有 32 位通用寄存器,偏移量由实模式的 16 位变成了 32 位。并且,还可以对变址寄存器乘以一个比例因子。比例因子只能是 1、 2、 4、 8。
在这里插入图片描述

运行模式反转

在实模式下,指令和操作数都是 16 位的,但我们也说过啦,它可以使用 32 位的资源。同样在保护模式下,指令和操作数都是 32 位的,它也可以使用 16 位的资源。也就是说,在某个模式下,可以使用另一模式下的资源。

CPU无法知道需要生成16位还是32位机器码,需要人为告知。编译器提供了伪指令 bits,用它来向编译器传达:我下面的指令都要编译成 xx位的,因为我知道下面的代码的运行环境是 xx 模式。
bits 的指令格式是[bits 16]或[bits 32]

  1. 操作数反转前缀0x66
    模式之间可以互相使用对方环境下的资源。比如, 16 位实模式下可以用 32 位保护模式下的寄存器。但这种福利的得来却是稍费功夫的,如果要用另一模式下的操作数大小,需要在指令前添加指令前缀 0x66,将当前模式临时改变成另一模式。
    在这里插入图片描述

  2. 寻址方式反转前缀 0x67
    不同模式之间不仅可以使用对方模式下的操作数,还可以使用对方模式下的寻址方式。
    在这里插入图片描述
    bits 伪指令用于指定运行模式,操作数大小反转前缀 0x66 和寻址方式反转前缀 0x67,用于临时将当前运行模式下的操作数大小和寻址方式转变成另外一种模式下的操作数大小及寻址方式。

全局描述符表

保护模式下,内存段(如数据段、代码段等)不再是简单地用段寄存器加载一下段基址就能用啦,段的信息增加了很多,需要提前把段定义好才能使用。
全局描述符表(Global Descriptor Table, GDT)是保护模式下内存段的登记表,这是不同于实模式的显著特征之一。

  1. 段描述符
    将描述内存段的属性放到了一个称为段描述符的结构中,该结构专门用来描述一个内段,存该结构是 8 字节大小(顺便说一句,在本书中提到的各种描述符大小都是 8 字节)。
    在这里插入图片描述
    在段描述符的高 32 位中,
    8~11 位是 type 字段,共 4 位,用来指定本描述符的类型,用于表示内存段或门的子类型。

    一个段描述符,在 CPU 眼里分为两大类,要么描述的是系统段,要么描述的是数据段,这是由段描述符中的 S 位决定的,用它指示是否是系统段。在 CPU 眼里,凡是硬件运行需要用到的东西都可称之为系统,凡是软件(操作系统也属于软件, CPU 眼中,它与用户程序无区别)需要的东西都称为数据,无论是代码,还是数据,甚至包括栈,它们都作为硬件的输入,都是给硬件的数据而已,所以代码段在段描述符中也属于数据段(非系统段)。 S 为 0 时表示系统段, S 为 1 时表示数据段。 type 字段是要和 S 字段配合在一起才能确定段描述符的确切类型。

    在这里插入图片描述
    表中的 A 位表示 Accessed 位,这是由 CPU 来设置的,每当该段被 CPU 访问过后, CPU 就将此位置 1。所以,创建一个新段描述符时,应该将此位置 0。

    段描述符的第 13~14 位是 DPL 字段, Descriptor Privilege Level,即描述符特权级。

    这两位能表示 4 种特权级,分别是 0、 1、 2、 3 级特权,数字越小,特权级越大。特权级是保护模式下才有的东西, CPU 由实模式进入保护模式后,特权级自动为 0。因为保护模式下的代码已经是操作系统的一部分啦,所以操作系统应该处于最高的 0 特权级。用户程序通常处于 3 特权级,权限最小。某些指令只能在 0 特权级下执行,从而保证了安全。

    段描述符的第 15 位是 P 字段, Present,即段是否存在。如果段存在于内存中, P 为 1,否则 P 为 0。P 字段是由 CPU 来检查的,如果为 0, CPU 将抛出异常,转到相应的异常处理程序,此异常处理程序是咱们来写的,在异常处理程序处理完成后要将 P 置 1。也就是说,对于 P 字段, CPU 只负责检查,咱们负责赋值。

    段描述符的第 20 位为 AVL 字段,从名字上看它是 AVaiLable,可用的。

    段描述符的第 21 位为 L 字段,用来设置是否是 64 位代码段。 L 为 1 表示 64 位代码段,否则表示 32位代码段。这目前属于保留位,在我们 32 位 CPU 下编程,将其置为 0 便可。

    段描述符的第 22 位是 D/B 字段,用来指示有效地址(段内偏移地址)及操作数的大小。

    对于代码段来说,此位是 D 位,若 D 为 0,表示指令中的有效地址和操作数是 16 位,指令有效地址用 IP 寄存器。若 D 为 1,表示指令中的有效地址及操作数是 32 位,指令有效地址用 EIP 寄存器。
    对于栈段来说,此位是 B 位,用来指定操作数大小,此操作数涉及到栈指针寄存器的选择及栈的地址上限。若 B 为 0,使用的是 sp 寄存器,也就是栈的起始地址是 16 位寄存器的最大寻址范围, 0xFFFF。若 B 为 1,使用的是 esp 寄存器,也就是栈的起始地址是 32 位寄存器的最大寻址范围, 0xFFFFFFFF。

    段描述符的第 23 位是 G 字段, Granularity,粒度,用来指定段界限的单位大小。所以此位是用来配合段界限的,它与段界限一起来决定段的大小。若 G 为 0,表示段界限的单位是 1 字节,这样段最大是 2的 20 次方1 字节,即 1MB。若 G 为 1,表示段界限的单位是 4KB,这样段最大是 2 的 20 次方4KB 字节,即 4GB。

  2. GDT
    全局描述符表 GDT 相当于是描述符的数组,数组中的每个元素都是8 字节的描述符。可以用选择子中提供的下标在 GDT 中索引描述符。

    全局描述符表位于内存中, 需要用专门的寄存器指向它后, CPU 才知道它在哪里。 这个专门的寄存器便是 GDTR,即 GDT Register,专门用来存储 GDT 的内存地址及大小。 GDTR 是个 48 位的寄存器。

    这 48 位内存数据划分为两部分,其中前 16 位是 GDT 以字节为单位的界限值,所以这 16 位相当于GDT 的字节大小减 1。后 32 位是 GDT 的起始地址。由于 GDT 的大小是 16 位二进制,其表示的范围是 2的16次方等于65536字节。每个描述符大小是8字节, 故, GDT中最多可容纳的描述符数量是65536/8=8192个,即 GDT 中可容纳 8192 个段或门。
    在这里插入图片描述

    GDTR有专门的指令lgdt进行初始化:
    lgdt 的指令格式是: lgdt48 位内存数据

    进入保护模式需要有 GDT,但进入保护模式后,还可以再重新换个 GDT 加载。在保护模式下重新换个 GDT 的原因是实模式下只能访问低端 1MB 空间,在这之前 GDT 只能位于 1MB 之内。

  3. 选择子
    段寄存器 CS、 DS、 ES、 FS、 GS、 SS,在实模式下时,段中存储的是段基地址,即内存段的起始地址。而在保护模式下时,由于段基址已经存入了段描述符中,所以段寄存器中再存放段基址是没有意义的,在段寄存器中存入的是一个叫作选择子的东西—selector。选择子“基本上”是个索引值。

    由于段寄存器是 16 位,所以选择子也是 16 位,在其低 2 位即第 0~1 位,用来存储 RPL,即请求特权级,可以表示 0、 1、 2、 3 四种特权级。
    选择子的第 2 位是 TI 位,即 Table Indicator,用来指示选择子是在 GDT 中,还是 LDT 中索引描述符。 TI为 0 表示在 GDT 中索引描述符, TI 为 1 表示在 LDT 中索引描述符。
    选择子的高 13 位,即第 3~15 位是描述符的索引值,用此值在 GDT 中索引描述符。选择子的索引值部分是 13 位,即 2 的 13 次方是 8192,故最多可以索引 8192 个段,这和 GDT中最多定义 8192 个描述符是吻合的。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值