中断(Interrupt)和异常(Exception)是指明系统、处理器或当前执行程序(或任务)的某处出现一个事件,该事件需要处理器进行处理。
INT n
指令产生中断。通常中断和异常会导致执行控制被强迫从当前运行程序转移到被称为中断处理程序(interrupt handler)或异常处理程序(exception handler)的特殊软件函数或任务中。处理器响应中断或异常所采取的行动被称为中断/异常服务(处理)。
对应用程序和操作系统来说,80x86的中断和异常处理机制可以透明地处理发生的中断和异常事件。当收到一个中断或检测到一个异常时,处理器会自动地把当前正在执行的程序或任务挂起,并开始运行中断或异常处理程序。当处理程序执行完毕,处理器就会恢复并继续执行被中断的程序或任务。
被中断程序的恢复过程并不会失去程序执行的连贯性,除非异常是不可恢复的或者中断导致当前运行程序被终止。
实模式和保护模式下的中断向量表(Interrupt Vector Table,IVT)是不同的。
实模式下的中断向量表:
在实模式下,中断向量表是一组连续的内存位置,每个位置存储一个4字节的中断向量,用于存放中断服务程序的地址。
0x00000
开始的,依次向上排列,每个中断向量占4个字节,占用从0x00000
到0x0003FF
共1KB的空间。
保护模式下的中断描述符表:
在保护模式下,中断描述符表(Interrupt Descriptor Table,IDT)取代了实模式下的中断向量表,充当引导程序到服务程序初始点执行的指针。
为了帮助处理异常和中断,每个需要被处理器进行特殊处理的异常和中断条件被分配一个唯一的标识号,称为向量(vector)。处理器使用分配给异常或中断的向量作为中断描述符表IDT(Interrupt DescriptorTable)的索引标识,来定位一个异常或中断的处理程序入口点位置。向量数的允许范围是0到255。
处理器从两种地方接收中断:
外部(硬件产生)的中断,通过处理器芯片上引脚或本地APIC接收。当本地APIC为全局/硬件禁用时,这些引脚分别配置为INTR和NMI引脚。
中断向量0-255可通过INTR引脚传递;中断向量16-255那些可以通过本地APIC传递;NMI使用固定的中断向量号2
软件产生的中断,INT n
指令可用于从软件中产生中断
INT n
指令允许通过提供中断向量号作为操作数从软件内部生成中断(n可取0-255)。标志寄存器EFLAGS中的IF标志可用来屏蔽所有这些硬件中断,但不能够屏蔽使用INT指令从软件中产生的中断。
处理器从三个来源接收异常:
INTO
、INT 3
和BOUND
指令可以用来从软件中产生异常。这些指令可对指令流中指定点执行的特殊异常条件进行检查。
INT n
指令可用于在软件中模拟指定的异常,但有一个限制,不能模拟那些会产生错误码的异常。原因是处理程序则会把EIP(正好处于缺少的错误码位置处)弹出堆栈,从而会造成返回位置错误。异常一般出现在当前执行的指令结束后,由CPU主动产生。根据异常被报告的方式以及导致异常的指令是否能够被重新执行,异常可被细分成故障(Fault)、陷阱(Trap)和中止(Abort)。
类别 | 原因 | 异步/同步 | 返回行为 |
---|---|---|---|
中断 | 来自I/O设备或其他硬件设备的信号 | 异步 | 总是返回到下一条指令 |
陷阱 | 有意的异常 | 同步 | 总是返回到下一条指令 |
故障 | 潜在可恢复的错误 | 同步 | 可能返回到当前指令 |
中止 | 不可恢复的错误 | 同步 | 不会返回 |
为了让程序或任务在一个异常或中断处理完之后能重新恢复执行,除了中止之外的所有异常都能报告精确的指令位置,并且所有中断保证是在指令边界上发生。
P6系列处理器推测性执行指令的能力不会影响处理器接收中断。中断发生在指令执行的退出阶段的指令边界;因此,它们总是在“有序”指令流中使用。
标志寄存器EFLAGS的中断允许标志IF(Interrupt enable Flag)和恢复标志RF(Reverse Flag)可用于开启和禁止处理器接收处理中断。
IF标志可以禁用从处理器的INTR引脚或通过本地APIC接收的可屏蔽硬件中断的服务。
但是,IF标志并不影响发送到NMI引脚的非屏蔽中断或通过本地APIC传递的NMI消息,也不影响处理器产生的异常。如同EFLAGS中的其他标志一样,处理器在响应硬件复位操作时也会将IF标志置0。
可屏蔽硬件中断组包括保留中断和异常向量0到32,当IF=1时,
- 0到32的任何向量的中断都可以通过INTR引脚传递给处理器
- 从16到32的任何向量都可以通过本地APIC传递。
而后处理器将生成一个中断,并调用向量号所指向的中断或异常处理程序。
但当通过INTR引脚到异常向量生成中断时,处理器不会将错误代码推送到堆栈上,因此异常处理程序可能无法正确操作。
IF标志可以通过以下方式修改:
STI
和CLI
来设置或清除。而若 C P L > I O P L CPL > IOPL CPL>IOPL时使用这两条指令会引发通用保护异常(#GP)。PUSHF
指令会把EFLAGS内容存入堆栈中,并且可以在那里被修改。而POPF
指令可用于把已被修改过的标志内容放入EFLAGS寄存器中。POPF
和IRET
指令会加载EFLAGS寄存器。因此,它们可用来修改IF标志。EFLAGS寄存器中的RF(恢复)标志控制处理器对指令断点条件的响应,能够防止处理器在指令断点上进入调试异常循环。
为了切换到不同的堆栈段,软件通常使用一对指令,例如:
MOV SS, AX
MOV ESP, StackTop
如果中断或异常发生在段选择器被加载到SS寄存器之后,但在ESP寄存器被加载之前,那么在中断或异常处理程序的持续时间内,进入堆栈空间的逻辑地址的这两个部分是不一致的。
为了防止这种情况,处理器在MOV To SS
指令或POP To SS
指令之后禁止中断、调试异常和单步陷阱异常,直到到达下一条指令之后的指令边界。(但还可能会产生其他故障,因而一般使用LSS
指令修改SS寄存器的内容)
如果在一条指令边界有多个异常或中断等待处理时,处理器会按规定的次序对它们进行处理。
处理器会首先处理最高优先级类中的异常或中断。低优先级的异常会被丢弃,而低优先级的中断则会被挂起。当中断处理程序返回到产生异常和/或中断的程序或任务时,被丢弃的异常会重新发生。
中断描述符表IDT(Interrupt Descriptor Table)将每个异常或中断向量分别与它们的处理过程联系起来。
IDT的基址应该在8字节的边界上对齐,以最大化缓存线填充的性能。界限值以字节表示,并将其添加到基址中以获得最后一个有效字节的地址(极限为 8 N − 1 8N-1 8N−1)。
IDT表可以驻留在线性地址空间的任何地方,处理器使用IDTR寄存器来定位IDT表的位置。寄存器IDTR中含有IDT表32位的基地址和16位的界限值。
使用IDT的基地址和中断向量号,你可以定位到相应的中断描述符。每个中断描述符包含了中断处理程序的地址以及其他相关信息,根据相应的计算方式即可获得中断处理程序的地址。
LIDT
(加载IDT寄存器)和SIDT
(存储IDT寄存器)指令分别加载和存储IDTR寄存器的内容。
LIDT
指令将保存在内存操作数中的基址和限制加载到IDTR寄存器中。
LIDT
指令从一个IDT切换到另一个IDT。SIDT
指令将存储在IDTR中的基值和限制值复制到内存中。
如果一个向量引用的描述符超出了IDT的限制,就会生成一个通用保护异常(#GP)。
IDT可以包含以下三种门描述符:
门描述符用于描述可执行代码,比如:一段程序、一个过程(例程、子程序)或者一个任务,存储段选择子。其格式中的DPL字段指定门的特权级别,而P位表示描述符是否存在。
这些门的不同之处在于处理器在EFLAGS寄存器中处理IF标志的方式。中断门会清除EFLAGS中的IF位,而陷阱门则不会,这使它们成为处理硬件中断的理想选择。陷阱门广泛用于硬件辅助虚拟化。
处理器对异常和中断处理过程的调用操作方法与使用CALL指令调用程序过程和任务的方法类似。当响应一个异常或中断时,处理器使用异常或中断的向量作为IDT表中的索引。
中断门或陷阱门引用在当前执行任务的上下文中运行的异常或中断处理程序。门中的段选择子指向GDT或当前LDT中可执行代码段的段描述符。门描述符的偏移字段指向异常或中断处理过程的开始。
在x86架构中,处理器一般具有4个特权级别(0是最高特权级,3是最低特权级),一般存在于描述符及段选择子中,进行某些操作或被别的对象访问时,可用于控制进行的操作或者限制访问。主要包括以下概念:
在判断中断处理过程与被中断任务的优先级时,主要依赖于中断服务程序和被中断任务的特权级别(CPL、RPL、DPL):
对于一般的中断,控制权会首先转移给中断处理器,CPU进入内核态(CPL=0),中断处理器完成一些基本的辅助工作后,再将控制权转给与已发生的特定中断相关的操作系统例程。
当有多种中断源时,则需要判定响应优先级和处理优先级
中断处理程序的处理方式可能会根据不同的优先级有所不同,特别是在多级中断处理系统中。
同时,在调用中断处理程序时也与当前执行任务的特权级具有关联。
当处理器执行异常处理程序或中断处理程序的调用时:
要从异常处理程序或中断处理程序中返回,处理程序必须使用IRET(或IRETD)
指令。IRET
指令与RET
指令类似,除了它将保存的标志恢复到EFLAGS寄存器中。
如果在调用处理程序过程时发生堆栈切换,则IRET
指令在返回时切换回被中断过程的堆栈。
异常和中断处理过程的特权级保护机制与通过调用门调用普通进程类似。处理器不允许把控制转移到比CPL更低的特权级代码段的中断处理过程中,否则会产生通用保护异常(#GP)。
另外,中断和异常的保护机制在以下方面与一般调用门过程不同:
INT n
、INT 3
或INTO
指令产生时,处理器才检查中断或陷阱门的DPL(CPL必须小于或等于门描述符的DPL)。
由于异常和中断通常不会在可预测的时间发生,因此这些特权规则有效地对运行异常和中断处理过程的特权级别施加了限制。但可以利用以下技术之一来避免违反特权级保护规则:
当通过中断门或陷阱门访问异常或中断处理程序时,处理器会在把EFLAGS寄存器内容保存到堆栈上之后清除EFLAGS中的TF标志。清除TF标志可以防止指令跟踪影响中断响应。随后使用IRET
指令从堆栈中内容恢复EFLAGS的原TF标志。
在调用异常和中断处理程序时,处理器还清除EFLAGS寄存器中的VM、RF和NT标志,然后将它们保存在堆栈上。
中断门与陷阱门唯一的区别在于处理器操作EFLAGS寄存器IF标志的方法。
IRET
指令则会用保存在堆栈上的内容恢复EFLAGS寄存器的IF标志。