Linux实模式与保护模式下的寄存器

来源:互联网 发布:squid nginx 比较 编辑:程序博客网 时间:2024/06/02 19:36

段寄存器在实模式下和保护模式下的作用

先说点相关的:80x86中除8086/8088只能在实模式下工作之外,其他微处理器均可在实模式和保护模式下工作。
好了,从定义和区别开始吧。
先从实模式下入手:存储器地址的分段,实模式下允许的最大寻址空间为1MB(因为8086/8088地址总线宽度是20为 2^20=1048576=1024k=1M)其他的微处理器也为1M 实际上实模式就是为8086/8088而设计的工作方式,它要解决在16位字长的机器里怎么提供20位地址的问题,而解决的方法是采用存储器地址分段的方法。
机器规定:从0地址,每16个字节为一小段,而在1MB存储器里每个储存单元都有一个唯一的20为地址(物理地址)以便CPU访问存储器,所以这个20位物理地址只好由16位段地址和16位偏移地址组成,把段地址(因为是首地址,所以低四位全为0,只取高16位)左移4位再加上偏移地址值就形成物理地址,即16Dx段地址+偏移地址=物理地址(决定了唯一性)

从80826开始,就引出了保护模式,由于实模式只提供了1MB的寻址空间,不够用,而且随着多任务出现对寻址空间的要求越来越高,如80826就提供了16MB,80836就提供了达4GB的地址空间,而且虚拟存储器也能扩展空间,而保护模式寻址则对虚拟存储特性有很好的支持。

下面是保护模式和实模式的区别:
在实模式存储器寻址时,程序员只要在程序中给出存放在段寄存器中的段地址并在指令中给出偏移地址,机器就会自动用段地址左移四位加上偏移地址,求得物理地址,从而所要的储存单元的内容,因此,程序员在编程时并未直接指定所选存储单元的物理地址,而是给出了一个逻辑地址(即段地址:偏移地址),是机器自动用某种方法来取得所选的物理地址的。
在保护模式寻址时,仍然要求程序员在程序中指定逻辑地址,只是机器采用了一种比较复杂或者说比较间接的方法来求得物理地址,因此,对程序员编程来说,并未增加复杂性。在保护模式下,逻辑地址是由选择器和偏移地址组成的,选择器存放在段寄存器中,但是并不能直接表示段地址(需地址转换),而是操作系统通过一定的方法取得段地址,再和偏移地址相加来表示物理地址,这个一个区别,另外一个区别:保护模式的偏移地址为32位长,最大段长可由64KB扩大到4GB。


**************************************************************************************************************************************************************************************

实模式、保护模式、虚拟模式都是X86中的概念。
从寻址方式来说,CPU中的IP(EIP)中存放虚地址,把虚地址转换到物理地址,各个模式有各自的转换方式。
实模式下,虚地址到实地址转换:DS段寄存器左移4位与偏移地址相加,得到物理地址,寻址1M。
保护模式下,虚地址到实地址转换经过MMU(内存管理单元),也就是分段与分页机制,寻址4G。

保护有两层含义:
1、任务间保护:多任务操作系统中,一个任务不能破坏另一个任务的代码,这是通过内存分页以及不同任务的内存页映射到不同物理内存上来实现的。
2、任务内保护:系统代码与应用程序代码虽处于同一地址空间,但系统代码具有高优先级,应用程序代码处于低优先级,规定只能高优先级代码访问低优先级代码,这样杜绝用户代码破坏系统代码。这是通过段式管理来实现,4G虚拟内存中,代码数据和堆栈各占有一个段,段是一个独立有意义的内存单元,有基地址和边界以及本段的优先级,windows系统有两个优先级,Ring0(高优先级)或Ring3(低优先级),系统代码段和数据段属于Ring0,不能被用户代码(Ring3)访问。

实模式:
16bit的8086处理器标志着IntelX86王朝的开始,并且引入了一个重要概念——段。8086处理器地址总线扩展到20位,但算术逻辑运算单元(ALU)宽度即数据总线却只有16位,也就是直接运算的指针长度是16位的。为支持1M寻址空间,引入分段的方法。为支持分段8086CPU设置四个16bit段寄存器:CS、DS、SS、ES,对应于地址总线中的高16位。寻址时,段寄存器*0x10+偏移地址=物理地址。这样实现16位内存地址到20位物理地址的转换,叫“映射”。
(之前在想:为什么每个段最大不超过64K,其实很简单,因为16位CPU数据线是16位的,所以最多只能用一个16bit数来标识一个偏移量,也就说一个段最大长度是64K)

保护模式:
80286处理器地址总线位数增加到24位,可以访问16M地址空间。并引入一个新概念——保护模式。这种模式下,内存段的访问受到了限制。访问内存时不能直接从段寄存器获得段起始地址了,而要经过额外转换和检查(从其不能随意存取数据段)。为与过去兼容,80286内存寻址有两种方式:保护模式和实模式。系统启动时处理器处于实模式,只能访问1M内存空间,经过处理可以进入保护模式,可访问16M内存空间,但要从保护模式回到实模式必须重启机器。它有个致命缺陷就是80286虽然扩大了寻址空间,但是每个段大小还是64K(因为数据线还是16位的),程序规模仍然受到限制,因此很快就被80386代替了。

80386是一个32位的CPU。它的地址总线和ALU数据总线都是32位的。寻址能力达到4G。理论上说当数据总线和地址总线宽度一致时,CPU结构应该简洁明了,但80386不能做到这点,作为80X86产品系列的一员,80386必须维持那些16位段寄存器的存在,必须支持实模式,同时还要支持保护模式。Intel选择在段寄存器基础上构筑保护模式,保留16位段寄存器。在保护模式下,段范围达到4G。从80386以后Intel的CPU经历了80486、Pentium、Pentium2、Pentium3等型号,但都属于同一种系统结构的改进与加强,所以把80386以后的处理器统称为IA32(32 Bit Intel Architecture)。


深入讨论下保护模式:
保护模式与实模式中程序运行的实质是一样的,都是“CPU执行指令,操作相关数据”。最大的变化是“地址转换方式”的变化。

这里可以看下两种模式下地址转换方式的区别: 在ES存入0x1000,DI存入0xFFFF
实模式:ES:DI = 0x1000 * 0x10 + 0xFFFF = 0x1FFFF,这就是“段基址左移4位加偏移地址”。
保护模式:(注意:0x1000 = 10 0000 0000 0 00 B)ES:DI = GDT全局描述符表中第0x200项描述符给出的段基址 + 0xFFFF。为什么是第0x200项?请看下边段选择子(即段值)的结构。
这里ES在两种模式下都不是真正段地址(实模式下称"段寄存器",保护模式下称"选择子"),都是一种映射,不过映射规则不同而已。


保护模式的基本组成:(围绕“地址转换方式”的变化增设了相应机构)
1、数据段
   实模式下的各种代码段、数据段、堆栈段、中断服务程序仍然存在,这里统称为“数据段”。
2、描述符
   保护模式引入描述符类描述各种数据段,描述符为8个字节(0-7),第5个字节说明描述符类型,类型不同,描述符的结构也不同。
   若干描述符集中组成描述符表,描述符表本身也是一种数据段,也使用描述符进行描述。描述符表是一张地址转换函数表。
3、选择子
   保护模式下,逻辑地址由段选择子和段内偏移两部分组成。与实模式相比,保护模式下的段选择子代替了段值。
   段选择子有2个BYTE。高13位是描述符索引,第2位TI(Table Indicator)是描述符表指示位,TI=0指示从GTD(全局描述符表)中读取描述符,TI=1指示从LTD(局部描述符表)中读取描述符。低2位是RPL(Request Privilege Level),即进程对段访问的请求权限,具体可以查阅有关资料。


这里80X86系列引入新的寄存器GDTR和LDTR。
GDTR表示GDT在内存中的段地址和段界限,GDTR是一个48位寄存器,其中32位表示段地址,16位表示段界限(段最大64K,之前提过描述符表也使用描述符进行描述,因此描述符表最大长度也是64K,里面最多可存放64K/8Byte=8K个描述符[每个描述符8Byte]。这也是段选择子中描述符索引占13bit的原因[13b=8192])。
   因为GDT不能由GDT本身之内的描述符进行描述定义,所以采用GDTR为GDT这一特殊系统段提供一个伪描述符。

LDTR表示LDT在内存中的位置。因为LDT本身也是一种数据段,它必须有一个描述符存放于GDT中。因此LDTR使用与DS、ES、CS等相同机制,只存放一个“选择子”,通过查GDT获得LDT内存地址。
   LDTR类似于段寄存器,由程序员可见的16bit寄存器和程序员不可见的高速缓冲寄存器组成。在初始化或切换任务过程中,把描述符对应任务LDT的描述符的选择子装入LDTR,处理器根据LDTR可见部分的选择子从GDT中取出对应描述符,并把LDT的基地址、界限和属性等信息保存在LDTR的不可见高速缓冲寄存器中。随后对LDT的访问可根据保存在高速缓冲寄存器中的有关信息进行合法性检查。
   LDTR包含当前任务的LDT选择子。所以装入到LDTR的选择子必须确定一个位于GDT中类型为LDT的系统段描述符,即选择子中的TI=0,且描述符类型字段所表示的类型必须为LDT。

一个多任务操作系统必须有一个GDT,而每一个正在运行的任务(进程)都有一个LDT。可以说保护模式的引进才使得80286以后处理器实现了对多任务的硬件支持。多任务系统运行中在进程间切换时需要“环境”保护(比如各寄存器值等)。这些“环境”数据构成了一类新的数据段TSS(任务状态段)。给TSS段设置描述符(TSS描述符),并把该类描述符放在GDT中(自然不能放在LDT中,80x86也不允许),再加个TR寄存器用于查表。TR(任务寄存器)是一个起“选择子”作用的16bit寄存器。任务切换的工作就是将原任务“环境”存入TSS,更新TR,系统将自动查GDT表获得并装载新任务的“环境”,然后找到该任务执行。


段描述符高速缓冲寄存器:
   为避免每次存储器访问时,都要访问描述符表获得对应段描述符,从80286开始每个段寄存器都配有一个高速缓冲寄存器,称为“描述符高速缓冲寄存器”或“描述符投影寄存器”(shadow register),对于程序员不可见。
   每当一个选择子装入某个段寄存器,处理器自动从描述符表中取出相应描述符,把描述符中的信息保存到对应高速缓冲寄存器中。此后对该段访问时,处理器都使用对应高速缓冲寄存器中的描述符信息。段描述符高速缓冲寄存器内保存的描述符信息将一直保存到重新把选择子装入段寄存器时再更新。程序员尽管不可见段描述符高速缓冲寄存器,但必须注意到它的存在和它的上述更新时机。例如,在改变了描述符表中的某个当前段的描述符后,也要更新对应的段描述符高速缓冲寄存器的内容,即使段选择子未作改变,这可通过重新装载段寄存器实现。


分页:
   不同程序由不同用户编写,而所有这些程序完全可能使用相同地址空间,而程序切换过程一般不包括内存数据的刷新,因为这么做太浪费。因此引入分页机制才能有效完成对多任务的支持。
   分页引入的主要目标就是解决不同进程之间发生地址冲突问题。分页实质就是实现程序内地址到物理地址的映射。用类似GDT的做法:先建立页表这种数据段,80x86中使用二级页表方案,增设一个CR3寄存器用于存放一级页表(页目录)在内存中的地址,CR3共32位,低12位总为零,高20位指示页目录的内存地址,因此页目录总是按页对齐的。CR3作为进程“环境”的一部分在进程切换时被存入TSS数据段中。还有一个相应的缺页中断机制及其相关寄存器CR2(页故障线性地址寄存器)。


中断:
   80x86(保护模式)与8086(实模式)的中断机制不一样。80x86系列为中断服务提供中断/陷阱描述符,这些描述符构成中断描述符表IDT,并引入一个48bit寄存器IDTR存放IDT的内存地址。理论上IDT表同样可以有8K项,可是因为80x86只支持256个中断,因此IDT实际上最大只能有256项(2K大小,因为所有描述符都是8BYTE)。


总结可得出,保护模式下增加了:
   1、寄存器:GDTR、LDTR、IDTR、TR、CR3、CR2
   2、数据段:描述符表(GDT、LDT、IDT)、任务数据段(TSS)、页表(页目录、二级页表)
   3、机制:权限检测(利用选择子/描述符/页表项的属性位)、线性地址到物理地址的映射