分类: LINUX
2013-03-03 20:57:33
本文是想简单的描述80x86 CPU在保护模式下的工作方式。
本文仅描述大概的东西,细节的并不多描述,比如一个描述符中的每位表示什么意思,这些不会涉及。具体可google。
本文没有图,因此难以理解。
1:控制寄存器(CR0~CR3)
CPU内含有4个控制寄存器,CR0~CR3,均为32位寄存器。
CR0:第0位为PE位,第31位称为PG位。 PE控制是否开启保护模式, PG控制是否开启分页机制。
PE PG 说明
0 0 实模式
0 1 无效组合
1 0 保护模式
1 1 保护模式 + 分页机制
CR1:保留寄存器,没用
CR2:存储页错误线性地址,即发生页异常时的线性地址保存在这里。(貌似用于缺页异常)
CR3:页目录表基地址。
CR2和CR3是用于分页机制的。关键的是CR0,当置位PE位时,CPU便进入了保护模式。
进入保护模式意味着CPU的工作机制发生了变化。访问内存的方式发生了变化,中断机制发生了改变,相比之实模式还多了许多的功能。
2:内存访问
不管实模式还是保护模式,当CPU访问内存的时都是提供如下形式的地址:
段地址:偏移地址
虽然形式一致,但意义有很大差异。这种地址就称为“虚拟地址”
由于意义的改变,通常将保护模式下的地址称为: 段选择子:偏移地址
无论保护模式、实模式,段选择子和段地址通常都是存放在段寄存器中的,如CS, DS, ES, FS,GS,SS
转换成物理地址的方式:
(1)实模式
实模式下计算物理地址的公式:物理地址 = 段地址 * 16 + 偏移地址
(2)保护模式
保护模式得到物理地址的方式: 段选择子:偏移地址 ----> 分段机制 -----> 线性地址 ------> 分页机制 ------> 物理地址
要想了解保护模式是如何访问物理地址的,则需要清楚分段机制和分页机制。
3:分段机制
首先要了解,当开启保护模式后,就开启了分段机制,但仅有置位PG位后,才会开启分页机制。因此要不要分页,由操作系统决定。
分段机制用到的数据结构为:描述符表,系统寄存器
(1)描述符表
描述符表就是由描述符组成的表。
每个描述符的大小为8字节,描述符表就是在内存中的一个数组,数组元素为大小8字节的描述符。
根据描述符的内容不同,可将描述符分成两大类:段描述符和门描述符。
段描述符内容包含:
段基地址 段限长 段属性
段描述符分为:代码段描述符,数据段描述符,系统段描述符。
其中系统段描述符分为:LDT段描述符, TSS段描述符
门描述符内容包含:
段选择子 段内偏移 段属性
门描述符分为:中断门描述符,陷阱门描述符,任务门描述符,调用门描述符。
描述符的意义:
一个段描述符指定了一个段。 也就是说当我们谈到“段”的时候,我们谈到的是一段虚拟内存空间。
段基地址指明了这段空间的起始地址,段限长指定了段的大小,而段属性就说明了段的属性,比如是代码段还是数据段,该段的访问权限等等信息。
关于门描述符的意义,下面再说。
系统有三种描述符表:
全局描述符表(GDT):全局的
局部描述符表(LDT):用于任务
中断描述符表(IDT):用于中断处理
(2)寄存器
全局描述符表寄存器(GDTR):存储GDT表的线性基地址(32位)和表长度(16位)
中断描述符表寄存器(IDTR):存储IDT表的线性基地址(32位)和表长度(16位)
局部描述符表寄存器(LDTR):存储当前使用的LDT表的段选择子(16位)
任务寄存器(TR):存储当前任务的段选择子(16位)
注意:这些寄存器里存储的是线性地址,不是物理地址。
(3)段选择子
在保护模式下提供的地址里,前一部分称为段选择子,
16位的段选择子分成3个部分:描述符索引(13位) TI位(1位) RPL(2位)
明显描述符索引是用来选择描述符的。
当TI位为0时,就会在GDT表中访问描述符索引指定的描述符
当TI位为1时,就会在LDT表中访问描述符索引指定的描述符
(4)地址转换
保护模式下,分段机制的地址转换过程大致如下:(抱歉没有图,只有文字说明)
i) CPU访问地址为“段选择子:偏移地址”
ii)利用“段选择子”部分的值和相应的寄存器GDTR/LDTR,获得相应描述符表中的描述符。
iii)进行相应的访问权限检查
iv)通过检查后,将描述符中的“段基地址”与“偏移地址”值相加得到“线性地址”。(如果没有开启分页机制,则线性地址就是物理地址)
与实模式相比, 保护模式里多了一步查表操作。
(5)有关LDT
LDT描述符表是作为一个段而存在的,因此需要有一个LDT段描述符来描述LDT段。
可以将一个任务的代码段和数据段的描述符放在一个LDT段描述符表中,然后让不同的任务有不同的LDT。
让LDTR指向不同的LDT时,就进行了“任务的切换”。(这只是简单的说法)
(6)需要注意的
关于描述符表,有意思的是它存储在内存里。有关这点,我还不太明白。
4:分页机制
通过分段之后得到线性地址,若开启了分页机制,则通过分页机制,才能得到物理地址。
通常一页的大小为4KB。
分页机制将线性地址空间按4KB大小分成一些页,将物理地址空间也按同样大小分成一些页,
通过页表这个结构,对两个地址空间中的页建立映射关系。不一定是一一映射。
(1)页表项
一个页表是由一些页表项组成,页表项大小为4个字节。
页表项包含两部分内容: 页物理地址 属性
因此通过页表项,可找到对应的物理页。
(2)二级页表结构
页目录存储在一个页面中,每个页目录也是由页表项组成的。页目录中的页表项指向的页的内容为页表。
一个页面的页目录,会有1024个页表项,每个页表项指向一个页表,因此一个页表最多可以访问4M的物理地址空间。
于是一个页目录可以访问4G的物理地址空间。
(3)地址转换过程:
i)32位的线性地址分成以下3个部分: 页目录索引(10位) 页表索引(10位) 页内偏移(12位)
ii)利用CR3寄存器和页目录索引,找到页目录中的一个页表项,得到二级页表的页地址
iii)利用页表索引和二级页表的页地址,得到要访问页的物理地址
iv)利用页的物理地址和页内偏移,得到要访问内存单元的物理地址。
5:门描述符
这里说明门描述符的作用。 此处以调用门为例,其他中断门,陷阱门类似。
现在运行在代码段A中,若想调用代码段B的代码。方法有以下两种:
i)JMP/CALL + 代码段B的段选择子
ii)JMP/CALL + 调用门描述符
(1)直接使用代码段B的段选择子
在进行代码跳转时会进行特权级检查。 在直接使用段选择子进行跳转时,系统的特权级不会发生改变,堆栈也不会改变。
(2)使用调用门描述符
在门描述符里包含有要跳转的代码段B的段选择子, 因此使用调用门,只是中间多了一步而已。
使用调用门的作用是,可以从低特权级代码段,跳转到高特权级代码段。 就好像应用程序调用系统函数的时候,会从用户态进入内核态。
(3)堆栈问题
当特权级发生变化的时候,堆栈也会发生变化。
每个任务会最多有4个堆栈, 对应于4个特权级, 当任务从低特权级跳转到高特权级后,任务的堆栈也会发生变化。
这4个堆栈的指针存放在任务的TSS段中。
CPU会将旧的堆栈中的参数拷贝到新堆栈中, 并且保存旧堆栈的SS和ESP,还有返回地址CS与EIP。
这样在调用ret指令返回时,能返回到之前的地址,并且恢复之前的堆栈。
6:中断机制
系统为每个中断分配一个中断向量号,共有256个中断向量后。
当发生中断时,会用中断向量号在IDT表中查找对应的门描述符, 然后利用门描述符中段选择子指定的代码来处理中断。
感觉中断与异常处理起来都差不多。
7:任务管理
一个任务包括:任务执行空间和任务状态段TSS
任务执行空间包括:代码段、数据段、堆栈段。
(1)TSS段
TSS段保存了一个任务的主要信息:
i)通用寄存器和段寄存器信息
ii)标志寄存器EFLAGS, 程序指针EIP
iii)特权级0、1、2的堆栈指针
iv)I/O映射位图
v)LDT段选择符
在进行任务切换时,会将旧任务的相应信息保存在TSS中, 新任务的相应信息从TSS中加载到寄存器中
(2)执行任务
当使用JMP/CALL调用一个TSS段选择子或者任务门描述符时,就会开始执行一个任务。执行时就会将旧的任务信息保存到TSS中。
从新任务的TSS内容加载到CPU寄存器中。 第一个任务可以用LTR指令,将TSS段选择子加载到TR寄存器中。