《See MIPS Run》–第五章 异常,中断与初始化

Sina WeiboBaiduLinkedInQQGoogle+RedditEvernote分享




第五章 异常,中断与初始化

在MIPS体系结构中,中断,陷入,系统调用,以及其他中断程序正常执行的事情统统被称为异常.异常在MIPS体系结构中被同一种机制处理.异常包括:

外部事件.包括中断,读总线错.在有外部事件时,中断被用来引起CPU的注意.使用中断比使用CPU轮询机制来得快且更有效.
中断是唯一由CPU执行以外的事件引起的异常.因为我们不能通过注意来避免中断,在必要是,我们只能用一种软件的办法来禁止中断.

内存映射异常.当没有合适的物理地址对应虚拟地址时,或当写一个有写保护的页时,会发生此种异常.操作系统会监查内存映射异常的具体原因.某些异常是由于应用程序访问了非法内存.操作系统会终止应用程序的执行以保护其他应用程序.良性的内存映射异常可以触发操作系统执行从复杂到简单的一系列操作:操作可以复杂到装入一个需要时调入内存的虚拟页,或简单到扩大栈的空间.

其他需要内核更正的不寻常情况.一个例子是浮点指令:当硬件无法处理某些困难和少见的操作符和操作数的组合时,硬件会产生一个异常,寻求软件模拟.这类情况比较模糊,因为不同的对这类情况会有不同的处理意见.未对齐的装入在某些操作系统中被由软件处理,在另外一些操作系统中被当做错误.

程序或硬件检查出的错误.包括非法指令,在不正确的用户权限下执行的指令,在相应SR位被禁止时执行的协处理器指令,整数溢出,地址对齐出错,用户模式下访问超出用户段(KUSEG)地址.

数据完整性错误.很多MIPS CPU不断对来自总线和缓存的数据作字节校验或字校验.校验错在R4000及以后的CPU上产生一个特殊异常.

系统调用和陷入.某些指令只是用来产生异常.它们提供了一种进入操作系统的安全机制(系统调用,条件陷入,断点).

某些事件不产生中断,尽管大家认为它们会.比如写总线错.CPU把数据和地址放入写缓冲中,然后继续执行.写操作可能在几个时钟周期后发生.在这种情况下,很难判断到底是哪条指令产生写错误.某些系统利用外部机制来解决这个问题.这种外部机制有可能会产生异常来引起CPU注意.

更为有意思的是,在大多数32位的CPU上,缓存中的校验错不产生异常.错误被放在一个状态寄存器位SR(PE)里,你必需自己去察看它.在R3000(32位的CPU)里,缓存校验在以后加入用于调试目的.

在这一章里,我们将要讨论:CPU如何决定产生异常,软件如何正确处理异常,为什么MIPS的异常叫做精确异常,异常入口点,以及一些软件编程约定.

在嵌入式系统中,来自CPU外部的硬件中断最常见的异常异常,要求得到及时处理,并很容易导致不易觉查的错误.异常嵌套—在一个异常的处理中发生另一个异常—可能引起一些特殊的错误.

系统重置(RESET)后,MIPS CPU靠异常启动,所以CPU启动也在这一章中讨论.在这一章末尾,我们会讨论一些相关的话题,如软件模拟机器指令,构造一个信号量用以提供任务到任务的通信.第十二章还包含一个有关异常处理的有注释的程序. 

第一节 精确异常(PRECISE EXCEPTION)

我们会在MIPS文档中看见精确异常这个词.精确异常非常有用.为了讲清楚为什么,我们应该看看它的对立面:

在一个以优化性能为主要目的的流水线中(或者是用于指令并行执行的设计中).系统的顺序执行只是一种抽象.如果硬件不是设计得特别聪明,中断讲使我们看到程序不是顺序执行的.

当一个异常发生,系统的顺序执行被中断时,流水线(PIPELINE)的CPU将会有几条指令出于流水线的不同阶段(STAGE).因为我们不想中断处理破坏程序的正常执行,对于没有执行完的指令,我们必需记住它们执行到哪一个阶段,以便在中断处理之后能恢复程序执行.

如果CPU是精确异常的,那么异常的软件处理就会非常简单.对于一个精确异常的CPU,在异常发生时,我们都会有一个引起异常的指令(EXCEPTION VICTIM).该指令前面的所有指令都以执行完,该指令以及该指令以后的指令都不会有任何软件值得考虑的负作用.所以软件作异常处理时,可以完全忽略指令的非顺序执行(即假定系统时顺序执行的—译者)

对于MIPS体系结构,几乎所有的异常都是精确异常,陈述如下:

能准确定位恢复程序执行的正确位置.在任何异常产生后,CPU的控制寄存器EPC指向中断处理后恢复程序执行的正确位置.在绝大多数情况下,它是产生异常的指令,如果产生异常的指令是转移指令的延迟单元(BRANCH DELAY SLOT),EPC则指向该延迟单元的前一条转移指令.异常处理后,讲返回该转移指令.如果EPC错误的指向延迟单元,则异常处理后讲会从延迟单元开始执行,从而导致程序的错误执行.如果产生异常的指令是转移指令的延迟单元,原因寄存器位CAUSE(BD)将会被设置,因为有些异常处理程序需要知道是那条指令引起的异常,在这种情况下,它是EPC+4.

看起来找出产生异常的指令可能很容易,但在有些级数很多的流水线CPU上这件事并不容易.

异常发生的顺序与指令的顺序相同.在非流水线的CPU上,这是很显然的.在流水线的CPU上,异常可能会发生在指令执行的不同阶段.产生潜在的问题.比如,如果一个读内存指令产生一个地址异常,这个异常一直要到读写内存(MEM)阶段才产生.如果它的后一条指令在取指令(IF)阶段就产生错误,则后一条指令讲想产生异常.从而破坏异常发生的顺序很指令的顺序相同这个约定.

为了避免这个问题.被发现的异常情况一直要到确认有异常情况的指令的前面的所有指令都不产生异常时才产生异常.在发现异常情况时,该情况只是被记下来沿着流水线传递下去直到某一级.如果在这个过程中以前的指令产生的异常被发现,该异常情况仅仅被简单的忽略掉.这样,以上的问题就被解决掉了.—该情况很有可能在中断处理返回后再一次发生.

产生异常的指令的后续指令无效.因为MIPS是流水线CPU,在发现异常时,产生异常的指令的后续指令也被执行了.但是我们可以确认这些产生异常指令之后的指令不产生系统程序员能看得到的后果.处理完异常情况后,程序会从EPC继续执行,就象从来没产生过异常一样.

在一些情况下,MIPS的异常不是精确异常.比如,整数相乘并不响应异常(注解).这个问题可以通过安排指令顺序被解决.指令顺序在汇编语言中被强制规定.

MIPS的精确中断代价很大.应为它限制了可以流水线执行的可能性.这种限制对于浮点运算尤为严重,因为浮点运算需要很多级才能完成.只有系统确认不会产生异常是才会让浮点指令进入算术逻辑单元(ALU).
 

第三节 中断向量,软件中断处理开始的地方

绝大多数的CISC处理器有专门的硬件或不为人知的微指令分析异常,根据异常产生的不同原因让CPU
进入不同的入口地址。MIPS处理器在这方面作的很少。如果你认为这是一个严重不足的话,请看下面分析。

首先,具有中断向量表的中断处理机制不如我们所希望的那样有用。在绝大多数操作系统中,中断处理程序共享代码(比如在中断处理入口存储寄存器的值等等)。在CISC的CPU中我们常看到CISC硬件或微指令把CPU分派到不同的入口地址,操作系统记住中断原因后再跳转到同一地址。

其次,如果不用微指令,很难想象硬件能做多少复杂的工作。在 RISC机器上,指令速度足够快,完全可以作为我们的首选。

再这里和其他地方,我们应该记住RISC处理器比外部设备快得多。通常中断处理程序会读外部寄存器的值,而对于90年代的CPU而言,外部总线的时钟周期(即读外部寄存器的值所花的时间-译者)大致是处理器时钟周期的20到50倍。所以很容易写一端由外部寄存器的值决定处理器执行什么中断处理程序的代码,在这段代码中,读外部寄存器的值花主要时间。因此,软件处理不会成为性能的瓶颈。

即使是MIPS处理器,也不是所有的中断都是同等对待的。随着处理器体系结构的进步,差别将会拉大。列举如下:

用户地址TLB( translate look-aside buffer)填充。在被保护的操作系统中,有一个经常发生的异常,它和地址映射有关(见第六章)。TLB硬件只保存有有限的虚拟地址和物理地址的对照表,因此在使用虚拟地址的操作系统中,很复杂的程序所用的虚拟地址有可能不在TLB中。我们把这个事件称为TLB miss(因为TLB是一种有软件管理的缓冲)。

使用软件来管理这种情况在精简指令处理器刚被推出时是有争议的。MIPS处理器为软件管理TLB miss提供了有力的支持。在这种异常处理中,硬件提供了足够的支持所以软件只需要13个时钟周期就足够了。

这种经常有用到的程序被提供了特殊的入口地址,所以它能被优化的很好,不必判断到底是哪种异常发生了(Amdal’s law)。

64位地址的TLB填充。在64位的处理器上,对于想充分利用64位地址空间的程序,它们的地址映射会用到稍微有些不同的寄存器安排和TLB填充程序。MIPS把它们称XTLB。这个设计同样是为了使程序运行更有效。

不在缓存中的中断处理程序入口点。为了让异常处理更有效,中断处理程序的入口点应该在缓存中。但在系统启动的时候,这几乎是不可能的。如果你想有一个健壮的自较验的启动序列你必需在启动时使用未被缓存的,只读的中断入口点。在MIPS处理器中没有不缓存模式,只有不能被缓存的内存区域,所以我们应该使用SR(BEV)位是中断入口点被冲定位到未被缓存的,启动安全的kseg1区域。

数据校验错。R4000和以后的处理器都检测数据错误并在检测到数据错误时产生一个陷入。数据错通常在数据从内存中读出的时候发生,在数据从缓存中被读出的时候被检测到。在数据出错时,用被缓存的入口地址显然是不明智的。所以此时无论SR(BEV)位被设置与否,都应该用为被缓存的入口地址。

系统重置。从很多方面来说,把系统重置看作一种异常都是有道理的。特别是在考虑到R4000及以后的处理器用同一个中断入口地址处理冷启动和热启动的时候。事实上,不可屏蔽中断应该被看作是一个较弱的热启动,和热启动相比,它的唯一区别就是,必须在当前指令被完成之后才生效。

所有异常的入口地址都在MIPS内存映象的未被映射的区域。如果入口地址是未被缓存的,他就位于kseg1中。如果入口地址是被缓存的,他就位于kseg2中。异常处理程序的入口地址见表5.1。如果处理器使用64为地址,则对相应的32位地址作符号扩展:如果32位地址是0×8000,0000,则64位地址是0xffff,ffff,8000,0000.表5.1描述的是32位地址的入口地址。

中断入口地址之间差128个字节,因为MIPS的设计者认为对于基本的中断处理,32条指令应该足够了。这样,我们就省掉了跳转指令,而有不浪费太多的内存。

(2个打分, 平均:4.50 / 5)

雁过留声

Comments are closed.