80386 和 保护模式.docx
《80386 和 保护模式.docx》由会员分享,可在线阅读,更多相关《80386 和 保护模式.docx(20页珍藏版)》请在冰点文库上搜索。
80386和保护模式
80386和保护模式
___WilliamLiu
IntelCPU一般可以运行在两种模式之下,即实模式和保护模式。
早期的IntelCPU(8086,8088)只能工作在实模式之下,系统中只能运行单个任务,而且只能使用实地址模式。
对于Intel80386以上的芯片则还可以运行在32位的保护模式之下。
在保护模式之下的CPU可以支持多任务;支持4GB的物理内存;支持64TB的虚拟内存;支持内存的页式管理和段式管理以及支持特权级。
本文档将首先介绍Intel80386CPU的几个内部寄存器,然后再由浅入深的分别介绍保护模式下的段式管理,页式管理,虚拟内存,多任务以及特权级管理等几个方面。
Intel80386CPU的内部寄存器
这一部分先大致介绍一下386的内部寄存器,具体细节在后面的几节中再详细说明。
一般来说,CPU设计用来系统编程的系统寄存器包括如下几类:
• 标志寄存器(EFLAGS)
• 内存管理寄存器(GDTR,LDTR,IDTR,TR)
• 控制寄存器(CR0,CR1,CR2,CR3,CR4)
• 兼容8086通用寄存器(EAX,EBX,ECX,EDX)
• 兼容8086段寄存器(CS,DS,ES,SS,FS,GS)
• 兼容8086数据寄存器(ESI,EDI,EIP,ESP)
下面分别加以介绍:
1)标志寄存器EFLAGS:
跟8086/8088的FLAGS大致差不多。
只不过位宽由16bit变成了32bit,负责的状态标志也多了一些。
见图一所示:
图一:
EFLAGS的结构
其中系统标志:
VM-虚拟8086模式;RF-恢复标志;NT-任务嵌套标志;IOPL-I/O特权级标志;IF-中断允许标志。
2)内存管理寄存器:
一共有4个,用于分段内存管理,都是用于存放指针的,只是所指的再内存单元中的内容有所不同。
GDTR全局描述符表寄存器(GlobalDescriptorTableRegister),存放的是一个指向内存单元列表的指针,用于指向全局段描述表(GDT),如图二所示。
共48bit,高32bit是GDT的基址,低16bit描述GDT的长度。
由于每项8Byte,所以共可以有2^(16)/8=2^13项。
IDTR中断描述符表寄存器(InterruptDescriptorTableRegister),存放的是也一个指向内存单元列表的指针,用于指向全局中断描述符表(IDT),如图二所示。
跟GDTR一样,共48bit。
LDTR局部描述符表寄存器(LocalDescriptorTableRegister),存放的是LDT的段选择字。
用于从GDT中索引出当前任务的局部描述符表(LDT),如图二所示。
共16bit,高13bit刚好可以索引到GDT的最大限长。
TI位置1,RPL是请求特权级(RequestPrivilegeLevel)用于特权检查。
TR任务寄存器(TaskRegister),跟LDTR一样,存放的是任务状态段TSS(TaskStateSegment)的段选择字。
用于从GDT中索引出当前任务的TSS,如图二所示。
共16bit。
图二:
系统内存管理寄存器
3)系统控制寄存器:
386CPU共四个,分别是CR0、CR1、CR2、CR3,486以后又增设了CR4。
如图三所示。
其中,CR0是用于系统整体的控制。
CR1保留。
CR2用于保存页面转换时出错的线性地址。
CR3存放页目录基址。
CR4用于各种CPU级联相关。
图三:
系统控制寄存器
需要注意的几个位是:
PE:
CR0的0bit,ProtectEnable。
使能386的保护模式。
PG:
CR0的31bit,Paging。
386进入保护模式之后,启动分页机制。
Page-DirectoryBase:
CR3的12-31bit存放页目录首地址。
每个任务只能唯一一个
Page-FaultLinearAddress:
CR2线性地址错误。
可以用于虚拟内存中
4)与8086兼容的系列寄存器:
所有通用寄存器EAX,EBX,ECX,EDX和所有数据寄存器EIP,ESP,ESI,EDI除了数据位由原来的16bit编程32bit外,功能基本没有变化。
当然为了保持对16位机的支持,你同样可以使用AX,AH,AL,IP,SP,SI,DI等等
至于段寄存器CS,DS,ES,FS,GS,SS。
你会发现名字都没有变化,事实上大小也是16bit,只是增加了几个。
事实上,386在运行实模式时这些段寄存器跟8086是完全一样的。
在运行于保护模式下时,他们同样用来指示段的地址,只不过里面存放的是段选择符,通过在LDT或者GDT中检索,间接的指示段地址。
保护模式下的段式管理
段式管理的目的是根据段基址值和段内数据的偏移值,生成数据在内存中的线性地址。
在实模式中该线性地址既是实际的物理地址;在保护模式下,如果用户选择了使用分页管理机制,那么该线性地址还要经过页式变换才能生成最后的物理地址。
我们知道在实模式下:
物理地址=线性地址=段基址值(由段寄存器给出)×16+偏移地址
但是在保护模式下,就没有这么简单了:
P
在保护模式下,用户要使用段式管理,必须至少维护好一张GDT列表(通过GDTR来指示),和若干张LDT列表(可以没有)。
如果我们要访问数据段中偏移值为XX(放于ESI中)的变量。
CPU的操作过程如下:
1)CPU首先读取DS中的段选择字。
注意这里DS中存放的不再是段基址了,而是保护模式下的段选择字,格式如图四所示(是不是觉得跟LDTR和TR的格式一样^_^)
图四,段选择字的格式(共16位)
这里,RPL是请求特权级(RequestPrivilegeLevel)用于特权检查,如果段寄存器是CS那么这里的RPL也叫CPL(CurrentPrivilegeLevel)表示当前任务的特权级别。
TI位为1时检索GDT表,为0时检索LDT表。
Index是索引号。
比如CS=0x80表示,当前任务的特权级别为0,对应于GDT表中的第1项。
(在linux中,这里放的是系统内核代码段的描述符)
2)CPU通过分析上述的段选择字,找到位于GDT或者LDT中相应的段描述符。
80386共有四种段描述符和3种门描述符。
分类如下:
注意:
门描述符我们将在后面介绍保护模式下的中断时再介绍
各种描述符长度都为8Byte,格式如下图五所示:
图五:
各种段描述符和门描述符
S=0表示是系统段。
1表示是存储段
SegmentLimit共20bit,表示相应段的长度,单位参考G标志
BaseAddress共32bit,表示相应段的基址
G=1表示limit的单位是4KB,0表示limit的单位是1B
P=1表示当前段存在于内存中,用户可以读写。
0表示当前段不存在,用户访问时会产生一个#NP的异常。
可以用来实现虚拟内存。
Type各个字段的意义如下:
段类型
Type
说明
Type
说明
S=1
S=0
数据段
0
只读
0
未定义
1
只读、已访问
1
可用286TSS
2
读/写
2
LDT
3
读/写、已访问
3
忙的286TSS
4
只读、向下扩展
4
286调用门
5
只读、向下扩展、已访问
5
任务门
6
读/写、向下扩展
6
286中断门
7
读/写、向下扩展、已访问
7
286陷阱门
代码段
8
只执行
8
未定义
9
只执行、已访问
9
可用386TSS
A
执行/读
A
未定义
B
执行/读、已访问
B
忙的386TSS
C
只执行、一致码段
C
386调用门
D
只执行、一致码段、已访问
D
未定义
E
执行/读、一致码段
E
386中断门
F
执行/读、一致码段、已访问
F
386陷阱门
这里我们主要关注S=1的情况,也即数据段或者代码段。
S=0的情况后面会介绍到。
3)CPU通过加载数据段断或者代码段的描述符,获取到相应段的基址。
4)将获得的基址跟偏移地址相加得到所需变量的线性地址。
整个过程如下图六和图七:
图六:
选择字中TI=1,从GDT中检索的情况
图七:
选择字中TI=0,从LDT中检索的情况
最后要强调的是,这里生成的32位线性地址并不一定是最后的物理地址。
在linux和windows下都需要进行分页转换之后,才能生成真正的物理地址。
保护模式下的页式管理
在80386下页式管理是可选的。
如果同时设置了CR0的PE位和PG位,则启动了分页机制。
将32位线性地址变换为物理地址,从地址总线上输出。
先介绍几个概念:
页框(帧)(PageFrame)、页表(PageTable)、页表项(Page-TableEntries)以及页转换高速缓存。
• 页框:
物理内存中,地址连续的4KB单元,大小固定,且以4KB地址对齐。
简称页。
• 页表:
本身占一页内存。
用于存放页框的索引。
因为每个页框用32bit来索引,所以一个页表最多可以索引1K个页框。
一个任务中最多可以有1K个页表
• 页表项:
页表中的元素。
用来索引页框。
格式如下图八:
图八,页表项的格式
由于页框是与4KB地址对齐的,也即页框首地址的低12位为零。
所以只需要知道高20位即可索引到对应的页框。
• 页目录表:
和页表相似,只不过使用来索引页表的。
相应地可以用来索引1K个页表。
一个任务中只能有一个页目录表。
通过CR3索引
页目录表项:
页目录表中的元素。
用来索引页表。
格式如下图九:
图九:
页目录项的格式
注意:
上面页目录项和页表项的格式中:
1.P(present)1表示该页或页表存在于内存中,可以进行页面转换。
0表示该页不在内存中,访问时会产生页错误异常(page-fault,#PF)
如果出现页错误异常,一般来说操作系统的缺页中断异常处理程序应该:
• 从磁盘将该页拷贝入内存空间
• 将该页加载到页表项或者页目录项中。
避过那设置相应的P位以及其他位。
• 从页错误处理函数中返回,并重启被中断的任务。
2.A(Accessed)标志:
1已经访问过;0刚加载入内存,还没有访问。
3.D(Dirty)标志:
1表示对应页已经被写入过。
在页目录表项中不被使用
操作系统的内存管理程序会在系统内存紧张时用这两位来决定取出那些页来腾出空间。
4.R/W标志:
1表示可读可写;0表示只读。
跟U/S和WP标志相关。
5.U/S标志:
设定页面的访问级别。
1:
普通用户级别;0:
管理员级别
这两位不用于地址转换,但可以用于分页级的地址保护。
在CPU的地址转换中同步操作的。
实际上,页式管理就是将每一个线性地址解释为三个部分:
一个页目录表的index部分、一个页表的index部分、一个页框中的offset部分
通过如上所述的两级检索产生物理地址。
流程图如图十所示:
图十:
分页管理机制的流程图
由上面看出页式管理条件下:
80386是采用两级目录检索机制。
其中页目录表和页表各占1页,一个页目录表可以索引1K个各页表。
所以:
一个任务可以访问1K×1K个内存页。
总的内存空间为1K×1K×4KB=4GB
另一方面页目录项和页表项的格式基本一样。
这样做是为了方便CPU处理,尽可能的提高CPU的处理速率。
最后,为了提高最大程度的提高地址转换的速率,处理器会将最近所使用的页表数据存放在CPU的内部高速Cache中。
操作系统设计人员必须在当前页面改变时刷新告诉缓冲区。
可供选择的方式有二:
• 通过使用mov指令重新加载CR3页目录基址寄存器
• 通过执行一个任务切换
保护模式下的虚拟内存管理
通过前面部分的介绍,我们知道CPU如何从逻辑地址(或者称虚拟内存地址),首先通过段变换转换为线性地址。
然后再通过分页变换转换为实际的物理地址。
因此,可以看出,CPU进行地址变换的目的就在于解决虚拟内存空间到物理内存空间的映射问题。
这里我们介绍的虚拟内存管理的技术,就是通过利用二级或者外部存储空间,使程序能够不受实际物理内存量限制而使用内存的一种方法。
通常,虚拟内存空间要比实际的内存空间大很多。
实际上虚拟内存管理是由CPU和操作系统结合在一起实现的。
在80386中虚拟内存管理是这样实现的:
• 当一个程序需要使用一块不存在的内存时(即在页表项中p位为0),CPU将产生一个页错误异常中断(PageFault),并将引起中断的线性地址放入CR2控制寄存器中。
• 页错误异常处理程序将把请求的页面从二级存储器(或硬盘上)加载到物理内存中。
如果此时物理内存已经被全部占满了,那么可以借助二级存储器的一部分作为交换缓冲区(Swaper)把内存中暂时不用的一部分页面交换到出来,然后调入要求的页面。
保护模式下的中断管理
一般意义上的中断实际上可以分为中断和异常两类。
主要区别在于中断产生于CPU外部,通过CPU的引脚INTR或者NMI进入CPU,分别成为可屏蔽中断和不可屏蔽中断;异常产生于CPU的内部。
根据引起异常的程序是否可悲恢复和恢复点的不同,又进一步划分为故障(Fault),陷阱(Trap)和中止(Abort)。
故障在引起异常指令之前,会把异常的情况告诉系统,所以是可排除的。
当控制转移到故障处理程序时,所保存的断点CS以及EIP的值指向引起故障的指令。
这样当故障排除之后,之行IRET将返回原来的地方继续执行。
比如页错误故障(Page-Fault)
陷阱是在引起异常指令之后,将异常情况报告给系统的一种异常。
当控制转移到陷阱处理程序时,所保存的断点CS以及EIP指向下一条要执行的指令。
软中断指令和单步调试都是陷阱异常的例子。
中止时系统出现严重错误时,通知系统的一种异常。
引起中止的指令是无法确知的,系统也无从恢复。
中止异常处理程序往往要重新建立系统表格,并可能重启操作系统。
硬件故障就是中止异常的一个例子。
在实模式下,首先由BIOS在内存的000H~3FFH区域放入256个中断向量(中断服务程序的入口地址),每个向量包含4个字节。
初始化完成之后可以由用户修改。
当CPU在当前指令执行结束后,检查到中断请求后,将查找相应的中断矢量n。
然后从中断向量表中读入服务程序IP和CS值。
跳转过去执行中断服务程序。
在保护模式下,用户必须首先建立一个中断描述符表(IDT),可以位于内存的任何地方,但是必须通过IDTR索引之(参考图二)。
IDT最长为2KB,可以存放256个门描述符。
这些描述符大小为8Byte,可以是中断门,陷阱门和任务门的描述符。
三种描述符的格式如上图五所示。
其中中断门指向的是硬件中断服务程序入口;陷阱门指向的是异常服务程序入口。
和实模式一样,当CPU在当前指令执行结束后,检查到中断请求后,将查找相应的中断矢量n。
然后通过IDTR检索第n项描述符。
并转到响应服务程序去。
总结一下,在实模式和保护模式下CPU检测中断和读取中断向量的方式都一样,不同之处在于实模式下CPU直接访问内存[4n---4n+3],读取中断处理程序入口。
而在保护模式下,CPU通过IDTR和n访问到服务处理程序的门描述符。
再通过门描述符访问服务程序。
保护模式下的特权管理相关的一些概念:
• RPL:
选择子当中的权限位确定的权限
• CPL:
特指CS中的选择子当中的权限位确定的权限
• EPL:
EPL=Max(RPL,CPL),即RPL和CPL中数值较大的,或说权限等级较小的
• DPL:
描述符中的权限位确定的权限
• PL:
泛指以上4种特权级
• 任务特权:
=CPL
• I/O特权:
由EFLAGS寄存器的位13、14确定的权限
• 一致代码段:
一种特殊的代码段,它在CPL>=DPL时允许访问
正常的代码段在CPL=DPLRPL<=DPL时才允许访问
保护模式下的多任务管理
保护模式下最大的好处就在于可以实现多任务管理。
其实保护的意义就在于保护在多任务情况下一个任务能够不被其他任务干扰而顺利执行。
CPU的多任务主要是通过任务寄存器TR和任务状态段TSS实现的。
每个任务都有一个自己独立的TSS,用于保存任务运行的环境,参看如下图十一。
TR是个任务状态段的选择字(参看图二)。
TR从GDT中索引TSS的描述符(参看图五)。
再由TSS的描述符索引到TSS。
这种通过TR间接访问TSS的好处就在于可是保证每个任务只能访问自己的TSS,从而保证操作系统中各个任务的独立运行。
图十一:
TSS段的结构
有四种情况会导致任务切换:
1)在当前程序、任务或过程中执行一条JMP或CALL指令转到GDT中TSS描述符。
2)当标志位EFLAGS·NT设置时,当前任务执行指令IRET(或IRETD,用于32位程序转换
这两种称为直接任务切换,原理见图
3)在当前程序、任务或过程中执行一条JMP或CALL指令转到GDT或当前LDT中一个任务门描述符。
4)通过一个中断或异常矢量指向IDT中的一个任务门描述符
这两种称为间接任务切换
图十二:
直接任务切换方式
图十三:
间接任务切换方式
当处理器切换到一个新任务时,执行下列操作步骤:
⑴从一个任务门或先前任务的连接域(反向链)中(由指令IRET启动任务切换)、从指令JMP或CALL的操作数为新任务获取TSS段选择字。
⑵检查当前的任务是否允许转向新任务,访问权限规则适用于指令JMP和CALL,当前任务的CPL阈值和新任务段选择子的RPL阈值必须小于或等于TSS描述符的DPL域值或所参考任务门的DPL域值。
即直接任务转换要求:
DPLTSS描述符≥MAX(CPL现行特权,RPL新任务的段选择子)
间接任务转换要求:
DPL任务门≥MAX(CPL现行特权,RPL指向任务门的选择子)
除了由指令INTn引起的软件中断之外凡是异常或中断以及指令IRET允许切换到新任务,均无需考虑目的任务门或TSS描述符的DPL,对于由指令INTn引起的中断,则要检查其DPL。
⑶检查新任务的TSS描述符是否存在,以及是否有一个合法的界限(大于或等于67H)。
⑷检查新任务是否有效(调用、跳转、异常或中断)和是否忙(IRET返回)。
⑸检查当前任务的TSS。
新任务的TSS和任务切换中所使用的所有段描述符是否被分页进入系统存储器。
⑹如果任务切换是由指令JMP或IRET引起,处理器则清除当前任务TSS描述符中的忙标志位B,如果由指令CALL、一次异常或一次中断所引起,设置忙标志位B。
⑺如果任务切换是由指令IRET引起,处理器则清除临时保存的EFLAGS寄存器副本中的标志位NT,如果由指令CALL或JMP、一次异常或一次中断所引起,临时保存的EFLAGS寄存器副本中的标志位NT不变。
⑻在当前任务的TSS中保存当前任务的状态,处理器根据TR内容寻址当前TSS的基地址,复制下列寄存器的状态到当前任务的TSS中;所有通用目的寄存器、保存在段寄存器中的段选择符、关于EFLAGS寄存器的临时存储副本、以及指令指针EIP。
此时必须注意的是:
如果所有的检查和保存都成功执行,处理器允许任务切换,如果在从第1步到第8步的过程中发生了一个不可恢复的错误,处理器将不能完成任务间的切换,并强迫处理器返回到启动任务切换指令执行之前的状态,如果这种错误发生在任务切换允许之后(即在第9步到第14步),处理器完成任务切换,而不执行其他的访问和段的有效性检查,并在开始执行新任务之前产生一个适当的异常。
如果在允许之后发生了异常,异常句柄必须在允许处理器执行任务之前,自身完成任务切换。
⑼如果任务切换是由执行CALL指令、一次异常或一次中断所引起,处理器将当前TSS的段选择子复制到新任务TSS的反向链中。
临时保存在新任务TSS中EFLAGS寄存器副本的标志位NT被处理器设置;如果由指令IRET所引起,处理器恢复临时保存在堆栈中EFLAGS寄存器副本的标志位NT;如果由指令JMP所引起,标志位NT不变。
⑽如果任务切换是由指令CALL、指令JMP、一次异常或一次中断所引起,处理器则设置新任务TSS描述符中的忙标志位B;如果由指令IRET所引起,忙标志位B被处理器清除。
⑾设置新任务TSS中的副本标志位CR0·TS。
⑿为TR装载新任务TSS所需要的段选择子和描述符。
⒀从新任务的TSS中装载新任务的状态到处理器中,在该步中可能发生任何与装载和校验段描述符相关的错误,被装载的任务状态信息包含在LDTR、PDRB(即CR3)、EFLAGS。
EIP、通用目的寄存器和段寄存器。
⒁开始执行新任务。
对于一个异常句柄,新任