Linux从头学09:x86 处理器如何进行-层层的内存保护?
扫描二维码
随时随地手机看文章
-
实模式:bootloader 为程序计算段的基地址
-
保护模式:bootloader 为自己创建段描述符
-
确定 GDT 的地址
-
创建代码段的描述符
-
创建数据段的描述符
-
创建栈段的描述符
-
段描述符是如何确保段的安全的?
-
段寄存器高速缓存
-
对段寄存器本身的保护
-
对段界限的检查
x86 处理器中的分页机制是可以被关闭的,此时线性地址就等于物理地址,这也是我们一直讨论的情况。
下一篇文章,我们就把 x86 中的分页机制打开,并与 Linux 中的分段和分页机制进行对比。
实模式:bootloader 为程序计算段的基地址
在之前的文章:Linux从头学06:16张结构图,彻底理解【代码重定位】的底层原理中,我们讨论了bootloader是如何把应用程序读取到内存中,最后跳入到程序的入口地址的。
这里所说的程序,可以是操作系统,也可以是应用程序。下面这张图,是程序被加载到内存中之后,header中的信息:
因为程序是被bootloader动态读取到内存中的,它是不知道自己被放在内存中的什么位置,因此它也不知道自己代码段、数据段、栈的开始地址。
这样的话,程序开始执行时,就可以从自己的header中获取到这3个段基地址,并且赋值给相应的寄存器,从而顺利的执行程序。
保护模式:bootloader 为自己创建段描述符
bootloader从BIOS接管系统之后,刚开始是运行在实模式下的。
bootloader被加载到0x0000_7C00地址处。
确定 GDT 的地址
在创建段描述符之前,需要先确定: 把 GDT 表放在内存中的什么位置?
创建代码段描述符
bootloader的代码放在0x0000_7C00开始的地址,长度是512B。
创建数据段描述符
bootloader待会需要把操作系统或其他应用程序,从硬盘读取到内存中,例如:读取到0x0002_0000的位置。
创建栈段描述符
理论上,bootloader可以使用内存中的任意一块空闲空间,来作为自己的栈。
根据以上这些信息,就可以创建出栈的段描述符,如下:
当以上这几个段的描述符都创建好之后,就可以把GDT的地址(0x0001_0000),设置到 GDTR 寄存器中了。
段描述符是如何确保段的安全访问的?
段寄存器高速缓存
进入保护模式之后,虽然对段寄存器中内容的解释改变了,但是执行每一条指令,还是需要使用到这些段寄存器的: cs, ds, ss等等。
对段寄存器本身的保护
当逻辑地址中段寄存器的索引号改变时,就会根据新的索引号,到GDT中去查表。
当然了,这个索引号不能超过 GDT 的界限。当定位到某一个描述符表项之后,就开始进行一系列检查。
bit8 ~ bit11定义了当前这个段的类型。
对段界限的检查
在通过了第一层的段类型保护之后,还会继续对段的界限进行检查,这就要使用到逻辑地址中的偏移地址(EIP)了。
在文末的





