继续“那些事儿”系列,这次的主题是Intel的中断处理。参考的资料主要来自Intel文档第三册的第六、第十和第二十九章节,以及这篇文章。其中,有一部分的内容来自于上面提到的那篇文章。
以下内容主要围绕下面五个问题来展开:
- 第一,中断是什么,种类有哪些/li>
- 第二,中断是如何被发送给CPU的/li>
- 第三,CPU是如何对接收到的中断进行处理的/li>
- 第四,中断的优先级问题/li>
- 第五,虚拟化环境对中断提供了哪些支持/li>
接下来会对它们一一进行解答。
中断是什么,种类有哪些/h3>
一般来说,中断主要是由一些硬件设备产生的,表示这些硬件有一些重要的事件需要通知处理器,比如某些从外部设备请求的数据准备好了,需要通知处理器对其进行读取等。当然这里所谓的“一般来说”是指也可以通过软件的方式来触发中断,比如调用INT n
指令,当然这种方式产生的中断和通过意见产生的中断最终的处理方式会有很大的不同。 因此从种类来分,可以将中断分为通过硬件产生的外部中断(External interrupt)和通过软件产生的软件中断(Software interrupt)。不管是外部中断还是软件中断,每个中断都有一个中断 与之对应,对于外部中断来说,可使用的中断 范围从16到255(0到15)为系统预留的中断 ,而对于软件中断来说,可使用的中断 为0到255。除此之外,16到255范围内的中断是可以通过EFLAGS中的IF flag
进行disable的,如果EFLAGS中的IF flag
被清零,则表示当前CPU不接受这个范围内的中断,如果其被置为1,则表示当前CPU可以正常处理这个范围内的中断。
中断是如何被发送给CPU的/h3>
中断在进入CPU之前,首先会进入一个被称为Advanced Programmable Interrupt Controller(APIC)的控制器中,可以说,每个CPU都有一个APIC,被称为该CPU的Local APIC(LAPIC)。每个LAPIC由一系列的寄存器组成,这些寄存器控制了LAPIC如何将中断送到处理器中。而根据实现的不同,对这些寄存器的访问方式也不一样,比如,对于传统的APIC和xAPIC来说,这些寄存器都是被映射在内存中的,可以直接通过内存读写的方式对其进行访问,而对于x2APIC来说,需要通过访问MSR的方式来访问这些寄存器,具体的地址和访问方法可以查看Intel文档。
下图展示了一个LAPIC的架构,里面包含了各种各样的寄存器,大部分的功能都可以通过查询Intel文档获得,其中有几个特别重要的寄存器:In-Service Register (ISR),Interrupt Request Register (IRR),EOI Register,Task Priority Register (TPR),Processor Priority Register (PPR),Interrupt Command Register (ICR),Local Vector Table (LVT)。这些会在接下来的篇幅中一一进行介绍。
总之,外部设备产生的中断最终通过IOAPIC被递送到了某个(或者多个)处理器中的LAPIC中。接下来,就要看LAPIC是如何将这些中断递送给处理器进行处理了。
CPU是如何对接收到的中断进行处理的/h3>
LAPIC无论是接收到来自IOAPIC的中断,来自本地中断源的中断,还是来自其他处理器发送的IPI中断,都会将其交由CPU进行处理,但是由于CPU这个时候可能正在处理其它中断,所以需要一套机制来保证中断处理的安全性。
首先需要注意的是,在RTE格式那张表中,中断的delivery mode可能有好几种,其中NMI、SMI、INIT、ExtINT和SIPI这几种delivery mode的中断将会直接交由CPU进行处理,如果当前CPU正在处理这些delivery mode的中断,则会禁止相同的中断被递送进来。除此之外,还有一种被称为fixed的delivery mode,也就是普通的中断,它们的递送机制是通过IRR和ISR寄存器完成的。在X86平台上,这两个都是256bits的寄存器(其实是由8个64bits的寄存器组成的),每个bit代表一个中断的vector,其中第0到第16个bit是reserve的。IRR和ISR每个bit代表的意思分别如下:
- IRR:如果第
n
位的bit被置上,则代表LAPIC已接收vector为n
的中断,但还未交CPU处理。 - ISR:如果第
n
位的bit被置上,则代表CPU已开始处理vector为n
的中断,但还未完成。
需要注意的是,当CPU正在处理某中断时,如果又被递送过来一个相同vector的中断,则相应的IRR bit会再次置一; 如果某中断被pending在IRR中,同类型的被再次递送过来,则ISR中相应的bit被置一。 这说明在APIC系统中,同一类型中断最多可以被计数两次。
另外,当某个中断被处理完之后,LAPIC需要通过软件写EOI寄存器来告知。
因此,根据处理器的不同,一个典型的LAPIC中断处理流程是这样的:
对于Pentium4和Xeon系列:
- 通过中断消息的destination field字段,确定该中断是否是发送给自己的;
- 如果该中断的delivery mode为 NMI、SMI、INIT、ExtINT、SIPI,直接交由CPU处理;
- 如果不为以上所列举的中断,则将IRR中相应的bit置一;
- 当中断被pending到IRR寄存器中后,根据TPR和PPR寄存器,判断当前最高优先级的中断是否能发送给CPU进行处理,并将ISR寄存器中相应的bit置一;
- 软件写EOI寄存器通知中断处理完成。如果中断为level触发,该EOI广播到所有IOAPIC,NMI、SMI、INIT、ExtINT、SIPI类型中断无需写EOI。
对于Pentium系列和P6架构:
- 确定该中断是否由自己接收。如果是一个IPI,且delivery mode为lowest priority,LAPIC与其它LAPIC一起仲裁该IPI由谁接收。
- 若该中断由自己接收,且类型为NMI、SMI、INIT、ExtINIT、INIT-deassert、或MP协议中的IPI中断(BIPI、FIPI、SIPI),直接交由CPU处理。
- 将中断pending到IRR或ISR寄存器,若已有相同的的中断pending在IRR和ISR寄存器上,拒绝该中断消息,并通知sender “retry”。
- 同Pentium4、Xeon系列流程。
- 同Pentium4、Xeon系列流程。
在上面的这两套流程中,涉及到几个关键的寄存器(TPR,PRR)和delivery mode(lowest priority),这就涉及到中断的优先级问题了,会在“中断的优先级问题”中进行解释。
当CPU开始处理中断的时候,会查询一个被称为中断描述符表(Interrupt Descriptor Table,IDT)的数据结构,该数据结构的每一项都被预先填上了一个门描述符(gate descriptor),其中有三种门描述符:task, interrupt和trap,这里我们主要关注的是interrupt-gate descriptor。下图显示了interrupt gate的相关信息:
主要包括两种情况,第一种情况是被中断的进程不是内核进程,则需要有一个权限级别的切换,因此需要换一个栈;第二种情况是被中断的进程是一个内核进程,因此不需要切换栈,只需要在原来的栈中保存信息就可以了。整个流程还是比较清楚的,因此这里也不详述了。
中断的优先级问题/h3>
就像之前提到的,中断是有优先级概念的,具体体现在优先级高的中断会被先递送给CPU进行处理,而优先级低的中断往往需要在优先级高的中断被处理完之后才会被处理。为了简单起见,中断的优先级是由中断本身的vector信息来得到的。
我们知道每个中断都有一个vector与之对应,而中断的优先级别由下列公式得到:
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!