上一篇博客我们简单写了一个引导,还没有进入系统,在进入系统之前,我们有必要先了解下CPU的实模式和保护模式。我们的程序不加任何保护,直接运行在CPU上,称为实模式,16位实模式最大寻址空间为1M。CPU可以通过保护模式拓展寻址空间,并进行访问权限管理和校验,32位保护模式的寻址空间可以拓展到4G。在保护模式下物理内存、页、中断都会保护起来、不同特权级下IO端口也不能随意使用。
一、段
段(Segment),在80X86中,分段机制将内存空间分成一个或者多个线性区域,我们把这些线性区域称为段。我们需要将这些段区分开来,于是分段机制为每个段赋予3个属性,分别是:段基址(Base address):指定段在线性地址空间中的开始地址。段界限(Limit):表示了段内最大可用偏移量,也就是说它定义了段的长度。段属性(Attribute):指定了段的特性,包括:可读,可写或者可执行,特权等级等特性。
段描述符(Descriptor),在程序中,我们需要定义一个数据结构来记录段的属性,有段基址(Base),段界限(Limit),段属性(Attribute),我们称它为段描述符(Descriptor)。段是逻辑概念,而段描述符是表示段的数据结构,每个段描述符要占用8个字节的空间。
段描述符表(Descriptor Table),在一个程序中,不只存在一个段(段描述符)。所以我们需要将这些段描述符组织起来,于是定义了一个存储段描述符的数组,称为段描述符表。段描述符表有两种,一种是全局描述符表(GDT),一种是局部描述符表(LDT),系统中供所有的任务共享的是全局描述符表,而不同的任务却是使用自己的局部描述符表。
段选择子(SelectorXXX),把所有段描述符都存储在段描述符表中,当我们使用其中某一个段的时候,我们并不直接指向该段,而是通过该段描述符在段描述符表中的位置来访问的。故段选择子,就是一个16位的标识符,用来标识该段描述符在描述符表中的位置。
段描述符表寄存器,如何让系统知道段描述符表在什么地方呢?处理器提供了内存管理寄存器,分别是全局描述符表寄存器(GDTR)、局部描述符表寄存器(LDTR)。GDTR寄存器中用于存放全局描述符表GDT的32位线性基地址和16位的表的长度值。LDTR寄存器中用于存放局部描述符表LDT的32位线性基地址和16位的表的长度值。通过系统指令,lgdt将GDT的线性基址和长度值加载到GDTR寄存器中,lldt将LDT的线性基址和长度值加载到LDTR寄存器中。
在实模式下,也就是在8086系统下的寻址方式。 Intel 8086是16位的CPU,它有着16位的寄存器(Register),16位的数据总线(Data Bus)以及20位的地址总线(Address Bus)和1MB的寻址能力。一个地址是由段和偏移两部分组成的,物理地址遵循这样的计算公式:
物理地址(Physical Address) = 段值(Segment) * 16 + 偏移(Offset)。其中段值和偏移都是16位的。故寻址范围为1MB。
在保护模式下,有了分段机制,所以它的寻址方式发生了很大的变化。
在保护模式下,首先使用段选择子在段描述符表中查找到相对应的段描述符,找到32位段基址,然后在与32位的偏移量相加,得到线性地址。段基址和段偏移量都是32位的,所以寻址范围大小为4GB。在程序中jmp dword SlectorCode32:0的作用,就是进入保护模式下的寻址方式。其中,在使用某个段时,它的段选择子是存储在段寄存器中的。
这里面存在着一个问题,是否我们每次寻址都要先去全局描述符表寄存器(GDTR)中,查找到全局描述符表(GDT)的基址,然后再次根据选择子的索引跳转到该描述符所在的位置,然后取得段描述符中的基址,如果这样的话,我们里里外外采访了几次内存,太浪费时间了。实际上段寄存器结构可以让我们直接获取段描述符。
二、特权级
在IA32的分段机制中,特权级总共有四个特权级别,从高到低分别对应0,1,2,3。数字越小表示特权越大。
作用:防止低特权级应用访问高特权级数据,代码。
特权级检验主要通过CPL,DPL,RPL来实现。
1.CPL
当前执行程序或任务的特权级。
被存储在CS的第0位和SS的第一位上。
通常表示代码段的特权级。
2.DPL
表示段或门的特权级。
存储在段描述符或门描述符的DPL字段中。
3.RPL
段选择子的第0位和第1位。
系统调用时使用RPL作为调用者的特权级。
补充:
参考:https://www.cnblogs.com/LittleHann/p/3850655.html
门描述符:
处理器对程序的执行主要时顺序和跳转两种方式,跳转也就是程序控制的转移,可以通过指令jmp、call、ret、sysenter、
sysexit、int n、iret引起,也可以由中断和异常机制引起。
在I386CPU中,除了”段描述符”(描述某种内存段)之外还有一种描述符叫做”门描述符”(描述控制转移的入口点,也就是异常控制中断的入口点),通过这种门可以实现特权级的转变和任务的切换。门描述符主要由以下几部分组成:
1. 选择子 2. 偏移地址 3. DPL
分为4种类型:
- 调用门
- 中断门
- 陷阱门
- 任务门
1. 调用门描述符
调用门一般用在特权级的切换,存在于GDT中或者LDT中。调用门的选择子指向代码段描述符,偏移地址对应代码段中的偏移量。当jump和call指令的操作数是调用门的时候,就会跳转到对应的代码处,并发生特权级的变化,也就会发生
堆栈的切换
2. 任务门描述符
任务门一般用在任务的切换,可以存放在GDT、LDT或IDT中。任务门的选择子指向GDT中的TSS选择符,偏移地址没有意义。当jmp和Call指令的操作数是任务门的时候,就会发生任务的切换。
3. 中断门描述符
4. 陷阱门描述符
中断门描述符、陷阱门描述符用来对中断服务例程进行寻址,从原理上来理解,中断例程的寻址本质上也是内存的寻址
保护模式初步总结:
- 描述符中段基址、段界限、段属性对段的一种保护
- 门描述符中特权级之间的变换
- 涉及到特权级的每一步中,处理器都会对CPL、DPL、RPL等内容进行比较。
关于段描述符和门描述符参考:https://blog.csdn.net/barech/article/details/4401417
可以理解为,段描述符是对存储访问的管理控制,门描述符是对调用,特权级转变的管理控制。
保护模式总结更新:
- 在GDT、LDT、IDT中,每一个描述符都有自己的界限和属性 — 对描述符所描述对象的一种限定和保护
- 分页机制中的PDE和PTE都含有R/W和U/S位 — 页级保护
- 页式存储使应用程序使用的是线性地址空间 — 物理内存被保护起来
- 中断不再像实模式下一样使用 — 特权检验,中断调用被保护
- I/O指令不再随便使用 — 端口被保护起来
- 在程序运行过程中,如遇到不同特权级间的访问,会对CPL、RPL、DPL、IOPL等内容进行检验,同时进行堆栈切换 — 对不同层级的程序进行了保护。
参考:https://blog.csdn.net/yihaolovem/article/details/23483927
备注:寄存器参考:https://www.cnblogs.com/joey-hua/p/5347257.html
https://blog.csdn.net/jnu_simba/article/details/11712675
段页机制:https://www.cnblogs.com/chenwb89/p/operating_system_003.html
https://blog.csdn.net/suppercoder/article/details/9422093
https://blog.csdn.net/fallingu/article/details/75221276
https://blog.csdn.net/bian1029/article/details/49124593(GDT表第一项为0)