中断
外部中断
外部硬件的中断是通过两根信号线通知CPU的,这两根信号线就是INTR(INTeRrupt)和NMI(Non Maskable Interrupt)
- NMI中断,不可屏蔽中断,产生这个中断的时候,表示系统发生了致命的错误。
- INTR可屏蔽中断。
中断程序会分为上半部和下半部,把中断程序需要立即执行的部分划分到上半部分,这部分是要限时执行的。上半部实在关中断不被打扰的情况下执行的。当上半部执行完成后就把中断打开了;下半部也属于中断程序,其在开中断的情况下执行,如果有新的中断发生,原来这个旧中断的下半部就会被换下CPU,先执行新的中断程序的上半部,等待线程调度机制为旧中断处理程序选择一个日期(依靠调度算法),再调度CPU完成下半部的执行。
内部中断
内部中断可以分为软中断和异常
-
int 8位立即数
可以通过它进行系统调用,可以表示256种中断,这与处理器所支持的中断数是相吻合的。 -
int3
注意,int和3之间没有空格,int3
是调试断点命令,其所出发的中断向量号为3 -
into
这是中断溢出指令,他所出发的中断向量号为4,但是,能否引发4号中断是要看eflags标志寄存器的OF位是否为1,如果是1才会引发中断,否则这个指令什么都不做。 -
bound
这是检查数组索引越界指令,它可以触发5号中断,用于检查数组的索引下标是否在上下边界之内,该指令的格式是bound 16/32位寄存器 16/32位内存
,目的操作数使用寄存器来存储的,其内容是待检测的数组的下标值。源操作数是内存,其内容是数组下标的下边界和上边界。当执行bound
指令时,如果下标处于数组索引的范围之外,则会触发5号中断。 -
ud2
未定义指令,这会触发第6号中断。表示指令无效,CPU无法识别。常用于软件测试中,没有实际用途。
中断处理过程及保护
完整的中断过程分为 CPU 外和 CPU 内两部分。
- CPU 外:外部设备的中断由中断代理芯片接收,处理后将该中断的中断向量号发送到 CPU。
- CPU 内: CPU 执行该中断向量号对应的中断处理程序。 CPU 外这部分的处理过程,在后面中断代理芯片 Intel 8259A 时大家就会了解,这部分内容 属于硬件范畴,我们这里只讨论处理器内的过程,这是软件能控制的部分。
- 处理器根据中断向量号定位中断门描述符。
中断向量号是中断描述符的索引,当处理器收到一个外部中断向量号后,它用此向量号在中断描述符 表中查询对应的中断描述符,然后再去执行该中断描述符中的中断处理程序。由于中断描述符是 8 个字节, 所以处理器用中断向量号乘以 8 后,再与 IDTR 中的中断描述符表地址相加,所求的地址之和便是该中断 向量号对应的中断描述符。 - 处理器进行特权级检查。
由于中断是通过中断向量号通知到处理器的,中断向量号只是个整数,其中并没有 RPL,所以在对由中断引起的特权级转移做特权级检查中,并不涉及到 RPL。中断门的特权检查同调用门类似,对于软件主动发起的软中断,当前特权级 CPL 必须在门描述符 DPL 和门中目标代码段 DPL 之间。这是为了防止位于 3 特权级下的用户程序主动调用某些只为内核服务的例程。
(a)如果是由软中断 int n, int3 和 into 引发的中断,这些是用户进程中主动发起的中断,由用户代码 控制,处理器要检查当前特权级 CPL 和门描述符 DPL,这是检查进门的特权下限,如果 CPL 权限大于等 于 DPL,即数值上 CPL~三门描述符 DPL,特权级“门槛”检查通过,进入下一步的“门框”检查。否则, 处理器抛出异常。
(b)这一步检查特权级的上限(门框〉:处理器要检查当前特权级 CPL 和门描述符中所记录的选择子 对应的目标代码段 DPL,如果 CPL 权限小于目标代码段 DPL,即数值上 CPL>目标代码段 DPL,检查通 过。 否则 CPL 若大于等于目标代码段 DPL,处理器将引发异常,也就是说,除了用返回指令从高特权级 返回,特权转移只能发生在由低向高。 若中断是由外部设备和异常引起的,只直接检查 CPL 和目标代码段的 DPL,和上面的步骤 b)是一样的,要求 CPL 权限小于目标代码段 DPL,即数值上 CPL >目标代码段 DPL,否则处理器引发异常。 -
执行中断处理程序。 特权级检查通过后,将门描述符目标代码段选择子加载到代码段寄存器 cs 中,把门描述符中中断处 理程序的偏移地址加载到 EIP,开始执行中断处理程序。
由于 IDT 中全都是门描述符,所以图 7-7 的 IDT 中的“某门描述符”表示中断门、陆阱门或任务门。 中断发生后,eflags 中的NT位和 TF位会被置0。如果中断对应的门描述符是中断门,标志寄存器eflags 中的 E 位被自动置 0,避免中断嵌套,即中断处理过程中又来了个新的中断,这是为防止在处理某个中断 的过程中又来了个相同的中断,即同一种中断未处理完时又来了一个,这会导致一般保护性(GP)异常。 这表示默认情况下,处理器会在无人打扰的方式下执行中断门描述符中的中断处理例程。 若中断发生时对应的描述符是任务门或陆阱门的话, CPU 是不会将 E 位清 0 的。 因为陆阱门主要用 于调试,它允许 CPU 响应更高级别的中断,所以允许中断嵌套。而对任务门来说,这是执行一个新任务, 任务都应该在开中断的情况下进行,否则就独占 CPU 资源,操作系统也会由多任务退化成单任务了。 从中断返回的指令是 iret,它从战中弹出数据到寄存器 cs、 eip、 eflags 等,根据特权级是否改变,判 断是否要恢复旧枝,也就是说是否将战中位于 SS_old 和 ESP_old 位直的值弹出到寄存 SS 和 esp。当中断 处理程序执行完成返回后,通过 iret 指令从棋中恢复 eflags 的内容。
- 处理器提供了专门用于控制 E 位的指令,通过它, IF 可以直接控制。指令
cli
使 IF 位为 0,这称为关中断,指令sti
使 E 位为 1,这称为开中断。 - IF 位只能限制外部设备的中断,对那些影响系统正常运行的中断都无效,如异常 exception,软中断, 如 int n 等,不可屏蔽中断 NMI 都不受 E 限制。
- 进入中断时要把
NT
位和TF
位置为 0。 TF 表示 Trap Flag,也就是陆阱标志位,这用在调试环境中,当TF
为 0 时表示禁止单步执行,也就是说,进入中断后将 TF 直为 0,表示不允许申断处理程序单步执行, 这是好理解的。
NT
位表示NestTaskFlag
,即任务嵌套标志位,也就是用来标记任务嵌套调用的情况。任务嵌套调用是指 CPU 将当前正执行的旧任务挂起,转去执行另外的新任务,待新任务执行完后, CPU 再回到旧任务继续执行。 为什么 CPU 执行完旧任务后还能回到新任务呢?
原因是在执行新任务之前, CPU 做了两件准备工作。
(1 )将旧任务 TSS 选择子写到了新任务 TSS 中的“上一个任务 TSS 的指针”字段中。
(2)将新任务标志寄存器 eflags 中的 NT 位置 l ,表示新任务之所以能够执行,是因为有别的任务调 用了它。
第(2)步中这个“别的任务”便是指 CPU 在第 1 步中写进新任务自己 TSS 的“上一个任务 TSS 的 指针” 字段中的值。 CPU 把新任务执行完后还是要回去继续执行旧任务的,怎样回到旧任务呢?
这也是通过iret
指令。 iret 指令 因此有了两个功能, 一是从中断返回,另外一个就是返回到调用自己执行的那个旧任务,这也相当于执行一个任 务。对同一条iret
指令,CPU 是如何知道该从中断返回,还是返回到旧任务继续执行呢? 这就用到 NT 位了,当 CPU 执行 iret 时,它会去检查 NT 位的值,如果 NT 位为 1,这说明当前任务 是被嵌套执行的,因此会从自己 TSS 中“上一个任务 TSS 的指针”字段中获取旧任务,然后去执行该任 务。如果 NT 位的值为 0,这表示当前是在中断处理环境下,于是就执行正常的中断退出流程。
这个还是编译器实现为准,见下节
中断发生时的压栈
-
中断时特权级发生变化的压栈
- 中断时特权级不发生变化的压栈
![](https://img.haomeiwen.com/i13348817/594dee5c70dc6c1e.png?imageMogr2/auto-orient/strip%7CimagA
eView2/2/w/1240) - 同类的指令还有
iretw
和iretd
, 16 位模式下用iretw
32 位模式下用iretd
。iret
是iretw
和iretd
的简写,无论是在 16 位模式,还是在 32 位模式下编码,都可以只用iret
指令,它是被编译成iretw
,还是被编译成iretd
,取决于伪指令 BITS 所指明的字长。 - 注意,如果中断有错误码,处理器并不会主动跳过它的位置,我们必须手动将其跳过,也就是说,在准备用
iret
指令返回时,当前栈指针 esp 必须指向栈中备份的 EIP_old 所在的位置,这样处理器才能将栈中的数据对号入座,弹出到各自对应的寄存器中。 - 注意,如果在返回时需要改变特权级,将会检查数据段寄存器 DS、 ES、 FS 和 GS 的内容,如果在它 们之中,某个寄存器中选择子所指向的数据段描述符的 DPL 权限比返回后的 CPL ( CS.RPL) 高,即数值上 返回后的 CPL>数据段描述符的 DPL,处理器将把数值 0 填充到相应的段寄存器。原理是选择子为 0 便指向 GDT 中第 0 个段描述符,该段描述符不可用,从而故意使处理器抛异常。 如果忘记了,可以参考从调用门返回时的部分。
中断错误码 ERROR_CODE
-
EXT
表示EXTernal event
,即外部事件,用来 指明中断源是否来自处理器外部,如果中断源是不
可屏蔽中断NMI 或外部设备, EXT 为 1,否则为 0。 - IDT 表示选择子是否指向中断描述符表 IDT, IDT 位为 1,则表示此选择子指向中断描述符表,否则指向全局描述符表 GDT 或局部描述符表 LDT。
- TI 和选择子中 TI 是一个意思,为 0 时用来指明选择子是从 GDT 中检索描述符,为 1 时是从 LDT 中 检索描述符。当然,只有在 IDT 位为 0 时 TI位才有意义。
- 选择子高 13 位索引就是选择子中用来在表中索引描述符用的下标。 高 16 位是保留位,全 0。
- 错误码本质上就是描述符选择子,通过低 3 位属性来修饰此选择子指向是哪个表中的哪个描述符。
可编程中断控制器
- 一个8259A只可以管理8个中断,INTEL处理器共支持256个中断。为了多支持一些中断设备,可以将多个8259A组合,即官方术语
级联
,有了级联以后,每个8259A都被称为1片。级联时只能有一个主片master,其余的称为从片slave。若采用级联的方法,n片8259A可以支持7n+1个中断源,(原理类似新交换机或插线板在原来的交换机上再引出一条线作拓展的计算),级联一个从片,要占用主片的一个IRQ接口。 -
个人电脑中只有两片8259A芯片,级联如图
-
8259A芯片内部结构
一些信号和寄存器介绍如下
- INT: 8259A 选出优先级最高的中断请求后,发信号通知 CPU。
- INTA: INT Acknowledge,中断响应信号。 位于 8259A 中的 INTA 接收来自 CPU 的INTA 接口的 中断响应信号。
- IMR: Interrupt Mask Register,中断屏蔽寄存器,宽度是 8 位,用来屏蔽某个外设的中断。
- IRR: Interrupt Request Register,中断请求寄存器,宽度是 8 位。它的作用是接受经过 IMR 寄存器过 滤后的中断信号并锁存,此寄存器中全是等待处理的中断,“相当于” 8259A 维护的未处理中断信号队列。
- PR: Priority Resolver,优先级仲裁器。 当有多个中断同时发生,或当有新的中断请求进来时,将 它与当前正在处理的中断进行比较,找出优先级更高的中断。
- ISR: In-Servi臼Register,中断服务寄存器,宽度是8 位。当某个中断正在被处理时,保存在此寄存器中。