为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

80386 和 保护模式

2010-12-01 17页 doc 368KB 31阅读

用户头像

is_131175

暂无简介

举报
80386 和 保护模式80386 和 保护模式 80386 和 保护模式 ___William Liu Intel CPU 一般可以运行在两种模式之下,即实模式和保护模式。早期的 Intel CPU ( 8086 , 8088 )只能工作在实模式之下,系统中只能运行单个任务,而且只能使用实地址模式。对于 Intel 80386 以上的芯片则还可以运行在 32 位的保护模式之下。在保护模式之下的 CPU 可以支持多任务;支持 4GB 的物理内存;支持 64TB 的虚拟内存;支持内存的页式管理和段式管理以及支持特权级。   本文档将首先介绍 Intel...
80386 和 保护模式
80386 和 保护模式 80386 和 保护模式 ___William Liu Intel CPU 一般可以运行在两种模式之下,即实模式和保护模式。早期的 Intel CPU ( 8086 , 8088 )只能工作在实模式之下,系统中只能运行单个任务,而且只能使用实地址模式。对于 Intel 80386 以上的芯片则还可以运行在 32 位的保护模式之下。在保护模式之下的 CPU 可以支持多任务;支持 4GB 的物理内存;支持 64TB 的虚拟内存;支持内存的页式管理和段式管理以及支持特权级。   本文档将首先介绍 Intel 80386 CPU 的几个内部寄存器,然后再由浅入深的分别介绍保护模式下的段式管理,页式管理,虚拟内存,多任务以及特权级管理等几个方面。   Intel 80386 CPU 的内部寄存器 这一部分先大致介绍一下 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 全局描述符表寄存器( Global Descriptor Table Register ),存放的是一个指向内存单元列表的指针,用于指向全局段描述表( GDT ),如图二所示。共 48bit ,高 32bit 是 GDT 的基址,低 16bit 描述 GDT 的长度。由于每项 8Byte ,所以共可以有 2^(16)/8=2^13 项。   IDTR 中断描述符表寄存器( Interrupt Descriptor Table Register ),存放的是也一个指向内存单元列表的指针,用于指向全局中断描述符表( IDT ),如图二所示。跟 GDTR 一样,共 48bit 。   LDTR 局部描述符表寄存器( Local Descriptor Table Register ),存放的是 LDT 的段选择字。用于从 GDT 中索引出当前任务的局部描述符表( LDT ),如图二所示。共 16bit ,高 13bit 刚好可以索引到 GDT 的最大限长。 TI 位置 1 , RPL 是请求特权级( Request Privilege Level )用于特权检查。   TR 任务寄存器( Task Register ),跟 LDTR 一样,存放的是任务状态段 TSS ( Task State Segment )的段选择字。用于从 GDT 中索引出当前任务的 TSS ,如图二所示。共 16bit 。   图二:系统内存管理寄存器   3) 系统控制寄存器: 386 CPU 共四个,分别是 CR0 、 CR1 、 CR2 、 CR3 , 486 以后又增设了 CR4 。如图三所示。 其中, CR0 是用于系统整体的控制。 CR1 保留。 CR2 用于保存页面转换时出错的线性地址。 CR3 存放页目录基址。 CR4 用于各种 CPU 级联相关。 图三:系统控制寄存器 需要注意的几个位是: PE : CR0 的 0bit , Protect Enable 。使能 386 的保护模式。 PG : CR0 的 31bit , Paging 。 386 进入保护模式之后,启动分页机制。 Page-Directory Base : CR3 的 12 - 31bit 存放页目录首地址。每个任务只能唯一一个 Page-Fault Linear Address : 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 是请求特权级( Request Privilege Level )用于特权检查,如果段寄存器是 CS 那么这里的 RPL 也叫 CPL ( Current Privilege Level )表示当前任务的特权级别。 TI 位为 1 时检索 GDT 表,为 0 时检索 LDT 表。 Index 是索引号。比如 CS = 0x80 表示,当前任务的特权级别为 0 ,对应于 GDT 表中的第 1 项。(在 linux 中,这里放的是系统内核代码段的描述符)   2 ) CPU 通过上述的段选择字,找到位于 GDT 或者 LDT 中相应的段描述符。 80386 共有四种段描述符和 3 种门描述符。分类如下: 注意:门描述符我们将在后面介绍保护模式下的中断时再介绍   各种描述符长度都为 8Byte ,格式如下图五所示: 图五:各种段描述符和门描述符   S = 0 表示是系统段。 1 表示是存储段 Segment Limit 共 20bit ,表示相应段的长度,单位参考 G 标志 Base Address 共 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 位线性地址变换为物理地址,从地址总线上输出。   先介绍几个概念:页框(帧)( Page Frame )、页表( Page Table )、页表项( Page-Table Entries )以及页转换高速缓存。   •  页框:物理内存中,地址连续的 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 将产生一个页错误异常中断( Page Fault ),并将引起中断的线性地址放入 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=DPL RPL<=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 域值。即直接任务转换要求: DPL TSS 描述符≥ MAX ( CPL 现行特权, RPL 新任务的段选择子) 间接任务转换要求: DPL 任务门≥ MAX ( CPL 现行特权, RPL 指向任务门的选择子) 除了由指令 INT n 引起的软件中断之外凡是异常或中断以及指令 IRET 允许切换到新任务,均无需考虑目的任务门或 TSS 描述符的 DPL ,对于由指令 INT n 引起的中断,则要检查其 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 、通用目的寄存器和段寄存器。 ⒁ 开始执行新任务。对于一个异常句柄,新任务的第一条指令不执行。 当任务成功切换时,当前执行任务的状态总被保存。如果该任务被恢复执行,执行使从所保存 EIP 值指向的指令处开始,寄存器的值也被恢复到任务挂起时的保存值,这与中断现场的恢复类似。 当发生任务切换时,新任务的特权级并不继承前任挂起任务的特权级,新任务按 CS 寄存器的 CPL 域指定的特权级开始执行, CS 从 TSS 中装载得到。通过各自独立的地址空间、 不同的 TSS 和访问权限可以对任务进行很好的隔离,在任务切换时软件并不需要执行对特权级的直接检查。 一个任务的运行环境 一个任务的运行环境由寄存器组和地址空间组成。寄存器组中保存的是任务当前的状态。地址空间由若干个段构成,每个段都有相应的段描述符,这些描述符要么放在 GDT 中,为所有任务共享,要么放在 LDT 中,是任务的私有空间。 任务的段包括: GDT 中索引的代码段,数据段,一般这时操作系统的内核数据段代码段。 LDT 段,就是一张局部描述符表,由该表再索引其他私有段,比如私有数据段,私有代码等。 TSS 段,保存有该任务的所有信息,任务切换用。 最后要说明的是,多任务状态是可选的。操作系统完全可以在没有任何任务的情况下运行。以 Linux 为例,系统就是在所有外设初始化完成之后才调用 move_to_user_mode() 函数,产生任务 0 的。 实现由实模式进入保护模式 1 )创建 GDT 表 2 )置 PE 位为 1 进入保护模式 3 )执行跳转命令,以清除在实模式下读取的任何指令
/
本文档为【80386 和 保护模式】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索