操作系统真相还原-------保护模式入门

1)为什么要有保护模式?
( 1 )实模式下操作系统和用户程序属于同一特权级,这哥俩平起平坐,没有区别对待 。
(2 )用户程序所引用的地址都是指向真实的物理地址,也就是说逻辑地址等于物理地址,实实在在地指哪打哪。
(3 )用户程序可以自由修改段基址,可以不亦乐乎地访问所有内存,没人拦得住。
以上 3 个原因属于安全缺陷,没有安全可言的 CPU 注定是不可依赖的,这从基因上决定了用户程序乃至操作系统的数据都可以被随意地删改,一旦出事往往都是灾难性的,而且不容易排查。
(4 )访问超过64kb的内存区域时要切换段基址,转来转去容易晕乎。
(5 )一次只能运行一个程序,无法充分利用计算机资源。
(6 )共 20 条地址线,最大可用内存为 1MB ,这即使在 20 年前也不够用。
2)保护模式初见
除段寄存器外,通用寄存器、指令指针寄存器、标志寄存器都由原来的 16 位扩展到了 32 位。
在这里插入图片描述
左边己经标注名字的寄存器有通用寄存器组,名字前统一加了字符 E 表示扩展,同样,
EFLAGS 寄存器和 EIP 分别在 FLAGS 和 E 基础上扩展而成。图下边的 6 个段寄存器,依然是 16 位 。
3)编译器如何知道代码是16位,还是32位?
编译器提供了伪指令 bi毡,用它来向编译器b传达:我下面的指令都要编译成 xx
位的,因为我知道下面的代码的运行环境是 xx 模式。比如在实模式下,运行的指令都是 16 位的,所以编译器要将代码编译成 16 位的指令。在实模式下准备好了保护模式所需要的环境后,进入保护模式后的代码就应该是 32 位指令。也就是,同一段程序要经历两种模式,所以同一段程序中有两种模式的机器码。
bits 的指令格式是 [bits 16]或 [bits 32]。
[bits 16]是告诉编译器,下面的代码帮我编译成 1 6 位的机器码 。
[bits 32]是告诉编译器,下面的代码帮我编译成 32 位的机器码。
“下面的代码”是哪里呢? bits 指令的范围是从当前 bits 标签直到下一个 bits 标签的范围,这个范围中的代码将被编译成相应宇长的机器码。 bits 外面的中括号是可以省略的,另外,在未使用 bits 指令的地方,默认是[bits 16]。
唠嗦一下,使用 bits 指令的情况是:您清楚所写的代码是运行在哪种模式下, 1{?}.需要向编译器明确指出将其编译成哪种模式的机器码 。 也许有同学有疑惑,打开保护模式的步骤是清晰且有限的7看上去,编译器根据代码似乎能猜测出是否是打开了保护模式,当前代码所处在的模式也能猜出,应该能自动识别编译成哪类机器码。
4)段描述符、全局描述符表 GOT,局部描述符表 LDT 及选择子
用哪些属性来描述这个内存段呢?
首先,先要解决实模式下存在的问题。
实模式下的用户程序可以破坏存储代码的内存区域,所以要添加个内存段类型属性来阻止这种行为。
实模式下的用户程序和操作系统是同一级别的,所以要添加个特权级属性来区分用户程序和操作系统的地位。
其次,是一些访问内存段的必要属性条件。
内存段是一片内存区域,访问内存就要提供段基址,所以要有段基址属性。
为了限制程序访问内存的范围,还要对段大小进行约束,所以要有段界限属性。
该结构专门用来描述一个内存段,该结构是 8 字节大小(顺便说一句,在本书中提到的各种描述符大小都是 8 宇节)。
在这里插入图片描述
一个段描述符只用来定义(描述)一个内存段。代码段要占用一个段描述符、数据段和战段等,多个内存段也要各自占用一个段描述符,这些描述符放在哪里呢?答案是放在全局描述符表,就是本节开头所说的 GDT (Global Descriptor Table)。全局描述符表 GDT 相当于是描述符的数组,数组中的每个元素都是8 宇节的描述符。可以用选择子(马上会讲到)中提供的下标在 GDT 中索引描述符。
为什么将该表称为“全局”描述符表?全局体现在多个程序都可以在里面定义自己的段描述符,是公用的。全局描述符表位于内存中,需要用专门的寄存柑旨向它后, CPU 才知道它在哪里。这个专门的寄存器便是 GDTR,即 GDTRegi脚,专门用来存储 GDT 的内存地址及大小。 GDTR 是个 48 位的寄存器。
在这里插入图片描述
使用lgdt指令修改GDTR寄存器的值,重新定位GDT表的位置。
这 48 位内存数据划分为两部分,其中前 16 位是 GDT 以宇节为单位的界限值,所以这 16 位相当于GDT 的字节大小减 1 。后 32 位是 GDT 的起始地址。由于 GDT 的大小是 16 位二进制,其表示的范围是 2的 16 次方等于 65536 字节。每个描述符大小是 8 字节,故, GDT 中最多可容纳的描述符数量是 65536/8=8192个,即 GDT 中可容纳 8192 个段或门。
在保护模式下,段寄存器里16位值不在是实模式下的段起始地址,而是段选择子,用来确定去哪里寻找段描述符以及确定此时代码所处的权限,是内核态还是用户态。由于段寄存器是 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 中索引描述符。前面说过 GDT 相当于一个描述符数组,所以此选择子中的索引值就是 GDT 中的下标。由于选择子的索引值部分是 13 位,即 2 的 13 次方是 8192,故最多可以索引 8192 个段,这和 GDT中最多定义 8192 个描述符是吻合的。
在这里插入图片描述
例如选择子是 0x8,将其加载到 ds 寄存器后,访问 ds: Ox9 这样的内存,其过程是0x8 的低 2 位是RPL,其值为 00。第 2 是 TI ,其值 0,表示是在 GOT 中索引段描述符。用 0x8 的高 13 位 0x1 在 GOT 中索引,也就是 GOT 中的第 1 个段描述符( GOT 中第 0 个段描述符不可用)。假设第 1 个段描述符中的 3个段基址部分,其值为 0xl234oCPU 将 0xl234 作为段基址,与段内偏移地址 0x9 相加, 0x1234+0x9=0x123d。用所得的和 0xl23d 作为访存地址。
值得注意的是上面括号中提到了 GOT 中的第 0 个段描述符是不可用的,原因是定义在 GOT 中的段描述符是要用选择子来访问的,如果使用的选择子忘记初始化,选择子的值便会是 0,这便会访问到第 0 个段描述符。为了避免出现这种因忘记初始化选择子而选择到第 0 个段描述符的情况, GOT 中的第 0 个段描述符不可用。也就是说,若选择到了 GOT 中的第 0 个描述符,处理器将发出异常。
5)打开A20地址线
因为为了能和8086的实模式兼容,所以cpu默认只有20位地址线,需要打开高位地址线。因此进入保护模式就需要打开
在这里插入图片描述
6)保护模式的开关
将CR0寄存器的第0位值1,就代表着CPU进入了保护模式,就可以访问4G内存。
在这里插入图片描述
在这里插入图片描述

发布了24 篇原创文章 · 获赞 3 · 访问量 930

猜你喜欢

转载自blog.csdn.net/HIT_zhanmusi/article/details/102925262
今日推荐