一个外部ajax超时 中断请求求会在哪些地方被挂起记录

更多相关文档外部中断的检测及其应用_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
外部中断的检测及其应用
阅读已结束,下载本文需要
想免费下载本文?
定制HR最喜欢的简历
下载文档到电脑,同时保存到云知识,更方便管理
加入VIP
还剩14页未读,
定制HR最喜欢的简历
你可能喜欢外部中断有几种触发方式?如何选择?在何种触发方式下,需要在外部设置中断请求触发器?为什么?_百度知道
外部中断有几种触发方式?如何选择?在何种触发方式下,需要在外部设置中断请求触发器?为什么?
我有更好的答案
外部中断中,有两种触发方式,当IT0或IT1为“0”时,为电平触发,为“1”时,为下降沿触发。对于下降沿触发方式,中断请求信号的高、低电平至少各保持一个机器周期;对于低电平触发方式,INT0、INT1引脚上请求中断的低电平应保持到CPU响应中断为止。
采纳率:33%
为您推荐:
其他类似问题
中断请求的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。msp430f149两个中断怎么处理? - MSP430技术论坛 -
中国电子技术论坛 -
最好最受欢迎电子论坛!
后使用快捷导航没有帐号?
msp430f149两个中断怎么处理?
14:49:56  
一个是AD转换的中断,一个是外部中断。我的AD转换要一直开启,但是在外部中断进来之后,AD转化就停止的。我想请问下可以让AD中断一直执行的同时再让外部中断响应吗?
已退回1积分
10:35:41  
你这个是矛盾的。在微小的时间片内,MCU只能执行单一任务,进去A中断时,如果发生B中断,且B中断的优先级高时才会响应B中断,否则B中断会被挂起,直到A中断执行完毕。 至于你想做的两个中断同时进行,至于把AD的采样或者取采样值的时间跨度调整一下。有啥问题,再下方回复我
10:35:59  
忘了说,记得多看书
23:36:58  
响应外部中断时AD转换会停止?不可能。你是说这时不响应AD中断吧?
所以一个重要原则是中断程序要尽量短,不必要的操作都移到中断外面去做,让中断尽快返回。
Powered by
供应链服务
版权所有 (C) 深圳华强聚丰电子科技有限公司博客访问: 231229
博文数量: 101
博客积分: 2500
博客等级: 少校
技术积分: 991
注册时间:
分类: LINUX 23:02:10
80386除了保持的相关功能外,还增强了中断处理能力,并引入了“异常”的概念。本文将介绍80386中断和异常的机制。
80386的中断和异常
把中断分为内部中断和外部中断两大类。为了支持多任务和虚拟存储器等功能,80386把外部中断称为“中断”,把内部中断称为“异常”。与一样,80386通常在两条指令之间响应中断或异常。80386最多处理256种中断或异常。
对80386而言,中断是由异步的外部事件引起的。外部事件及中断响应与正执行的指令没有关系。通常,中断用于指示I/O设备的一次操作已完成。与一样,80386有两根引脚INTR和NMI接受外部中断请求信号。INTR接受可屏蔽中断请求。NMI接受不可屏蔽中断请求。在80386中,标志寄存器EFLAGS中的IF标志决定是否屏蔽可屏蔽中断请求。
外部硬件在通过INTR发出中断请求信号的同时,还要向处理器给出一个8位的中断向量。处理器在响应可屏蔽中断请求时,读取这个由外部硬件给出的中断向量号。处理器对这个中断向量号并没有规定。但在具体的微机系统中,系统必须通过软件和硬件的配合设置,使得给出的这个中断向量号不仅与外部中断源对应,而且要避免中断向量号使用冲突情况的出现。可编程中断控制器芯片8259A可配合80386工作,能够根据设置向处理器提供上述中断向量号,还能处理中断请求的优先级。每个8259A芯片可以支持8路中断请求信号,如果使用9个8259A芯片(一个主片,8个从片),就可使80386在单个引脚INTR上接受多达64个中断源的中断请求信号。
处理器不屏蔽来自NMI的中断请求。处理器在响应NMI中断时,不从外部硬件接收中断向量号。与一样,在80386中,不可屏蔽中断所对应的中断向量号固定为2。为了不可屏蔽中断的嵌套,每当接受一个NMI中断,处理器就在内部屏蔽了再次响应NMI,这一屏蔽过程直到执行中断返回指令IRET后才结束。所以,NMI处理程序应以IRET指令结束。
异常是80386在执行指令期间检测到不正常的或非法的条件所引起的。异常与正执行的指令有直接的联系。例如,执行除法指令时,除数等于0。再如,执行指令时发现特权级不正确。当发生这些情况时,指令就不能成功完成。软中断指令“INT n”和“INTO”也归类于异常而不称为中断,这是因为执行这些指令产生异常事件。
80386识别多种不同类别的异常,并赋予每一种类别以不同的中断向量号。异常发生后,处理器就象响应中断那样处理异常。即根据中断向量号,转相应的中断处理程序。把这种中断处理程序称为异常处理程序可能更合适。
根据引起异常的程序是否可被恢复和恢复点不同,把异常进一步分类为故障(Fault)、陷阱(Trap)和中止(Abort)。我们把对应的异常处理程序分别称为故障处理程序、陷阱处理程序和中止处理程序。
故障是在引起异常的指令之前,把异常情况通知给系统的一种异常。80386认为故障是可排除的。当控制转移到故障处理程序时,所保存的断点CS及EIP的值指向引起故障的指令。这样,在故障处理程序把故障排除后,执行IRET返回到引起故障的程序继续执行时,刚才引起故障的指令可重新得到执行。这种重新执行,不需要操作系统软件的额外参与。故障的发现可能在指令开始执行之前,也可能在指令执行期间。如果在指令执行期间检测到故障,那么中止故障指令,并把指令的操作数恢复为指令开始执行之前的值。这可保证故障指令的重新执行得到正确的结果。例如,在一条指令的执行期间,如果发现段不存在,那么停止该指令的执行,并通知系统产生段故障,对应的段故障处理程序可通过加载该段的方法来排除故障,之后,原指令就可成功执行,至少不再发生段不存在的故障。
陷阱是在引起异常的指令之后,把异常情况通知给系统的一种异常。当控制转移到异常处理程序时,所保存的断点CS及EIP的值指向引起陷阱的指令的下一条要执行的指令。下一条要执行的指令,不一定就是下一条指令。因此,陷阱处理程序并不是总能根据保存的断点,反推确定出产生异常的指令。在转入陷阱处理程序时,引起陷阱的指令应正常完成,它有可能改变了寄存器或存储单元。软中断指令、单步异常是陷阱的例子。
中止是在系统出现严重情况时,通知系统的一种异常。引起中止的指令是无法确定的。产生中止时,正执行的程序不能被恢复执行。系统接收中止后,处理程序要重新建立各种系统表格,并可能重新启动操作系统。硬件故障和系统表中出现非法值或不一致的值是中止的例子。
在一条指令执行期间,入检测到不只一个中断或异常,那么按下表所列优先级通知系统。把优先级最高的中断或异常通知系统,其它优先级较低的异常被废弃,而优先级较高的中断则保持悬挂。
80386响应中断/异常的优先级
中断/异常类型
陷阱指令INT n和INTO
象中断分为多种类型一样,异常也可分为多种类型。
1.80386识别的异常
80386识别的多种不同类别的异常及赋予的对应中断向量号如下表所示。某些异常还以出错码的形式提供一些附加信息传递给异常处理程序,出错代码列中的“无”表示没有出错代码,“有”表示有出错代码。
异常一览表
单字节INT3
非法操作码
非法指令编码或操作数
设备不可用
浮点指令或WAIT
协处理器段越界
访问存储器的浮点指令
无效TSS异常
JMP、CALL、IRET或中断
装载段寄存器的指令
堆栈段异常
装载SS寄存器的任何指令、对SS寻址的段访问的任何指令
通用保护异常
任何特权指令、任何访问存储器的指令
任何访问存储器的指令
协处理器出错
浮点指令或WAIT
由上表可见,保护模式下的某些中断向量号的分配与实模式的中断向量号发生了冲突。实模式下的中断向量号的分配基于PC微机系统的 CPU,上表中的中断向量号的分配是80386所规定的。实际上,Intel在宣布时,保留了这些发生冲突的中断向量号。尽管发生这样的冲突,但以80386为CPU的微机系统仍可保持与以为CPU的微机系统的兼容,原因是在80386的实模式下,几乎不发生那些中断向量号与外部硬件中断请求时所提供的中断向量号存在冲突的异常。需要注意的是,在保护模式下必须重新设置8259A中断控制器,以产生不与异常相冲突的硬件中断向量。
2.故障类异常
当发生故障,控制转移到故障处理程序时,所保存的断点CS及EIP的值指向引起故障的指令,以便在排除故障后恢复执行。
(1)除法出错故障(异常0)
除法出错是一种故障。当执行DIV指令或IDIV指令时,如果除数等于0或者商太大,以至于存放商的操作数容纳不下,那么就产生这一故障。除法出错故障不提供出错码。
(2)边界检查故障(异常5)
如果BOUND指令发现被测试的值超过了指令中给定的范围,那么就发生边界检查故障。边界检查故障不提供出错码。
(3)非法操作码故障(异常6)
如果80386不能把CS和EIP所指向存储单元的位模式识别为某条指令的部分,那么就发生非法操作码故障。当出现如下情况时,发生这样的故障:(1)操作码字段的内容不是一个合法的80386指令代码;(2)要求使用存储器操作数的场合,使用了寄存器操作数;(3)不能被加锁的指令前使用了LOCK前缀。非法操作码故障不提供操作码。
(4)设备不可用故障(异常7)
设备不可用故障支持80387数字协处理器。在没有80387协处理器硬件的系统中,可用该异常的处理程序代替协处理器的软件模拟器。在发生任务切换时,使得只有在新任务使用浮点指令时,才进行80387寄存器状态的切换。设备不可用故障不提供出错码。该故障在下列情况下产生:(1)在执行浮点指令时,控制寄存器CR0中的EM位或TS位为1;(2)在执行WAIT指令时,控制寄存器CR0中TS位及EM位都为1。需要注意的是,本异常的处理程序必须是一个过程而不能是任务,否则当处理程序发布一条IRET指令时,80386就设置TS位。然后协处理器再次执行这个发生故障的指令,发现TS是置位的,因此就再次发生异常7,结果是无休止的循环。处理程序能通过陷阱门被调用,因为执行期间可以允许中断。
(5)无效TSS故障(异常0AH)
当正从任务状态段TSS装入选择子时,如果发生了除了段不存在故障以外的段异常时,就发生无效TSS故障。在进入故障处理程序时,保存的CS及EIP指向发生故障的指令;或者该故障作为任务切换的一部分发生时,指向任务的第一条指令。
无效TSS故障提供了一个出错码,出错码的格式如下图所示。其中选择子部分是指向引起故障的TSS的选择子。16位的出错代码的主要成分是选择子,指向引起故障的TSS的选择子。高13位是选择子的索引部分,TI位是描述符表指示位。
出错代码的格式
BIT15—BIT3
选择子的索引部分
上图所示出错码格式是异常时出错码的一般格式。从图中可见出错码中不含选择子的RPL,而由IDT位和EXT位代替。当处理某一异常或外部中断时,又发生了某种异常,那么EXT位置1。当从中断描述符表IDT中读出表项并产生异常时,IDT位置1,这只在中断或异常的处理期间才会发生。当没有选择子时,构成出错码选择子部分的值为0。
一些引起无效TSS故障的原因如下:
TSS描述符中的段限长小于103;
无效的LDT描述符,或者LDT未出现;
堆栈段不是一个可写段;
堆栈段选择子索引的描述符超出描述符表界限;
堆栈段DPL与新的CPL不匹配;
堆栈段选择子的RPL不等于CPL;
代码段选择子索引的描述符超出描述符表界限;
代码段选择子不指向代码段;
非一致代码段的DPL不等于新的CPL;
一致代码段的DPL大于新的CPL;
对应DS、ES、FS或GS的选择子指向一个不可读段(如系统段);
对应DS、ES、FS或GS的选择子索引的描述符超出描述符表的界限。
(6)段不存在故障(异常0BH)
处理器在把描述符装入非SS段寄存器的高速缓冲时,如果发现描述符其它方面有效,而P位为0(表示对应段不存在),那么在引用此描述符时就发生段不存在故障。有关SS段的情形纳入堆栈段故障。在进入故障处理程序时,保存的CS及EIP执行发生故障的指令;或者该故障作为任务切换的一部分发生时,指向任务的第一条指令。
段不存在故障提供了一个包含引起该故障的段选择子的出错代码。出错码的格式如上图所示。选择子索引部分为引起段不存在故障的段描述符选择子的索引。
(7)堆栈段故障(异常0CH)
当处理器检测到用SS寄存器进行寻址的段有关的某种问题时,就发生堆栈段故障。在进入故障处理程序时,保存的CS及EIP指向发生故障的指令;或者该故障作为任务切换的一部分发生时,指向任务的第一条指令。堆栈段故障提供一个出错码,出错码的格式也如上图。
具体地说,当出现下列三种情况时,将引起堆栈段故障:
(1)在堆栈操作时,偏移超出段界限所规定的范围。这种情况下的出错码是0。例如PUSH操作时,堆栈溢出。
(2)在由特权级变换所引起的对内层堆栈的操作时,偏移超出段界限所规定的范围。这种情况下的出错码包含有内层堆栈的选择子。
(3)装入到SS寄存器(高速缓冲寄存器)的描述符中的存在位为0。这种情况下的出错码包含有对应的选择子。
上述第一种情况是容易辨别的。第二和第三种情况的辨别要通过判断出错代码所含选择子所指示的描述符中的存在位进行。如果存在位为1,那么是第二种情况;否则是第三种情况。
(8)通用保护故障(异常0DH)
除了明确列出的段异常外,其它的段异常都被视为通用保护故障。在进入故障处理程序时,保存的CS及EIP指向发生故障的指令;或者该故障作为任务切换的一部分发生时,指向任务的第一条指令。通用保护故障提供一个出错码,出错码的格式也如上图所示。
根据处理程序可能作出的响应,通用保护故障可分为如下两类:
(1)违反保护方式,但程序无须中止的异常。这类故障提供的出错码为0。这种异常在应用程序执行特权指令或I/O访问时发生,支持虚拟8086程序的系统或支持虚拟I/O访问的系统需要模拟这些指令,并在模拟完成产生故障的指令后,重新执行被中断的程序。
(2)违反保护方式,并导致程序终止的异常。这类故障提供的出错码可能为0,也可能不为0(能确定选择子时)。引起这类故障的一些原因如下:
向某个只读数据段或代码段写;
从某个只能执行的代码段读出;
将某个系统段描述符装入到数据段寄存器DS、ES、FS、GS或SS;
将控制转移到一个不可执行的段;
在通过段寄存器CS、DS、ES、FS或GS访问内存时,偏移超出段界限;
当访问某个描述符表时,超过描述符表段界限;
把PG位为1但PE位为0的控制信息装入到CR0寄存器;
切换到一个正忙的任务。
对上述两类通用保护故障的辨别,可通过检查引起故障的指令和出错码进行。如果出错码非0,那么肯定是第二类通用保护故障。如果出错码是0,那么需要进一步检查引起故障的指令,以确定它是否是系统支持的可以模拟的指令。
(9)页故障(异常0EH)
关于页故障的详细说明请见后面的文章。
(10)协处理器出错(异常10H)
协处理器出错故障指示协处理器发生了未被屏蔽的数字错误,如上溢或下溢。在引起故障的浮点指令之后的下一条浮点指令或WAIT指令,把协处理器出错作为一个故障通知给系统。协处理器出错故障不提供出错码。
3.陷阱类异常
(1)调试陷阱(异常1)
调试异常有故障类型,也有陷阱类型。调试程序可以访问调试寄存器DR6,以确定调试异常的原因和类型。调试异常不提供出错码。
(2)单字节INT3(异常3)
INT3是一条特别的单字节“INT n”指令。调试程序可利用该指令支持程序断点。INT3指令被看成是一种陷阱,而不是一个中断。当由于执行INT3指令进入异常3处理程序时,被保存的CS和EIP指向紧跟INT3的指令,即INT3后面的字节。INT3陷阱不提供出错码。
(3)溢出(异常4)
INTO指令提供条件陷阱。如果OF标志为1,那么INTO指令产生陷阱;否则不产生陷阱,继续执行INTO后面的指令。在进入溢出处理程序时,被保存的CS和EIP指向INTO指令的下一条指令。溢出陷阱不提供出错码。
4.中止类异常
(1)双重故障异常(异常8)
当系统正在处理一个异常时,如果又检测到一个异常,处理器试图向系统通知一个双重故障,而不是通知第二个异常。双重故障属于中止类异常,所以在转入双重故障处理程序时,被保存的CS和EIP可能不指向引起双重故障的指令,而且指令的重新启动不支持双重故障。双重故障提供的出错码为0。
当正处理一个段故障异常时,有可能又产生一个页故障。在这种情况下,通知给系统的是一个页故障异常而不是双重故障异常。但是,如果正处理一个段故障或页故障时,又一个段故障被检测到;或者如果正处理一个页故障时,又一个页故障被检测到,那么就引起双重故障。
当正处理一个双重故障时,又一个段或页故障被检测到,那么处理器暂停执行指令,并进入关机方式。关机方式类似于处理器指令一条HLT指令后的状态:处理器空转,并维持到处理器接收到一个NMI中断请求或者被重新启动为止。在关机方式下,处理器不响应INTR中断请求。
双重故障通常指示系统表出现严重的问题,例如段描述符表、页表或中断描述符表出现问题。双重故障处理程序在重建系统表后,可能不得不重新启动操作系统。
(2)协处理器段越界(异常9)
协处理器段越界异常属于中止类异常,这是因为引起该异常的指令不能被重新启动。当浮点指令操作数超出段界限时,产生该中止异常。协处理器段越界异常不提供出错码。在异常处理程序入口保存的CS及EIP指向被中止的指令。这种中止不是系统的中止,而是只影响检测到这种异常时正执行的指令所在的程序。
中断和异常的转移方法
80386实模式下的中断和异常的转移方法与8086相同。这里介绍的中断和异常的转移方法是指 80386在保护模式下响应中断和处理异常时所采用的转移方法。
1.中断描述符表IDT
与一样,在响应中断或者处理异常时,80386根据中断向量号转对应的处理程序。但是,在保护模式下,80386不使用实模式下的中断向量表,而是使用中断描述符表IDT。在保护模式下,80386把中断向量号作为中断描述符表IDT中描述符的索引,而不再是中断向量表中的中断向量的索引。
象全局描述符表GDT一样,在整个系统中,中断描述符表IDT只有一个。中断描述符表寄存器IDTR指示IDT在内存中的位置。由于80386只识别256个中断向量号,所以IDT最大长度是2K。
中断描述符表IDT所含的描述符只能是中断门、陷阱门和任务门。也就是说,在保护模式下,80386只有通过中断门、陷阱门或任务门才能转移到对应的中断或异常处理程序。
从前文中给出的门描述符的格式可见,门描述符包含由选择子和偏移构成的48位全指针。另外,双字计数字段对中断门、陷阱门和任务门而言无意义。
2.中断响应和异常处理的步骤
由硬件自动实现的中断响应和异常处理的步骤如下:
首先,判断中断向量号要索引的门描述符是否超出IDT的界限。若超出界限,就引起通用保护故障,出错码为中断向量号乘8再加2。
其次,从IDT中取得对应的门描述符,分解出选择子、偏移量和描述符属性类型,并进行有关检查。描述符只能是任务门、286中断门、286陷阱门、386中断门或386陷阱门,否则就引起通用保护故障,出错码是中断向量号乘8再加2。如果是由INT n指令或INTO指令引起转移,还要检查中断门、陷阱门或任务门描述符中的DPL是否满足CPL<=DPL(对于其它的异常或中断,门中的DPL被忽略)。这种检查可以避免应用程序执行INT n指令时,使用分配给各种设备用的中断向量号。如果检查不通过,就引起通用保护故障,出错码是中断向量号乘8再加2。门描述符中的P位必须是1,表示门描述符是一个有效项,否则就引起段不存在故障,出错码是中断向量号乘8再加2。
最后,根据门描述符类型,分情况转入中断或异常处理程序。
对于异常处理,在开始上述步骤之前,还要根据异常类型确定返回点;如果有出错代码,则形成符合出错码格式的出错码,并在实际执行异常处理程序之前把出错码压入堆栈。为了保证栈的双字边界对齐,16位的出错码以32位的值压入,其中高16位的值未作定义,对于16位段也是如此。
3.通过中断门或陷阱门的转移
如果中断向量号所指示的门描述符是386中断门或386陷阱门,那么控制转移到当前任务的一个处理程序过程,并且可以变换特权级。与其它调用门的CALL指令一样,从中断门和陷阱门中获取指向处理程序的48位全指针。其中16位选择子是对应处理程序或代码段的选择子,它指示全局描述符表GDT或局部描述符表LDT中的代码段描述符;32位偏移指示处理程序入口点在代码段内的偏移量。
通过中断门或陷阱门的转移过程如下所示,该过程由硬件自动进行。
(1)若选择子为空,则产生通用保护故障;
(2)取对应的描述符;
(3)若非存储段描述符,则产生通用保护故障;
(4)若非一致代码段且DPL<CPL且段存在,则切换到内层堆栈;
(5)调整RPL=0;
(6)把描述符装入CS;
(7)若入口偏移越界,则产生通用保护故障;
(8)EFLAGS压入堆栈;
(9)CS压入堆栈;
(10)EIP压入堆栈;
(11)使TF=0,NT=0;
(12)若为中断门,则使IF=0;
(13)若有出错码,则把出错码压入堆栈;
(14)转入处理程序。
由上述转移过程可见,中断门或陷阱门中指示处理程序的选择子必须指向描述一个可执行的代码段的描述符。如果选择子为空,就引起通用保护故障,出错码是0。如果描述符不是代码段描述符,就引起通用保护故障,出错码含选择子。
中断或异常可以转移到同一特权级或内层特权级。上述指定处理程序代码段的描述符中的类型及DPL字段,决定了这种同一任务内的转移是否要发生特权级变换。如果是一个非一致代码段,并且DPLCPL则产生通用保护异常。
上述转移过程中的第六步,也就是“把描述符装入CS”,是指把上述指定处理程序段的描述符装入CS的高速缓冲寄存器中,在这一步骤中要对描述符进行类似通过调用门进行转移的其它检查,包括是否代码段描述符和代码段描述符是否存在等,因此可能再发生异常。在对该描述符进行检查时,通过调整门中选择子的RPL=0(在处理器内部调整,而不影响存储器中的选择子的RPL字段)的方法,实现只考虑代码段的DPL,而不考虑门中选择子的RPL。把描述符装入CS之后,还要检查门描述符中给出的表示处理程序代码段入口的偏移是否越界,即是否超出段界限。如果越界,就引起出错码为0的通用保护故障。
从转移过程还可以看出,把标志寄存器和断点压入堆栈的做法和顺序与实模式是相同的,但这里每一次堆栈操作是一个双字,CS被扩展成32位。在16位段中亦是如此。
把TF置成0,表示不允许处理程序单步执行。把NT置成0,表示处理程序在利用中断返回指令IRET返回时,返回到同一任务而不是一个嵌套任务。需要注意的是,任何特权级的程序都可改变NT位,这样可以利用中断或陷阱处理程序完成任务切换。
通过中断门的转移和通过陷阱门的转移之间的差别只是对IF标志的处理。对于中断门,在转移过程中把IF置为0,使得在处理程序执行期间屏蔽掉INTR中断(当然,在中断处理程序中可以人为设置IF标志打开中断,以使得在处理程序执行期间允许响应可屏蔽中断);对于陷阱门,在转移过程中保持IF位不变,即如果IF位原来是1,那么通过陷阱门转移到处理程序之后仍允许INTR中断。因此,中断门最适宜于处理中断,而陷阱门适宜于处理异常。
在有出错码的情况下,转入处理程序之前还要把出错码压入堆栈。只有异常处理才可能有出错码。下图给出了通过中断门或陷阱门转移时的堆栈情况。(a)是没有变换特权级和没有出错码的情形;(b)是没有变换特权级有出错码的情形;(c)是变换特权级和没有出错码的内层堆栈的情形。(d)是变换特权级和有出错码的内层堆栈情形。注意图中每一项为双字。
4.通过任务门的转移
如果中断向量号所只是的门描述符是任务门描述符,那么控制转移到一个作为独立的任务方式出现的处理程序。任务门中的选择子是指向描述对应处理程序任务的TSS段的选择子,即该选择子指示一个可用的286TSS或386TSS。通过任务门的转移与通过任务门到一个可用的386TSS的段间调用指令CALL的转移很相似,主要的区别是,对于提供出错码的异常处理,在完成任务切换之后,把出错码压入新任务的堆栈中(通过任务门进行转移时,返回地址和外层栈指针不压入新任务的堆栈)。
通过任务门的转移,在进入中断或异常处理程序时,标志寄存器EFLAGS中的NT位被置为1,表示是嵌套任务,则IRET指令返回时,沿TSS中的链接字段返回到最后一个被挂起的任务。
在响应中断或处理异常时,使用任务门可提供一个处理程序任务的自动调度。这种任务调度由硬件直接执行,并且越过包含在操作系统中的软件任务切换,这就为处理程序提供了一个快速的任务切换。
5.转移方法的比较
对中断的响应和异常的处理,80386允许通过使用中断门或陷阱门实现由当前任务之内的一个过程进行处理;也允许通过使用任务门实现由另一个任务进行处理。在当前任务之内的处理程序较为简单,并可以很快地转移到处理程序,但处理程序要负责保存及恢复处理器的寄存器等内容。转到不同任务的处理程序要花费较长时间,保存及恢复处理器寄存器内容的开销作为任务切换的一部分。使用当前任务内的处理程序的方法,在响应中断或处理异常时,对正执行任务的状态可直接进行访问,但是这就要求每一个任务之内都包含一个处理程序。使用独立任务的处理方法,使处理程序得到较好的隔离,但在响应中断或处理异常时,对原任务状态的访问变得较为复杂。需要注意得是,有些异常必须由中断门或陷阱门进行处理,如上面提到得异常7。
无效TSS异常必须使用任务门进行处理,以保证处理程序有一个有效得任务环境。其它得异常通常在任务环境之内进行处理。在任务内,异常被检测并且不必屏蔽中断,所以,所以使用陷阱门。由陷阱门指示的异常处理程序是一个由所有任务共享的过程,所以该处理程序最好置于全局地址空间之内。如果各个任务要求有不同的处理程序,那么全局异常处理程序可保存一个各处理程序的入口表,并为引起异常的任务调用相应的处理程序。
中断通常与正执行的任务没有关系,并可能从使用任务门提供的隔离中获得好处。要求较快响应的中断,通过中断门可以得到较好的处理。因为中断随时都可能发生,所以,通过中断门访问的中断处理程序,必须置于全局地址空间中,以便对所有的任务都有效。还需强调的是,80386程序绝不能调用一个低特权级的过程,当处理器调用一个中断或异常处理程序时,它实施相同的规则,所以,这样一个过程必须至少具有与在其任务上下文中被调用的、由任务所执行的最高特权级的过程相同的特权级。因此,在使用中断门时,中断处理程序通常必须被安排在特权级0,否则若正在特权级0执行时发生中断,则不能进入中断处理程序,而会引起通用保护故障(中断处理程序使用任务门时除外,因为任务切换可以从任何特权级切换到目标任务的任何特权级)。
6.中断或异常处理后的返回
中断返回指令IRET用于从中断或异常处理程序的返回。该指令的执行根据任务嵌套标志NT位是否为1分为两种情形。
NT位为1,表示是嵌套任务的返回。当前TSS中的链接字段保存有前一任务的TSS的选择子,取出该选择子进行任务切换就完成了返回。这种情形在由通过任务门转入的中断或异常处理程序返回时出现,因为在由中断门或陷阱门转入处理程序时,NT位已被清0。
NT位为0,表示当前任务内的返回。这种情形在由通过中断门或陷阱门转入的中断或异常处理程序返回时出现。具体进行的操作包括:从堆栈顶弹出返回指针EIP及CS,然后弹出EFLAGS值。弹出的CS选择子的RPL字段,确定返回后的特权级。如果返回选择子的RPL与CPL相同,则不进行特权级改变。若RPL规定了一个外层特权级,则需要特权级改变,从内层堆栈中弹出外层堆栈的指针ESP及SS的值。这些做法与RET指令相似。例如,使用CS选择子的RPL,而不是由选择子标识的段的DPL,是为了返回到不在DPL给定特权级的一致代码段。若弹出的CS的选择子的RPL规定了一个内层特权级,则产生通用保护故障。需要注意的是,对于IRET指令,保存在当前堆栈中的返回地址中的选择子字段必须指向代码段描述符。而不能是系统段或门描述符。否则将引起通用保护故障。
对于提供出错代码的异常处理程序,必须先人为地从堆栈中弹出出错代码,然后再执行IRET指令,及出错代码不会自动被处理器弹出或取消。
中断返回指令IRET不仅能够用于由中断/异常引起的嵌套任务的返回,而且也适用于由段间调用指令CALL通过任务门引起的嵌套任务的返回,如前文所述,在执行通过任务门进行任务切换的段间调用指令CALL时,标志寄存器中的NT位被置为1,表示任务嵌套。而RET指令不能实现此功能。
演示中断处理的实例(实例六)
下面给出一个用于演示中断处理的实例。该实例的逻辑功能是,在屏幕的左上角以倒计时方式显示秒为单位的时间,在时间用完后结束。该实例演示内容包括:外部中断处理程序和陷阱处理程序。
1.源程序组织和清单
本实例由如下几部分组成:
(1)全局描述符表GDT。GDT中除了含有常见的几个描述符外,还含有描述时钟中断处理程序所使用的代码段和数据段描述符,以及描述显示程序所使用的代码段和数据段描述符。
(2)中断描述符表IDT。为了在保护模式下响应中断和处理异常,必须有IDT。IDT含有256个门描述符。8号安排的是一个通向时钟中断处理程序的中断门,0FEH号安排的是通向显示处理程序的陷阱门,其它均安排成通向其它中断或异常处理程序的陷阱门。
(3)时钟中断处理程序的代码段和数据段。
(4)实现直接写显示缓冲区进行显示的程序代码段和数据段。
(5)处理其它中断或异常的处理程序的代码段。
(6)演示程序的代码段、数据段和堆栈段。
(7)实模式下执行的启动和结束程序代码段和数据段。
源程序清单如下:
386SCD.INC
SEGMENT PARA USE16
<0ffffh,,,ATDW,,>
<0ffffh,8000h,0bh,ATDW,,>
<0ffffh,TempCodeSeg,,ATCE,,>
<DemoCodeLen-1,DemoCodeSeg,,ATCE,,>
<DemoDataLen-1,DemoDataSeg,,ATDW,,>
<DemoStackLen-1,DemoStackSeg,,ATDWA,,>
<EchoCodeLen-1,EchoCodeSeg,,ATCE,,>
<EchoDataLen-1,EchoDataSeg,,ATDW,,>
<TICodeLen-1,TICodeSeg,,ATCE,,>
<TIDataLen-1,TIDataSeg,,ATDW,,>
<OtherCodeLen-1,OtherCodeSeg,,ATCE,,>
($-EFFGDT)/(SIZE Desc)
Normal_Sel
Normal-GDT
VideoBuf-GDT
TempCode_Sel
TempCode-GDT
DemoCode_Sel
DemoCode-GDT
DemoData_Sel
DemoData-GDT
DemoStack_Sel
DemoStack-GDT
EchoCode_Sel
EchoCode-GDT
EchoData_Sel
EchoData-GDT
TICode_Sel
TICode-GDT
TIData_Sel
TIData-GDT
SEGMENT PARA USE16
<OtherBegin,Other_Sel,,AT386TGate,>
<TIBegin,TICode_Sel,,AT386IGate,>
<OtherBegin,Other_Sel,,AT386TGate,>
<EchoBegin,EchoCode_Sel,,AT386TGate,>
<OtherBegin,Other_Sel,,AT386TGate,>
OtherCodeSeg
SEGMENT PARA USE16
CS:OtherCodeSeg
OtherBegin
ax,Video_Sel
WORD PTR es:[0],ax
OtherBegin
OtherCodeLen
OtherCodeSeg
SEGMENT PARA USE16
SEGMENT PARA USE16
CS:TICodeSeg,DS:TIDataSeg
ax,TIData_Sel
ax,EchoData_Sel
ax,DemoData_Sel
BYTE PTR fs:Mess,'0'
BYTE PTR gs:Flag,1
BYTE PTR fs:Mess
EchoDataSeg
SEGMENT PARA USE16
EchoDataLen
EchoDataSeg
EchoCodeSeg
SEGMENT PARA USE16
CS:EchoCodeSeg,DS:EchoDataSeg
ax,EchoData_Sel
ax,Video_Sel
ax,WORD PTR Mess
WORD PTR es:[0],ax
EchoCodeLen
EchoCodeSeg
DemoStackSeg
SEGMENT PARA USE16
DemoStackLen
DemoStackLen DUP(0)
DemoStackSeg
DemoDataSeg
SEGMENT PARA USE16
DemoDataLen
DemoDataSeg
DemoCodeSeg
SEGMENT PARA USE16
CS:DemoCodeSeg,DS:DemoDataSeg
ax,DemoStack_Sel
sp,DemoStackLen
ax,DemoData_Sel
DemoConti:
BYTE PTR Flag,0
TempCode_Sel,<OFFSET ToDos>
DemoCodeLen
DemoCodeSeg
TempCodeSeg
SEGMENT PARA USE16
CS:TempCodeSeg
DemoCode_Sel,DemoBegin
ax,Normal_Sel
<SEG Real>,<OFFSET Real>
TempCodeSeg
SEGMENT PARA USE16
<GDTLen-1,>
<IDTLen-1,>
SEGMENT PARA USE16
CS:RCodeSeg,DS:RDataSeg
ax,RDataSeg
QWORD PTR NORVIDTR
BYTE PTR IMaskRegV,al
QWORD PTR VGDTR
QWORD PTR VIDTR
<TempCode_Sel>,<OFFSET Virtual>
ax,RDataSeg
sp,DWORD PTR SPVar
QWORD PTR NORVIDTR
al,IMaskRegV
si,OFFSET EFFGDT
ax,[si].BaseL
edx,eax,16
WORD PTR [si].BaseL,ax
BYTE PTR [si].BaseM,dl
BYTE PTR [si].BaseH,dh
si,SIZE Desc
WORD PTR VGDTR.Base,ax
WORD PTR VGDTR.Base+2,dx
WORD PTR VIDTR.Base,ax
WORD PTR VIDTR.Base+2,dx
2.关于实例六的说明
(1)时钟中断仍使用8H号中断向量
为了即简单又清楚地演示在保护模式下响应外部中断并进行处理,实例使用了时钟中断源,但没有通过重新设置中断控制器的方法改变对应的中断向量。所以,时钟中断使用的8H号中断向量号就与双重故障异常使用的中断向量号发生冲突。但实例仅是演示程序,所以只要保证不发生双重故障异常,就可避免冲突,从而不会影响演示。
设置中断屏蔽寄存器,仅开放时钟中断。所以,在开中断状态下,也只可能发生时钟中断,而不会发生其它外部中断。
(2)时钟中断处理程序的设计
由于通过中断门转时钟中断处理程序,所以在控制转移时不发生任务切换。但外部中断随时可能发生,因此中断处理程序必须采取保护现场等措施。作为演示程序,该中断处理程序检查和调整在其数据段中的计数器,满18次后,就认为已满一秒,再调整用于显示的倒计数信息。如果倒计数信息为0,那么就设置演示程序数据段中的时间为0标志。该中断处理程序通过约定的数据区与显示程序及演示程序交换信息。
(3)利用一个软中断(陷阱处理)程序实现显示
为了演示陷阱及其处理,把显示过程安排成陷阱处理程序。上述时钟中断处理程序通过软中断调用指令INT调用该显示程序,以显示倒计数。在控制转移时,也没有任务切换。该陷阱处理程序相当于一个“软中断”处理程序,类似实模式下的BIOS中断INT 10H。
(4)对其它中断或异常的响应
为了简单,除了8H号和0FEH号外,IDT中其它的门均通向一个处理程序。该处理程序用于处理其它中断或异常。处理过程也极其简单,在屏幕左上角显示蓝底白字的符号“!”,然后进入无限循环。实际上,按演示程序现在的安排,不可能发生这种情况。
(5)没有特权级变换
为了简单,实例涉及的中断处理程序和异常处理程序都保持特权级0。所以,控制转移时不发生特权级变换。因此,没有使用其它堆栈。
(6)对IDT的初始化
由于IDT中门描述符没有32位段基地址,并且入口点偏移较小,所以就直接填写门描述符结构变量,没有额外再初始化。过程InitIDT只是设置IDT伪描述符。
(7)装载和保存IDTR寄存器
再使IDT发挥作用之前,还要装载中断描述符表寄存器IDTR;但为了回到实模式后,恢复原来的IDTR之内容,所以先保存IDTR的内容。实例使用如下指令保存IDTR:
QWORD PTR NORVIDTR
该指令的功能是把IDTR的内容保存到存储器中的伪描述符NORVIDTR中。该伪描述符的结构如前文所述的结构类型PDESC所示,低字是以字节为单位的界限,高双字是基地址。在后面的文章中将对SIDT指令作详细说明。
本实例使用如下指令装载IDTR寄存器:
QWORD PTR VIDTR
QWORD PTR NORVIDTR
LIDT指令类似于LGDT指令,在后面的文章中将对LIDT指令作详细说明。
演示异常处理的实例(实例七)
下面给出一个用于模拟异常和演示异常处理的实例。该实例的逻辑功能是,在屏幕上显示一条提示用户以按键方式选择异常类型的字符,然后模拟指定的异常。该实例演示内容包括:除法出错故障处理、溢出陷阱处理、段不存在故障处理、堆栈段出错处理和通用保护故障处理;还有作为一个独立任务方式出现的陷阱处理程序。
1.源程序组织和清单
为了演示以独立任务方式出现的陷阱处理程序,实例含有两个任务:演示任务和读键盘任务。实例由如下几部分组成:
(1)全局描述符表GDT和中断描述符表IDT;
(2)读键盘任务局部描述符表、任务状态段、堆栈段和代码段等;
(3)演示任务的局部描述符表、任务状态段、堆栈段、代码段和数据段等;
(4)作为演示任务一部分的有关陷阱处理和故障处理程序的代码段;
(5)作为演示任务一部分的显示出错码过程的代码段;
(6)实模式下执行的启动和结束程序代码段和数据段。
在切换到保护模式后,就进入临时代码段。为了简单,演示任务不发生特权级变换。演示步骤如下:
(1)从临时代码段转移到演示代码段。
(2)做演示准备。把演示任务的LDT选择子装入LDTR,并填入TSS,装载任务寄存器TR,建立演示任务堆栈,设置其它数据段寄存器。
(3)接收要模拟的异常类型号。通过软中断指令INT调用读键盘任务完成该步骤。读键盘任务只有在接收到指定的字符后才结束。接收的字符是0、4、B、C和D。
按接收的字符模拟异常。即根据键入的字符,执行有关程序片段。在这些片段中,有意安排了能引起有关故障或陷阱的指令。
结束演示,转临时代码段,返回DOS。
程序清单如下:
386SCD.INC
SEGMENT PARA USE16
<0ffffh,,,ATDW,,>
Normal_Sel
Normal-GDT
<0ffffh,8000h,0bh,ATDW,,>
VideoBuf_Sel
VideoBuf-GDT
<0ffffh,TempCodeSeg,,ATCE,,>
TempCode_Sel
TempCode-GDT
<DemoCodeLen-1,DemoCodeSeg,,ATCE,,>
DemoCode_Sel
DemoCode-GDT
<DemoLDTLen-1,DemoLDTSeg,,ATLDT,,>
DemoLDT_Sel
DemoLDT-GDT
<DemoTSSLen-1,DemoTSSSeg,,AT386TSS,,>
DemoTSS_Sel
DemoTSS-GDT
<BufferLen-1,BufferSeg,,ATDW,,>
XBuffer_Sel
XBuffer-GDT
<GKeyLDTLen-1,GKeyLDTSeg,,ATLDT,,>
GKeyLDT_Sel
GKeyLDT-GDT
<GKeyTSSLen-1,GKeyTSSSeg,,AT386TSS,,>
GKeyTSS_Sel
GKeyTSS-GDT
<EchoCodeLen-1,EchoCodeSeg,,ATCE,,>
EchoCode_Sel
EchoCode-GDT
<SubCodeLen-1,SubCodeSeg,,ATCE,,>
SubCode_Sel
SubCode-GDT
<OtherCodeLen-1,OtherCodeSeg,,ATCE,,>
($-EFFGDT)/(SIZE Desc)
SEGMENT PARA USE16
<DivBegin,Divide_Sel,,AT386TGate,>
<OtherBegin,Other_Sel,,AT386TGate,>
<OFBegin,OF_Sel,,AT386TGate,>
<OtherBegin,Other_Sel,,AT386TGate,>
<SNPBegin,SNP_Sel,,AT386TGate,>
<SSEBegin,SSE_Sel,,AT386TGate,>
<GPBegin,GP_Sel,,AT386TGate,>
<OtherBegin,Other_Sel,,AT386TGate,>
<EchoBegin,EchoCode_Sel,,AT386TGate,>
<,GKeyTSS_Sel,,ATTaskGate,>
GKeyLDTSeg
SEGMENT PARA USE16
<0ffffh,GKeyCodeSeg,,ATCE,,>
GKeyCode_Sel
GKeyCode-GLDT+TIL
<GKeyStackLen-1,GKeyStackSeg,,ATDWA,,>
GKeyStack_Sel
GKeyStack-GLDT+TIL
($-GLDT)/(SIZE Desc)
GKeyLDTLen
GKeyLDTSeg
GKeyTSSSeg
SEGMENT PARA USE16
GKeyBegin,0
GKeyStackLen,0
Normal_Sel,0
GKeyCode_Sel,0
GKeyStack_Sel,0
Normal_Sel,0
Normal_Sel,0
Normal_Sel,0
GKeyLDT_Sel,0
GKeyTSSLen
GKeyTSSSeg
GKeyStackSeg
SEGMENT PARA USE16
GKeyStackLen
GKeyStackLen DUP(0)
GKeyStackSeg
GKeyCodeSeg
SEGMENT PARA USE16
CS:GKeyCodeSeg,DS:RDataSeg,ES:BufferSeg
ax,Normal_Sel
<SEG GetKey>,<OFFSET GetKey>
ax,RDataSeg
sp,DWORD PTR SPVar
QWORD PTR NORVIDTR
dx,OFFSET Mess
ax,BufferSeg
BYTE PTR es:KeyASCII,dl
QWORD PTR VIDTR
<GKeyCode_Sel>,<OFFSET GetKeyV>
ax,GKeyStack_Sel
GKeyCodeLen
GKeyCodeSeg
OtherCodeSeg
SEGMENT PARA USE16
CS:OtherCodeSeg
OtherBegin
si,OFFSET MessOther
WORD PTR es:[0],ax
OtherBegin
OtherCodeLen
OtherCodeSeg
DivCodeSeg
SEGMENT PARA USE16
CS:DivCodeSeg
si,OFFSET Mess0
DivCodeLen
DivCodeSeg
SEGMENT PARA USE16
CS:OFCodeSeg
si,OFFSET Mess4
SNPCodeSeg
SEGMENT PARA USE16
CS:SNPCodeSeg
si,OFFSET MessB
SubCode_Sel,SubBegin
SNPCodeLen
SNPCodeSeg
SSECodeSeg
SEGMENT PARA USE16
CS:SSECodeSeg
si,OFFSET MessC
SubCode_Sel,SubBegin
SSECodeLen
SSECodeSeg
SEGMENT PARA USE16
CS:GPCodeSeg
si,OFFSET MessD
eax,[bp+4]
SubCode_Sel,SubBegin
DWORD PTR [ebp+8],2
SubCodeSeg
SEGMENT PARA USE16
CS:SubCodeSeG
si,OFFSET ErrCode
si,OFFSET ErrMess
SubCodeLen
SubCodeSeg
EchoCodeSeg
SEGMENT PARA USE16
CS:EchoCodeSeg
EchoCodeLen
EchoCodeSeg
SEGMENT PARA USE16
128 DUP(?)
DemoLDTSeg
SEGMENT PARA USE16
<DemoTSSLen-1,DemoTSSSeg,,ATDW,,>
ToDemoTSS_Sel
ToDemoTSS-DLDT+TIL
<DemoStackLen-1,DemoStackSeg,,ATDWA,,>
DemoStack_Sel
DemoStack-DLDT+TIL
<DemoDataLen-1,DemoDataSeg,,ATDW,,>
DemoData_Sel
DemoData-DLDT+TIL
<DivCodeLen-1,DivCodeSeg,,ATCE,,>
Divide_Sel
Divide-DLDT+TIL
<OFCodeLen-1,OFCodeSeg,,ATCE,,>
OverFlow-DLDT+TIL
<SNPCodeLen-1,SNPCodeSeg,,ATCE,,>
SNPCode-DLDT+TIL
<SSECodeLen-1,SSECodeSeg,,ATCE,,>
SSECode-DLDT+TIL
<GPCodeLen-1,GPCodeSeg,,ATCE,,>
GPCode-DLDT+TIL
<0ffffh,,,ATDW-80h,,>
TestNPS_Sel
TestNPS-DLDT+TIL
($-DLDT)/(SIZE Desc)
DemoLDTLen
DemoLDTSeg
DemoTSSSeg
SEGMENT PARA USE16
DemoTaskSS
DemoTSSLen
DemoTSSSeg
DemoStackSeg
SEGMENT PARA USE16
DemoStackLen
DemoStackLen DUP(0)
DemoStackSeg
DemoDataSeg
SEGMENT PARA USE16
'Divide Error (Exception 0)',0
'Overflow (Exception 4)',0
'Segment Not Present (Exception 11)',0
'Stack Segment (Exception 12)',0
'General Protection (Exception 13)',0
'Other Exception',0
'Error Code = '
4 DUP(0),'H',0
DemoDataLen
DemoDataSeg
DemoCodeSeg
SEGMENT PARA USE16
CS:DemoCodeSeg,DS:DemoDataSeg
ax,DemoLDT_Sel
ax,DemoStack_Sel
esp,DemoStackLen
ax,ToDemoTSS_Sel
WORD PTR gs:DemoTaskSS.TRLDTR,DemoLDT_Sel
ax,DemoTSS_Sel
ax,DemoData_Sel
ax,VideoBuf_Sel
ax,XBuffer_Sel
ax,XBuffer_Sel
al,BYTE PTR fs:KeyASCII
ax,TestNPS_Sel
ax,DemoTSS_Sel
TempCode_Sel,<OFFSET ToDos>
DemoCodeLen
DemoCodeSeg
TempCodeSeg
SEGMENT PARA USE16
CS:TempCodeSeg
DemoCode_Sel,DemoBegin
ax,Normal_Sel
<SEG Real>,<OFFSET Real>
TempCodeSeg
SEGMENT PARA USE16
<GDTLen-1,>
<IDTLen-1,>
'Press a key[0,4,B,C,D]:$'
SEGMENT PARA USE16
CS:RCodeSeg,DS:RDataSeg
ax,RDataSeg
ax,GKeyLDTSeg
cx,GKeyLDNum
si,OFFSET GLDT
ax,DemoLDTSeg
cx,DemoLDNum
si,OFFSET DLDT
QWORD PTR VGDTR
QWORD PTR NORVIDTR
QWORD PTR VIDTR
<TempCode_Sel>,<OFFSET Virtual>
ax,RDataSeg
sp,DWORD PTR SPVar
QWORD PTR NORVIDTR
si,OFFSET EFFGDT
ax,[si].BaseL
edx,eax,16
WORD PTR [si].BaseL,ax
BYTE PTR [si].BaseM,dl
BYTE PTR [si].BaseH,dh
si,SIZE Desc
WORD PTR VGDTR.Base,ax
WORD PTR VGDTR.Base+2,dx
WORD PTR VIDTR.Base,ax
WORD PTR VIDTR.Base+2,dx
ax,WORD PTR FS:[si].BaseL
edx,eax,16
WORD PTR fs:[si].BaseL,ax
BYTE PTR fs:[si].BaseM,dl
BYTE PTR fs:[si].BaseH,dh
si,SIZE Desc
2.关于实例七的说明
上述模拟与演示程序的许多内容与实例六相同,下面就各异常处理程序和读键盘任务的实现作些说明:
(1)除法出错故障处理程序的实现
从源程序可见,除法出错是在执行故意安排的被除数为2000,而除数为2的无符号除指令时引起的。作为演示,除法出错故障处理程序先显示一条提示信息,然后把存放被除数AX的内容右移一位,然后就返回。由于除法出错为故障类异常,所以在故障处理结束后,仍执行该无符号除指令。显然将再次引起同样的故障,仍把被除数右移一位。由于每次处理时都把被除数减半,所以几次故障后就不发生该故障了。
(2)溢出陷阱处理程序的实现
作为演示的溢出陷阱处理程序较简单。先显示一条提示信息,然后就返回。因为溢出异常为陷阱类异常,所以在陷阱处理结束后,就直接返回到引起陷阱指令的下一条指令。
(3)段不存在故障处理程序的实现
从源程序可见,段不存在故障是在执行故意安排的把一个选择子送段寄存器GS的指令时引起的。该选择子索引的描述符中的存在位P被置为0,表示对应段不在内存。在正常情况下,段不存在故障处理程序要把对应的段装入内存,再把描述符内的P位修改为1,于是,在故障处理结束后,引起故障的指令可得到顺序执行。为了简单,这里安排的故障处理程序先显示一条提示信息,然后显示出错码,最后调整堆栈中的返回地址并返回。段不存在故障提供一个出错码,该故障处理程序利用POP指令把它用堆栈中弹出,这样堆栈指针就指向返回地址。由于段不存在异常属于故障类异常,所以返回点仍是引起故障的指令。因此,演示程序调整了堆栈中的返回地址,使其返回到引起故障的指令的下一条指令。
(4)堆栈段出错故障处理程序的实现
引起堆栈出错故障的原因有多种,实例通过执行故意安排的偏移超过段界限的堆栈段访问指令来模拟堆栈段出错故障的产生。作为演示的堆栈出错故障处理程序比较简单,先显示一条信息,然后显示出错码,最后调整堆栈中的返回地址并返回。
(5)通用保护障处理程序的实现
引起通用保护故障处理程序的原因有多种,实例通过把一个指向系统段描述符的选择子装入数据段寄存器GS来模拟通用保护故障的产生。作为演示的通用保护故障处理程序,象上述两个故障处理程序一样比较简单,先显示一条提示信息,然后显示出错码,最后调整堆栈中的返回地址并返回,但在废除堆栈中的出错码和调整堆栈中的返回地址时采用了其它方法。
(6)异常处理程序的一般说明
在实例中,通向上述各种异常处理程序的门都是陷阱门。所以,在发生异常而转入这些异常处理程序时,都不发生任务切换。于是,这些异常处理仍作为演示任务的一部分。
正常情况下,异常处理程序应该注意现场的保护和恢复,但为了简单,作为演示的异常处理程序没有能够切实地保护现场。注意,这些异常处理程序所采用的处理方法与所模拟的指令有关,不适用于一般情况。
(7)显示出错代码的过程
实例采用一个过程用于显示出错代码,该过程的入口参数是AX含出错码。利用该过程不仅缩短程序,而且也用于表现异常处理程序的实现。
(8)读键盘任务的实现
在实例的IDT中,0FFH号门描述符是任务门,指向一个独立的任务。该任务的功能是读键盘,接收一个指定范围内的字符。演示任务通过指令“INT 0FFH”来调用它,接收一个代表需要模拟异常的字符。
为了简单,该任务在实模式下读键盘,接收指定范围内的字符。为此,该任务每次经历如下步骤:(1)转到实模式。此前要作必要的准备,转到实模式后,要恢复必须的实模式下的部分现场。(2)接收指定的字符。调用DOS功能显示提示信息,调用BIOS中断读键盘,如果在指定范围内,那么就显示,并保存在约定的数据段中。(3)转回到保护模式,此前也要作必要的准备。
尽管在任务切换时,自动利用TSS保护和恢复现场,但由于该任务相当于一个读键盘的过程,所以在开始任务时,还通过堆栈保护必要的现场,在结束任务时恢复现场。请特别注意,安排在该任务代码段中的IRETD指令之后的转移指令的作用。当执行IRETD指令时,由于NT位为1,所以按反向链进行任务切换,同时保存各寄存器到当前的TSS,为了下次进入时仍能从头开始执行此任务,所以在IRETD后加一条转移到该任务开头的指令。
各种转移途径小结
如上所述,中断/异常可引起任务切换、任务内特权级变换和任务内无特权级变换的转移。至此,任务切换、任务内特权级变换和任务内无特权级变换转移的各种途径已全部列出。
1.任务切换的途径
任务之间切换的途径如下图所示。段间转移指令JMP、段间调用指令CALL、软中断指令INT和中断返回指令IRET引起的任务切换是主动的任务切换,或者说是当前任务要求的任务切换。中断和异常(不包括软中断指令)引起的任务切换是被动的任务切换,或者说是不受当前任务左右的任务切换。
伴随着任务切换,特权级当然可能发生变换。只要任务切换发生,这种特权级的变换取决于目标任务,而与当前任务无关。
2.任务内特权级变换的途径
任务内特权级变换的途径如下图所示。图中特权级m是外层特权级,特权级n是内层特权级。通常RET与CALL对应;IRET与INT、中断/异常对应。但也可以通过在堆栈中建立合适环境的手段,使RET或IRET从内层特权级变换到外层特权级。
3.任务内相同特权级转移的途径
任务内相同特权级转移的途径如下图所示。由图可见,任务内相同特权级转移的途径多种多样。
书&&&&&&&&名
出&&版&&社
《保护方式下的80386及其编程》
清华大学出版社
周明德主编&&&&
《80X86汇编语言程序设计教程》
清华大学出版社
扬季文主编&&&&
《32位系统软件编程指南》&&&&&
电子工业出版社
程荷、武航翻译
阅读(3226) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~
小跨,有空多来看看^_^
Hi,剛哥,拽啊
请登录后评论。

我要回帖

更多关于 ajax超时 中断请求 的文章

 

随机推荐