书本第三章第一节是《认识保护模式》,初步 讲解了保护模式下全局描述符表GDT、段描述符、段选择子、从实模式进入保护模式等内容。去年看这个的时候,如果不是有以前学习保护模式时做的笔记,还真 不好懂呢,因为作者提供的材料不够系统,对仅学习过8086汇编语言的人来说,是不太好理解的。下面的内容大体以我以前做的笔记为纲,较为简略,只说明要 点。(本来还可以参考下以前学习保护模式时收集的资料的,很可惜,移动硬盘坏了,资料都没有了,早就该注意信息安全了的。)
8086是16位处理器,有16位的寄存器和数据总线,20位的地址总线,寻址能力为1MB。地址由段基址和段偏移两部分组成,段基址和偏移地址都是16 位的,物理地址的计算方式为:物理地址=段基址×16+段偏移。从80386开始,Intel处理器进入了32位时代,地址总线为32位,寻址能力为 4GB。此时,通用寄存器变成从16位变成了32位,但段寄存器仍然是16位的,原来的基地址加偏移值的物理地址计算方法已经不适用了,需要新的计算方 法。
386以上CPU运行于保护模式时,虽然段寄存器仍然是16位的,但是其意义已经发生了变化:不再是表示段基地址了,而是表示段描述符在描述符表中的索引。此时,段寄存器的值也有了一个新的名称:段选择子。
段描述符 是 描述段属性的一个8字节数据结构,分为三种类型:(数据段和代码段)段描述符、系统段描述符和门描述符。书本第43页给出了描述符的结构图。书中说“由于 历史问题,它们(描述符中的段基址和段偏移)都被拆开存放”。我想,这个历史问题是指为兼容80286处理器的设计吧:80286采用24位地址线,寻址 能力16MB,所以段基址被拆开成低24位和高8位两部分;80286已经引入“保护模式”的概念,是推出实模式和保护模式CPU的分水岭。附两个粗略介 绍80286的网址:http://wiki.ccw.com.cn/80286 ,http://baike.baidu.com/view/193778.htm
描述符表 是内存中保存一个或者多个描述符的区域,由相关寄存器(GDTR、LDTR、IDTR)指示其起始地址。有三种描述符表:
GDT Global Description Table 全局描述符表
LDT Local Description Table 局部描述符表
IDT Interrupt Description Table 中断描述符表
段选择子 是 段寄存器的内容。虽然386处理器中段寄存器还是16位的,但是在保护模式下,其内容的意义已经发生了变化,不再是表示段基地址,而是表示段描述符在描述 符表中的位置。其中第0位和第1位是RPL(Request Privilege Level 请求特权级),用以进行访问权限检查,这个以后再详细描述;第2位是TI(Task Indication,任务指示),其值为0表示使用GDT,为1表示使用LDT;第3至15位是段描述符在相应的描述符表中的索引。
实模式下的寻址方式同8086:段基地址×16+段偏移地址。
保护模式下使用48位地址指针:16位段选择子和32位段偏移地址。寻址过程为:从相应的段寄存器中取出段选择子,根据高13位从相应的描述符表中取得段 描述符;用段描述符给出的32位段基地址,加上32位的偏移地址得到32位线性地址;如果没有启用分页,则32位线性地址就是物理地址,否则线性地址再经 过分页机制转化成物理地址。当然,寻址过程中会根据段描述符给出的段属性进行各种检查,如特权级检查、地址越界检查、段页是否存在检查等。
我觉得,没有给出386的寄存器模型是本书的一大缺憾。这里给出386的寄存器模型并作简要说明。(这张图我花了好长时间用WPS Office画的)
图中阴影部分是实模式下可用的寄存器。
EAX、EBX、ECX、EDX:8086中的通用寄存器已经扩展成32位的了。当然,还是可以使用AX、BX、CX、DX访问低16位;或者用AH、AL等分别访问低16位的高低两个字节。
CS、DS、SS、ES、FS、GS:新增了FS和GS这两个16位的段寄存器。CS、DS、SS、ES的用途不变,还是代码段、数据段、堆栈段和扩展段寄存器;FS和GS也可以看作是方便程序员使用的扩展段寄存器。
EIP:地址变成32位了,指令指针当然也要扩展成32位的了。
ESP、EBP、ESI、EDI:堆栈指针、基址指针、源变址、目标变址寄存器,名字前面加上字母E表示扩展成32位了。
EFLAGS:标志寄存器也变成32位的了。
以下是386新增的寄存器:
IDTR、GDTR和LDTR:IDTR和GDTR是48位的,分别用以保存中断描述符表和全局描述符表的基地址(32位)和限长(16位)。LDTR保 存局部描述符的段选择子,其使用方式为:1 在LDTR中装入段选择子,CPU自动从GDT中取出局部描述符,放入高速缓存中,为当前任务建立LDT;2 以后使用段寄存器中的选择子时,若发现TI位为1,则从LDT中找到相应的段描述符进行寻址操作。
TR:任务寄存器,存放任务状态段选择子,指示任务状态段描述符在GDT中的位置(类似LDTR)。使用方式类似LDTR:在TR中装入段选择子,CPU 自动从GDT中取出任务状态段描述符,放入高速缓存中,为当前任务建立TSS。TSS是Task State Segment(任务状态段)的简写,它定义了启动任务所需要的信息,简言之(不太准确),就是一个进程的相关信息。
CR0、CR1、CR2、CR3、CR4:控制寄存器,保存了系统的各种标志位。其中CR0的低16位就是8086中的MSW(Machine State Word,机器状态字)。这里需要关注的是CR0的第0位PE标志,其值为0表示当前运行在实模式下;为1表示运行于保护模式下。
书本上给出了进入保护模式的代码,还有详细的说明,我就不给自己的代码了,只对一个地方进行简要说明:为什么要在GDT的起始位置处定义一个没有使用的空的描述符?
这个问题我想了好久也没想通,后来在网上找到了答案:GDT中索引为0的描述符是特殊的空描述符,是不能使用的,如果使用它进行存储器访问,会引发Genernal Protection异常。关于这个问题,可以参考http://leonstar.bokee.com/36918.html