结构微处理器的工作方式.ppt
第 13 章 IA-32结构微处理器的结构与工作方式,本章讲述:13.1 80 x87 FPU的结构13.2 IA-32 结构微处理器的工作方式,从功能上来说,IA-32结构微处理器由以下三部分构成:(1)自80386开始的32位微处理器;(2)自80486引入的80 x87 FPU数值处理器;(3)片内的Cache。本章介绍80 x87 FPU结构特点,重点介绍IA-32结构微处理器的工作方式。,13.1 80 x87 FPU的结构13.1.1 概述,16位微处理器之前的CPU是不适合于数值计算的。因为它们的字长太短,能表达的数值范围太小;若用多字节表示,则计算的速度太慢;而且没有乘除法指令,使用很不方便。16位甚至32位微处理器虽然有了较强的功能和较大的数值表示范围,但是做数值运算仍然十分困难。为此,在16位微处理器的基础上设计了与之相配合的专门用于数值计算的协处理器。自80486开始,在芯片内集成了协处理器(相当于80387),这些协处理器统称为80 x87 FPU。,数值计算主要有两个要求:(1)计算精度高;(2)计算速度快。为了满足这样的要求,80 x87 FPU中设置了8个80位的寄存器。这样的80位的寄存器可以用于以下7种数据类型:(1)整数字(16位);,(2)短整数(32位);(3)长整数(64位);(4)短实数(32位:1个符号位,8位阶,23位尾数),相当于单精度数;(5)长实数(64位:1个符号位,11位阶,52位尾数),相当于双精度数;(6)组合的十进制数(80位:1个符号位,18位BCD数);(7)临时实数(80位:1个符号位,15位阶,64位尾数)。也称为扩展的双精度数。,在80 x87 FPU的内部是用80位的临时实数表示的,比一般高级语言中用的双精度数还长得多。其可表达的数值范围达到:3.19*10-4932X1.2*104932,是一个相当大的数值范围,可以达到很高的精度。在80 x87 FPU中设计了具有很强数值计算能力的指令系统,其主要的指令种类如表13-1所示。,80 x87 FPU的指令是与IA-32结构微处理器的指令混合编制在一个完整的程序中的,即程序中有一些指令由80 x87 FPU执行,而另一些指令由IA-32结构微处理器执行。指令这样分配是自动实现的。而且在80 x87 FPU执行数值运算指令时,IA-32结构微处理器可以继续执行自己的指令,做到两个处理器的并发执行,从而大大提高了系统的能力。IA-32结构微处理器的所有寻址方式都可以用于寻址80 x87 FPU所需的存储器操作数,因而80 x87 FPU可以方便地处理数值数组、结构、各种变量。,80 x87 FPU与IA-32结构微处理器的密切配合可以使数值运算(特别是浮点运算)的速度提高约100倍。8086与8087结合的一些典型运算指令的执行时间如表13-2所示。特别要指出的是,80 x87 FPU的浮点运算符合IEEE的浮点标准。由于80 x87 FPU能不带舍入地处理18位十进制数,所处理的整数长度可达64位(1018),因而广泛地应用于在事务处理、商业部门和计算机辅助设计(CAD)等领域。,13.1.2 80 x87 FPU的数字系统,当人们用纸进行数值计算时,从理论上讲可以是完全精确而不带任何误差的。也即人们所使用的实数系统是连续的,任意大小与精度的数值都可以表示。对任何一个实数值来说,总存在着无穷多个更大的数值和无穷多个更小的数值;同时,在任何两个实数之间,也存在着无穷多个实数。例如在1.5与1.6之间,就存在着1.51、1.52、1.555、1.599999、1.59999999等无穷多个实数。,显然,计算机是不可能工作在整个实数轴上的。无论计算机的规模有多大,它的寄存器和存储器的长度总是有限的,这就使得计算机所能表示的数值大小(范围)及数值精度受到限制。所以,实际上计算机所能表示的实数系统是一组离散的、有限的数值,它仅仅是实数集的一个子集,是实数系统的一种近似。计算机的位数越多,可表达的数值范围越大,能表示的数值的精确度也越高。,80 x87 FPU中的实数是一种双精度的长实数,它的表示范围大约为4.1910-3071.6710306。这是一个相当大的数值范围,在实际应用中,需处理的数据和最终结果超出这一范围的情况是相当罕见的。可见,虽然80 x87 FPU是微型计算机中的协处理器,但已为实际应用提供了一个“足够大”的取值范围。而80 x87 FPU中的临时实数的取值范围更大,如表13-3所示。,把80 x87 FPU的基本实数系统投影到实数轴上,其中的实点()表示80 x87 FPU所能精确表示的实数,如图13-1所示。80 x87 FPU所能精确表示的两个相邻的实数之间总有一个间隔。如果某次运算的结果正好是80 x87 FPU所能表示的某个实数值,那么80 x87 FPU就精确地表示它;但是经常出现结果值落在两个相邻的实数之间,此时,就要根据舍入规则,将该结果舍入成为它所能表示的值。这就有一个精度问题。但是在80 x87 FPU中,大部分应用场合精确度是足够的。,从图13-1可以看到,80 x87 FPU所能表示的实数不是均匀地分布在实数轴上,在任意的2的连续的幂次方之间,80 x87 FPU所能表示的实数个数是相等的(因为它的位数是固定的),即在216(65536)和217(131072)之间存在着的可以表示的实数个数与21(2)和22(4)之间是相同的。因此,可以表示的两个相邻实数之间的间隔是随着数值的增大而增大的。,一般来说,双精度数的实数集已经足够大(表达范围)、足够密(精确度)了。在80 x87 FPU中所以还要有80位的临时实数是想给常数和中间结果以更大的范围和更高的精度,以保证最后结果的精确度。所以,在运算过程中,应尽可能把中间结果保存在80 x87 FPU的寄存器堆栈中,而不要以结果的形式存放在存储器中。80 x87 FPU的7种数据类型的格式,如图13-2所示。80 x87 FPU各种数据类型所能表达的数值范围如表13-4所示。,1.二进制整数80 x87 FPU中有三种二进制整数类型,但它们的格式实际上是相同的,只是位数不同。最高位(最左面的位)是符号位,“0”表示正,“1”表示负。负数用补码表示。值得注意的是,在80 x87 FPU中只有二进制整数是用补码表示的,其他数据类型均采用原码表示正负数只是符号不同,数值位是相同的。,2.十进制整数在80 x87 FPU中的十进制整数是用组合的BCD码表示,共用10个字节即80位。最高字节的最高位为符号位,其余位无用,后面9个字节,每个字节为两位BCD数(故称为是组合的),因此总共是18位十进制数。,3.二进制实数80 x87 FPU的二进制实数都采用科学记数法表示,分为阶和尾数,在计算机中则用浮点数表示,符合IEEE标准。每个数由三部分组成:符号字段、阶码字段和有效数字段。符号字段规定数的正负;有效数字段用于存放数值的有效数字(尾数);阶码字段用于调整二进制小数点的位置,它也决定了数值的大小。,80 x87 FPU中通常是以规格化的格式来表示其有效数字的,即以1fffff的格式表示有效数字的。其中“”表示一个假设的小数点,故有效数字由一位整数及一个由多位数字组成的小数部分组成。其中小数部分的位数取决于实数的类型,短实数为23位,长实数为52位,而临时实数为63位。在实数的这种规格化表示中,整数位取值总是“1”,这样就消除了“小”的数值的前面的那些“0”,从而使得有效位字段中所表示的有效数位的数目达到最大值。,但是80 x87 FPU的短实数和长实数中的整数位是一个隐含位,即它实际上并没有真正出现在实数格式当中,而只有在临时实数格式当中,才真正有这个整数位。为了确定数值大小,还必须考虑指数部分(阶码)。尾数的规格化处理提高了数值的精度,而引入阶码就扩大了数值的表达范围。阶码是为了把二进制小数点定位到有效数字中,它与科学计算中所采用的十进制指数类似。指数(阶码)为正,表示小数点应向右移;指数为负,小数点应向左移。,为了省去指数中的符号和便于实现实数之间的比较,80 x87 FPU中以偏移的形式来存放指数,即在原指数上加上了一个常数偏移基数。这个偏移值对于不同的数据类型是不相同的。对于短实数,偏移值为127=7FH;对于长实数偏移值为1023=3FFH;对于临时实数,偏移值则为16383=3FFFH。选择这样的偏移值是为了使阶码总是为正。这样做的好处是,两个实数可以像两个不带符号的二进制整数一样进行比较,一旦发现某个对应位不同时,就可以确定数的大小,对后面的各位就没有必要再进行比较了。采用了偏移指数以后,实现的真实指数可以由阶码段的值减去相应的偏移基数来求得。,若有一个用十六进制数表示的短实数为:BE580000展开成二进制为:1011 1110 0101 1000 0000 0000 0000 0000把它的符号位、阶码字段和有效位字段分开为:1 011 1110 0101 1000 0000 0000 0000 0000符号位为1,则此数为负数。阶码部分转换的十进制值124,减去偏移基数127,则实际的指数为:124-127=-3有效数为:1.1011 000 0000 0000 0000 0000,把这个二进制数字转换为十进制格式得到:1.0+.5+.125+.0625=1.6875把这三部分综合起来,可得短实数为:x=-1.68752-3=-1.6875(0.125)=-0.2109375,13.1.3 80 x87 FPU的结构,8087、80287、80387的结构分别如图13-3、图13-4、图13-5所示。它们的主体部分是一样的。8087的结构分成两大部分:控制单元(CU)和数值处理单元(NEU)。数值处理单元负责执行所有的数值运算处理指令;而控制单元则负责取指令、对指令译码、读写操作数、执行8087的非数值运算指令等。这两个单元能彼此相对独立地进行操作,可以使有些操作并发进行,并使NEU在进行数值运算处理时,由CU保持与主CPU(8086)同步。,1.控制单元控制单元CU的重要功能之一是保持8087与CPU(8086/8088)同步。8087的设计,可以把8087看成是8086/8088在结构上、功能上的扩充,8087的指令与CPU的指令是处在同一指令流中的。之所以能实现这一点,是由于在CPU的指令系统中安排了一条换码(交权)指令ESC。这是一条16位的指令,但只要它的前五位为11011,则CPU就把它看为一条ESC指令。所有的8087指令,对于CPU来说就是一条ESC类的指令;当主CPU取出这样一条指令时,就按ESC指令处理。,此时,CPU内部不进行任何操作相当于一条NOP指令;但ESC指令可以寻址存储器操作数,主CPU对这个操作数进行一次“假读”,即执行一次存储器读周期,按规定的寻址方式,把地址送至地址总线上,发出读存储器的有关控制信号,把存储器中指定单元的信息读出至数据总线上,但封锁了CPU的数据总线(即CPU不读取这个数据),然后CPU执行下一条指令。对于8087来说就要执行指定的指令。所以,8087就要和CPU一样取指令及对指令译码。当译码为CPU指令时(非ESC指令),8087不做任何操作;只有当译码到ESC类指令时,8087才进行指定的操作。,8087的CU监视CPU发出的状态信息(S0#、S1#、S2#与S6),当它们为(1、0、1、0)时,就是CPU的取指周期,则CU同样从数据总线上读取指令,以使8087与主CPU同步地获取指令。8086为了做到取指令与执行指令并发进行(流水线结构),在CPU内部有一个预取指令队列。所以,8087中也要有一个预取指令队列。CU又可以通过监视CPU的队列状态线(QS0、QS1)做到与CPU同步地从指令队列中取得指令,并及时对其进行译码。,8087的指令,有的没有存储器操作数,这时主CPU执行的ESC指令与执行NOP类似;有的需要存储器操作数,则主CPU执行一个上述的“假读”周期,最多可读取一个字(主CU不获取这个字),用于执行ESC指令。对于8087来说,读出的存储器操作数可能用于加载指令(读),也可能用于存储指令(写),而且操作数往往不只是一个字,这就可能出现以下情况:(1)8087只读取一个“字存储器操作数”,则在上述的主CPU的“假读”周期中,8087把从存储器中读出的一个字,从数据总线上加载至8087中。,(2)8087要读取多个“字存储器操作数”。则在主CPU执行这条ESC指令时,执行一次“假读”周期。8087一方面在上述的“假读”周期中从数据总线上获取一个字操作数;另一方面把出现在地址总线上的地址锁存下来。接着8087向主CPU发出DMA请求,接管总线,以锁存的地址为基准,从存储器中读取余下所需的字操作数。然后,把总线释放给CPU。,(3)若8087要用存储器保存数据,也就是要写存储器,则在主CPU执行这条ESC指令时,进入“假读”周期;但8087并不获取“假读”的操作数,而只是获取锁存在地址总线上的地址。当8087完成了内部操作,真正要执行写存储器操作时,它向CPU发出DMA请求,接管总线。以锁存的地址为基准,写入指定数量的操作数。然后,把总线释放给主CPU。,2.数值处理单元凡是涉及8087寄存器堆栈的所有指令都是由数值处理单元NEU执行的。这些指令包括算术运算、逻辑比较、超越函数的计算、数据传送以及常数指令。在NEU中,主要部分是8个80位的寄存器栈。为了适应各种数据类型的需要,为了保证运算的精确度,也为了使数的传送和处理更为方便,NEU中的数据通路的宽度是80位的,其中小数部分64位,指数部分15位,另加一个符号位。NEU中的80位数据是放在寄存器栈中的,并通过栈来进行处理。,8个80位的寄存器构成了一个先进后出的栈,由状态寄存器中的三位形成栈指针(ST),规定了当前的栈顶。栈的操作是以栈顶为基准的。压入(PUSH)操作把ST减1,再将某个数值装入新的栈顶寄存器;弹出(POP)操作把当前栈顶寄存器的数值输出,然后将ST加1。所以,8087中的堆栈与8086/8088中的堆栈相似,是下推式的堆栈。堆栈操作和栈中的数值及堆栈指针的变化情况,如图13-6所示。,在8087初始化时,ST=0,则在入堆操作时ST-1=000-001=111=7,这在工作时是要注意的。8087对寄存器栈操作时,可以明显规定某个寄存器ST(i),这称为显式寻址;也可以不明显规定是哪个寄存器,则隐含着对当前的栈顶(即状态寄存器中的ST所指向的某个寄存器)进行操作。例如,求平方根指令通常为:FSQRT即对栈顶寄存器中的操作数求平方根,这种寻址称为隐式寻址。,要注意的是,隐式寻址是指出当前栈顶寄存器,也即是由状态寄存器中的堆栈指针所指定的寄存器,而不一定是寄存器0(R0)。若状态寄存器中的堆栈指针为000,则当前栈顶即为R0;若堆栈指针是010,则当前栈顶是R2。另外,显式指定的ST(i),其中0i7,也是相对于当前栈顶的,ST(i)是从当前栈顶计算起的第i个寄存器。例如有指令:FADD ST,ST(2)若状态寄存器中的ST=001,则是指R1与R3(001+2=3)的内容相加。,为了优化数值协处理器的功能,每一个寄存器都有一个标志位(每寄存器两位)与之相对应,用以反映寄存器的情况,如图13-7所示。这个标志字,对于程序员来说是无用的。80287的功能结构几乎与8087完全一样,只是把8087的控制单元CU改称为总线接口单元BIU,两者在功能上是完全一样的,特别是数值处理单元部分都是以8个80位的堆栈作为核心,支持多种数据格式。,80387的结构更为复杂一些。从大的方面来说,有总线控制逻辑、数据接口和控制单元及浮点单元三个部分。但从本质上来说,仍可分为80387内部的浮点运算部分以及与80386和存储器连接的接口和控制部分。后一部分负责监视80386的指令流,当译码分析到数值运算指令时,由80387执行。执行所需的数据以及计算结果的存储也是由这部分负责,其工作过程与8087部分介绍的一致。,只是80387与80386相应的有关寄存器和指针是32位的,而且有可能与工作在保护虚地址方式的80386协同工作,主要反映在事故指针上。但80387的主体部分仍是以 8个80位的堆栈为核心,配合必要的运算逻辑的浮点运算部分。这部分除了在运算逻辑部分作了较大的改进外,其他部分与8087是一致的,特别是堆栈格式和操作方法是完全一样的。8087、80287和80387的状态字分别如图13-8、图13-9、图13-10所示。这三者的状态字基本上是一样的。,最高位都是B(BUSY)位,反映80 x87 FPU是否忙的状态位。80 x87 FPU都有一条BUSY引线,一旦它们的数值运算部分开始执行指令,则状态寄存器中的B位就置1,输出引线BUSY就变为高电平。在IA-32结构微处理器中,设置了引线TEST(TEST与80 x87 FPU的BUSY线相连)和指令WAIT,就可以做到IA-32结构微处理器与80 x87协处理器同步。第1311位ST即为堆栈指针,它的值规定了哪一个寄存器是当前的栈顶。ST=000表示栈顶是第0号寄存器R0,ST=001表示栈顶是R1,S=111表示栈顶是R7。,4个条件位C3、C2、C1、C0,用于80 x87 FPU的比较指令后,反映比较的结果,是一些转移指令的依据。当执行了比较指令(栈顶的内容与指定的操作数相比较)之后,状态位C3、C2、C0反映了比较以后的结果,如表13-5所示。若操作数为0,则上述条件码也可以用来反映栈顶(TOP)的内容是否等于0或大于0、小于0。当执行了检验指令FXAM后,条件码反映出栈顶内容的一些特殊情况。8087的条件码和数据检验情况如表13-6所示。,状态字的第7位在8087中为IR位即中断请求标志位,在80287和80387中都称为ES位即总的出错状态位。即在80 x87 FPU中,有任一种未屏蔽的异常发生,则IR位或ES位置1,用来表示8087向主CPU发出了一个待处理的中断请求;在80287和80387中相应的ERROR#引脚就发出有效信息。,在80387中还利用了状态字的第6位(在8087和80287中此位未用,为0)作为堆栈标志SF,这一位用于区分无效操作是由于堆栈的上溢或下溢造成的,还是由于别的类型的无效操作造成的。SF=1,表示是堆栈操作造成的,此时,条件码的C1=1表示是堆栈上溢;C1=0表示是堆栈下溢。,状态字的第05位用来反映发生了某种异常事件。当80 x87 FPU在执行某条指令时,有可能发生下列的6种异常情况,当某种异常情况发生时,状态字中的相应位置位若未予屏蔽的话,就可以发出中断(或异常请求)。,80 x87 FPU中可能出现的6种异常情况为:(1)无效操作(Invalid Operation):在出现下面某种情况时,80 x87 FPU就认为是一种无效操作:堆栈溢出(80 x87 FPU中的堆栈是由8个寄存器组成,在做连续9个以上的压栈或退栈操作时,将发生堆栈溢出);将某个非有效数(NAN)(例如字符)作为操作数;操作结果是不定的(例如、-、求负数的平方根)。这些无效操作,一般说明发生了程序错误。,(2)上溢(Overflow):当运算的结果太大,超过了目标操作数所能表达的范围,就发生上溢错误。(3)下溢(Underflow):运算的结果虽然不是0,但其数据太小,超出了目标操作数能表达的最小值的范围,就发生下溢错误。(4)被零除(Zero divisor):当用0去除一个非0操作数,就发生此类错误。,(5)不可规格化操作数(Denormalized Operand):当操作结果中至少有一个为不可规格化的操作数时,就发生此类错误。不可规格化的操作数是指操作数太小,若要规格化就会产生下溢。(6)结果不精确(Inexact Result):如果操作结果的真值,不能用规定格式的目标操作数精确表示,也就是要按80 x87 FPU中规定的舍入方法进行舍入处理时,就设置精度事故标志。在实际应用中,这种事件是经常发生的,它表示在运行过程中牺牲了一些精度,这通常是可以接受的。,对以上6种异常事件,在状态寄存器中设置了相应的出错标志位(IE、OE、UE、ZE、DE、PE)。在这6种异常中,无效操作、被0除、不可规格化操作数等是故障类异常,是在执行实际操作之前产生异常;而上溢/下溢及精度事故等,则要在指令执行后,在结果计算出来后才能检测,因此是陷阱类异常。,在发现前一类事故时,寄存器堆栈和存储器中的操作数的内容不会改变,它们与引起错误的指令未被执行时的状态一样;而后一类事故,当发现错误时,指令已经执行,寄存器堆栈和存储器中的内容可能已经更新。因此,这两类事故所需的恢复和处理过程也有所不同。这6种异常事件有可能多个同时发生,此时对异常事件的处理就要按照事件的优先权次序顺序进行。80 x87 FPU中规定的优先权次序为:,不可规格化操作数(未屏蔽)无效操作被零除不可规格化操作数(被屏蔽)上溢/下溢结果不精确排在前面的优先权高。在一般情况下,当这些异常事件发生时,就要求程序员用软件进行处理。故当发生这些异常事件时,80 x87 FPU除了在状态寄存器中设置相应的标志外,还要向主CPU发出中断请求,用程序员编制的中断服务程序进行处理。,但是,事故处理程序往往是难于编写的,好的事故处理程序就更难于编写。Intel公司的设计者综合了各方面的情况,针对各种异常条件,编写了较合理的事故处理程序,放在80 x87 FPU中作为隐含的异常处理程序。只要用户对某种异常事故设置屏蔽,则当此类异常发生时,80 x87 FPU一方面设置相应的异常标志,另一方面不发出中断请求,而是转入片内隐含的异常处理程序进行处理。80 x87 FPU对各种异常事件的响应如表13-9所示。,在大量应用中发现:在把除无效操作之外的所有事故都屏蔽掉之后,就能够以最少的软件开销获得令人满意的结果。之所以没有把无效操作这种异常事件屏蔽掉,是因为它通常代表了程序中的一种必须加以纠正的“致命”的错误。要对事故进行屏蔽就要用到控制字。80 x87 FPU的控制字格式分别如图13-11、图13-12、图13-13所示。,三者格式基本上是相同的,只是在80287和80387中没用中断允许控制位IEM,在80387中没用无穷大控制位IC(在80387中的无穷大都是仿射的)。80 x87 FPU为了适应各种数据类型和各种运算处理的需要,设置了三个处理方式控制位。精度控制位PC,是与参与运算的数据类型相一致的,用以决定操作数是短整数、短实数还是长实数等。,在算术运算或存取操作中,当目标数据的格式不能完全准确地表示结果时,就会发生舍入问题。例如,在把某个实数类型的数值送到一个短实数或整数时,就有可能需要进行舍入处理。80 x87 FPU中具有四种舍入处理方式,采用何种处理方式,由舍入控制RC决定。若结果X不能用目标数据类型精确地表示,而在给定的数据类型中与它最接近的可表示的数为X1和X2,且X1XX2,那么,在运算得到X之后,就要根据舍入控制方式,把X舍入成X1或X2。四种方式的舍入结果如表13-10所示。,显然,在实际应用中,应根据需要选择舍入方式。在大多数应用中,都是采用“最近舍入”方式,这与习惯的“四舍五入”方式十分相似;“截尾”舍入方式往往用于对整数的运算中;“向下舍入”和“向上舍入”一般用于区间运算。8087、80287的控制字中还有一位IC(第12位),专门用于对无穷大进行控制。8087、80287的实数系统有两种模型,一种是仿射闭包,另一种是投射闭包,如图13-14所示。,控制字中的IC位用来在这两种模型中选择一个。当IC位为0时,选择投射闭包。此时8087、80287的特殊值无穷大没有正、负之分,即它是不带符号的,在绝大多数计算机中,使用的都是这种模型。当IC位为1时,选择仿射闭包。此时无穷大就有+和-之分。80387中使用这种方式。,有些异常事件是在指令执行后检测到的,当异常发生时,原有的数据已经改变了。为了便于对事故进行分析,在80 x87 FPU中设置了事故指针。8087中的事故指针如图13-15所示。这是一个4个字(16位)的指示器,用以保存产生异常指令的20位物理地址;保存了此指令操作码的低11位(高5位为ESC指令标志),以确定是什么指令;若此指令有存储器操作数,则保存它的20位物理地址,以便查找。每当8087的NEU执行一条指令时,其CU就把上述内容存入事故指示器中。可用8087的FSAVE/FNSAVE或FSTENV/FNSTENV指令,把事故指示器作为环境的一部分放在存储器中以供查找。,80387是与80386配合工作的。80386是32位的处理器,其内部寄存器是32位,地址偏移量一般也是32位。80386有两种工作方式:实地址方式和保护虚地址方式;而且在保护虚地址方式下还有32位方式和16位方式(为了与80286兼容)。在各种方式下的事故指示器分别如图13-17、图13-18、图13-19所示。,13.2 IA-32结构微处理器的工作方式,IA-32结构微处理器有两种主要的工作方式:实地址方式和保护虚地址方式。实地址方式是为了与8086兼容而设置的方式。在实地址方式下,具有32条地址线的IA-32结构微处理器只有低20条地址线起作用,能寻址1MB的物理地址;此时,IA-32结构微处理器相当于一个快速的8086,虽然可以使用32位的数据寄存器,但远不能充分发挥IA-32结构微处理器的全部功能。,保护虚地址方式是IA-32结构微处理器的主要工作方式,在此方式下,全部32条地址线都能寻址,故可寻址高达4GB的物理存储器;在保护方式下,IA-32结构微处理器支持虚拟存储器的功能,一个任务可运行多达16K个段,每个段最大可为4GB,故一个任务最大可达64MMB的虚拟地址;在保护方式下运行的程序分为4个特权等级:0、1、2、3,操作系统核心运行在最高特权等级0;用户程序运行在最低特权等级3。IA-32结构微处理器中有完善的特权检查机制,既能实现资源共享又能保证程序和数据的安全和保密,任务之间的隔离。,在保护方式下,IA-32结构微处理器支持多用户多任务操作系统,可以用一条指令实现任务切换,而且任务的环境得到了很好的保护。IA-32结构微处理器的芯片内包含一个存储管理单元MMU,在保护方式下可以实现分页,通过二级页表,可以把物理地址映射到线性地址空间的任何区域。总之,在保护虚地址方式下,IA-32结构微处理器有很强的功能,保护方式是IA-32结构微处理器的主要工作方式。而且,IA-32结构微处理器在保护虚地址方式下,增加了一种虚拟8086方式,可以在多任务的条件下,有的任务运行MS-DOS,这是一种与8086兼容但又不同于实地址方式的工作方式。,13.2.1 实地址方式,实地址方式和保护虚地址方式的区分是由控制寄存器CR0的最低位PE位决定的。若PE位为0,则工作在实地址方式;若PE=1,则工作在保护虚地址方式。IA-32结构微处理器在系统复位后,CR0的PE=0,即工作在实地址方式。在经过了必要的初始化以后(在后面详细介绍),用MOV指令使CR0加载一个PE位等于1的新的操作数,就使工作方式切换到保护虚地址方式。,在实地址方式下的存储器寻址与8086是一样的,32位地址线中的A31A20不起作用。由段寄存器(CS、SS、DS、ES)的内容乘以16作为段基地址,加上16位的段内偏移量形成20位的物理地址。在实地址方式下,每一个段最大可达64KB。所有的段都是可读、写和执行的。在实地址方式下的内存是不能分页的,故线性地址和物理地址是统一的。,在实地址方式下运行的程序不分特权等级。实际上,实地址方式下的程序相当于工作在特权级0,它能执行控制寄存器(CR0、CR3)传送指令,加载GDTR、LDTR、TR等特权指令。除保护虚地址方式下的一些专用指令之外,所有其他指令都能在实地址方式下执行。所以,系统复位以后,要在实地址方式下,初始化GDT、IDT和两级页表,加载CR3,然后通过加载CR0使PE=1才能进入保护虚地址方式。,实地址方式下不能实现多任务,所以IA-32结构微处理器的实地址方式,是系统复位后向保护虚地址方式过渡的一种方式。一部分IA-32结构微处理器在DOS支持下工作,只工作在实地址方式,主要是为了与8086兼容,能运行DOS支持下的软件。,IA-32结构微处理器在实地址方式下有两个内存保留区:系统初始化区和中断向量表区。IA-32结构微处理器在复位以后,CS寄存器的值为F000H,而IP初始化为FFF0H,而且系统强迫地址总线的高12位为1。所以,初始化后的入口地址为FFFFFFF0H,从FFFFFFF0HFFFFFFFFH为初始化的保留区,通常在FFFFFFF0H处存放一条段间跳转指令,转至系统的入口处。,实地址方式与8086方式相似,在内存的00000000H000003FFH的1KB区域内,存放一个具有256个向量的中断向量表,每一向量对应着一个四个字节的中断服务程序的入口地址(两个字节的段寄存器值,两个字节的段内偏移量)。在实地址方式下的中断与异常与保护虚地址方式下有较大的区别。这主要涉及系统程序员的工作,在本书中不作详细分析。,13.2.2 保护虚地址方式,1.保护方式下的寻址机制在保护方式下,一个存储单元的地址也是由段基地址和段内偏移量两部分组成。以段内偏移量来说,除能扩展到全地址(32位)外与实地址方式下区别不大,寻址方式的根本区别在于如何确定段基地址。在实地址方式下,段寄存器的内容16(即左移四位)就形成段基地址,故段基地址是20位的,只能寻址1MB;在保护方式下,段基地址也是32位的,所以就不能由段寄存器的内容直接形成32位的段基地址,而要经过转换。,于是在内存中就有一个表,每一个内存段对应着表中的一项,此项中包含32位的段基地址。为了适应多用户、多任务操作系统的需要,一个段还要有一些其他信息,例如,段的大小(界限)和段的一些读写权限、段的类型等。在IA-32结构微处理器中,一个段用一个8字节的描述符来描述,这些描述符构成了一个表,称为描述符表。由描述符中所规定的段基地址再加上32位的段内偏移量就可以寻址一个存储单元,如图13-20所示。,由段基地址(32位)和段内偏移量(32位)形成的地址称为线性地址(32位)。在IA-32结构微处理器片内有分页的MMU,当启用分页机制时(CR0的最高位PG=1),经过分页机制可以把线性地址转换为存储器的物理地址,如图13-21所示。当不启用分页机制时(CR0的PG=0),线性地址即为内存的物理地址。保护方式下的段内偏移量为32位,故一个段最大可达4GB。,2.全局描述符表和局部描述符表在IA-32结构微处理器中,有三种类型描述符表:全局描述符表(gdt)、局部描述符表(ldt)和中断描述符表(idt)。在整个系统中,全局描述符表和中断描述符表都只有一个,局部描述符表可以有若干个,每一个任务一个。每个描述符表本身形成一个段,最多可以有8K(8192)个描述符。但IA-32结构微处理器中,最多只能处理256个中断向量,故中断描述符表最多只包含256个中断描述符。每个描述符表构成一个段,也有段的基地址、段的界限和其他特性,也即有一个相应的描述符来描述。这样的描述符必须放在全局描述符表中。,(1)全局描述符表(gdt)全局描述符表中,包含着系统中每一个任务都可能(或可以)访问的段的描述符,通常包含操作系统使用的码段、数据段和堆栈段,各种任务状态段、系统中所有的ldt表的描述符等。(2)局部描述符表(ldt)通常,操作系统的设计者使每一个任务都有自己的ldt。ldt包含了此任务所使用的码段、数据段、堆栈段描述符;也可包含此任务所使用的一些控制描述符,如任务门、调用门描述符。使用ldt这样的数据结构,就可以使指定任务的码段、数据段等与别的任务相隔离以达到保护。,使用gdt、ldt这两种数据结构可以达到既保护又可共享全局数据的目的。从系统的虚拟地址空间来看,整个虚拟地址空间可以分成两半,一半空间的描述符在全局描述符表中,另一半空间的描述符在局部描述符表中。每一个表都可以包含多达8192个描述符(即对应的空间可由8192个段组成),每一个段最大可为4GB。故最大的虚拟地址空间可为:2819244GB=64MMB当任务切换时,ldt就切换为新任务的ldt,而gdt是不变的。因此,由gdt所映像的虚拟地址空间对所有的任务是公共的;而ldt所映像的虚拟地址空间,只局限于任务,随着任务而改变。,对于一个系统来说,操作系统是面向所有任务的,它应该在gdt的映像中。一些全局性的数据、表格,公用的实用程序等也应在gdt的映像中。上述的全局和局部地址空间的情况如图13-22所示。它既可以做到互相隔离、保护,也可以做到全局数据的共享。,3.描述符在保护虚地址方式下的每一个段,都有一个相应的描述符。描述符由8个字节组成,包含了此段的基地址(32位)、段的大小(20位)、段的类型等一些主要特性。在IA-32结构微处理器中,主要有两种类型描述符:段码和数据段描述符;特种数据段和控制描述符。在后一种里又分为特种数据段描述符和控制(门)描述符两大类。,(1)码段和数据段描述符码段和数据段描述符一般格式如图13-23所示。图13-23中规定了32位的段基址(由基址3124、基址2316和基址150三部分构成)、20位段界限(由界限1916和界限150两部分构成)。另有一个粒度位G。G=0,段长度以字节为粒度,20位段界限可定义段的大小为1兆字节(1MB);G=1,段长度以页为粒度,每页为4KB,段的界限为1M页,可定义段的大小:1M4KB=4GB,描述符中有一个字节,称为段的访问权字节,它描述了段的一些重要特性:段的访问权字节的高4位在所有的段描述符中都是相同的:最高位P(Present)为存在位,P=1,此段被映像到物理存储器;P=0,无物理存储器映像存在。第6、5位DPL(Descriptor Privilege Level)为描述符特权级,规定了此段的特权级,用于特权检查,以决定对此段能否进行访问。第4位S(Segment)区分上述的两大类描述符。S=0,为特殊数据段或控制描述符;S=1,为码段或数据段描述符。,访问权字节的低4位,在S=0或S=1时是不同的。在S=1即为码段或数据段描述符时,其作用如表13-11所示。它反映了码段或数据段,以及段能否被读写访问。段的访问权字节是段的极其重要的属性。描述符的各个字段,可以由相应的指令来读取和设置。在描述符中,比访问权字节地址高的一个字节的有些位也是很重要的,其中的G位是界限的粒度位。,D位也很重要,其作用如下:码段描述符的D位用于设置由指令所引用的地址和操作数据的默认值。D=1,指示默认值是32位地址、32位或8位操作数,这是IA-32结构微处理器在保护方式下的正常设置;D=0,指示默认值是16位地址、16位或8位操作数,这是在IA-32结构微处理器中为了执行80286的程序而设置的。由D位所设置的默认值,可以由前面章节中提到的地址前缀和操作数前缀加以改变。,对于设置为向下扩展的段,D位决定段的上边界。D=1,指示段的上边界边4G;D=0,指示段的上边界为64K。由SS寻址的段,若D位=1,规定用ESP作为指针,堆栈操作是32位的;若D=0,则用SP作为指针,堆栈操作是16位的。,(2)特种数据段和控制描述符此类描述符的一般格式如图13-24所示。其中,TYPE字段共4位,用此4位二进制的值区分描述符的具体类型。,(3)特种数据段描述符80 x86系统中有两种特种数据段描述符,即局部描述符表(ldt)的描述符和任务状态段TSS(Task Status Segment)的描述符。ldt中包含了一个任务所要访问的段的所有描述符,但此表本身构成一个存储器数据段,它也有段基地址、段的界限、段的属性,需要一个描述符与之相对应,这样的描述符当然要放在gdt中。,当任务切换时,一个任务的环境(状态)需要保存(离去的任务)或需要恢复(进入的任