概述
中断的基本分类
首先,我们必须对 x86 体系架构下中断的分类有一个基本的分类认知:
Hardware Interrupt : 我们将有外部硬件设备产生的中断信号称为硬件中断,由于它可以随时发生 (但仍然按照 CPU 时钟周期来产生),因此也被称为Asynchronous Interrupt ;-
Software Interrupt : 软件中断由 CPU 执行的指令产生,由于它伴随着 CPU 指令的执行而产生,因此也被称为Synchronous Interrupt 。具体可以分为两种情况:Exception : 当处理器在执行指令的过程中发现异常事件 (e.g. 页表不存在, 除 0 错误等) 的时候被触发,由 CPU 控制单元发出;Sysenter : 程序可以通过执行一些指令来产生软件中断 (e.g. x86 平台下的int
指令)
其中,对于 Exception,Intel 平台下又有如下的分类 intel_document_volume_3a_c6:
-
Fault : 该类异常通常可以被纠正,纠正后程序可以继续运行。当 Fault 异常发生时,处理器会记录下产生 Fault 异常的指令的地址 (i.e. 当前CS
和EIP
寄存器的值),作为异常处理程序的返回地址。在运行完异常处理程序后,处理器将返回原先产生 Fault 异常的指令并重新运行; -
Trap : 该类异常通常是由于处理器运行了Trapping Instruction 引起的,在运行完异常处理程序后,程序可以被继续运行。值得的注意的是此时异常程序返回后执行的是紧跟在 Trapping Instruction 后面的指令,这与 Fault 异常有所区别; -
Abort : 该类异常代表着处理器无法精确地获取导致异常的指令的地址,因此产生 Abort 异常后,程序将无法恢复。
Linux 中断子系统
Linux 中断子系统的设计结构如
在本文中,我们将看到通用中断层中相关的数据结构和 API,另外我们还将结合 x86 架构分析底层中断从产生到最终运行对应设备驱动的中断回调的全过程。下面,我们自底向顶,逐渐分析 x86 平台下中断的全流程。
中断硬件
说到中断硬件,我们的主角就是
在启动对上述两种中断控制器架构的分析之前,我们首先比如明确中断控制器具体的功能:
这里 CPU 中断向量,在 x86 架构下实际上指的就是处理器硬件所能识别的硬件中断编号,也即
在明确了中断控制器的具体功能后,下面我们对 PIC 和 APIC 中断控制器结构进行分析,其中重点分析 APIC。
PIC
Intel 8259 wiki_8259 是历史上最经典的 PIC,由 Intel 在上个世纪 80 年代为 8086 处理器开发,其 28 个引脚分布如下所示:
其中,各个管脚的功能分别是:
IR0~IR7
(Interrupt Request 0~7): 用于连接设备;INT
: 连接 CPU, 当有中断请求时,拉高该管脚以通知 CPU 中断的到来;INTA
: 连接 CPU,CPU 通过该管脚应答中断请求,并通知 PIC 提交中断的 vector 到数据线;
此外,由于一片 8259 只能连接 8 个设备,对于当时较为先进的 PC 架构来说显得过少,通常会通过 CS (片选) 将两个 8259A 连在一起构成一个可以连接 15 个设备的 PIC。
正如其名 Programmable Interrupt Controller,8259 是可编程的芯片。ICW (Interrupt Command Word) 寄存器用于初始化 8259 芯片; OCW (Operation Command Word) 寄存器用于控制 8259 芯片。另外,8259 中还有以下三个内部使用的寄存器:
- IRR: Interrupt Request Register,一共 8 bits,对应于 IR0~IR7 八个中断管脚。当某个管脚的中断请求到来后,若该管脚没有被屏蔽,则 IRR 中对应的 bit 被置位,表示当前 PIC 已经接收到设备的中断请求,但是还未提交给 CPU;
- ISR: Interrupt Service Register,一共 8 bits,每个 bit 的意义同上。当 IRR 中的某个中断请求被发送给 CPU 后,ISR 中对应的 bit 即被置位,表示中断已经发送给 CPU,但 CPU 仍未处理完;
- IMR: Interrupt Mask Register,一共 8 bits,每个 bit 的意义同上,用于屏蔽中断。当某个 bit 被置位时,对应的中断管脚将被屏蔽,上面介绍的 IRR 对应的 bit 在中断来临时将不会被置位。
PIC 与我们下面将要介绍的 APIC 不同,每一个管脚都具有优先级,以 0 号管脚优先级最高,管脚号码越小的设备,具有较高的中断优先级。
我们上面提到过,中断控制器的功能是把外部设备的中断电平信号,转化为 x86 硬件可以理解的中断向量。通过对 8259 的 ICW 寄存器进行编程,我们可以设定起始的中断向量号。例如我们设置起始的中断向量号为 $16$,则当 IR3
管脚接收到中断请求时,中断控制器输出的中断向量号将为 $16+3=19$。
通过 8259 的 PIC 发起的中断流程一般如下所示:
- 一个或多个
IRx
管脚上产生电平信号,若对应的中断没有被屏蔽,IRR 中相应的 bit 被置位; - PIC 拉高
INT
管脚通知 CPU 中断发生; - CPU 通过
INTA
管脚应答 PIC,表示中断请求收到; - PIC 收到
INTA
应答后,将 IRR 中具有最高优先级的 bit 复位,并置位 ISR 中对应的 bit; - CPU 通过
INTA
管脚第二次发出脉冲,PIC 收到后计算最高优先级中断的中断向量值,并将它提交到数据线上; -
PIC 等待 CPU 写
EOI
(End Of Interrupt):- 收到
EOI
后,ISR 中最高优先级的 bit 被清零; - 如果 PIC 处于 AEOI (Auto End Of Interrupt) 模式,在 PIC 收到第二个
INTA
脉冲时,ISR 中最高优先级的 bit 就会被自动清零。
- 收到
前面提到,PIC 的各个管脚是具有优先级的,当一个中断正被 CPU 处理时,优先级等于或低于该中断的中断被自动屏蔽。一个比当前中断优先级更高的中断会被马上发送给 CPU, 而不管 CPU 是否已经为当前的中断写过 EOI
。
值得注意的是,以上关于优先级的论述,是基于 PIC 默认的 Full Nested 模式说的。这是 PIC 最常用的模式。实际上 PIC 还有优先级轮转 (rotating)、特殊优先级轮转模式。优先级轮转是指 PIC 在服务完一个管脚后将其优先级降低,并升高未服务管脚的优先级,以实现一种类似轮询的模式,这和后面讲到的 LAPIC Arb 机制类似。
APIC
上面介绍的 PIC 中断架构,最大的问题在于它无法应用在
APIC 由两部分组成,一个称为
如
上图更加清晰地展示了老架构所使用的 3-Wire APIC Bus;后来的 Intel 至强处理器,I/O APIC 直接使用 System Bus 发送中断请求,然后通过北桥中的 Bridge 转发给 LAPIC;LAPIC 之间的通信走的同样也是 System Bus。
中断信号、中断消息的区别
在正式展开对 I/O APIC 和 LAPIC 的分析之前,我们需要再次梳理中断信号、中断消息这两者的区别。
在收到中断信号后,中断控制器/中断处理结构 需要通过查表等一系列操作,将中断信号转化为在系统总线上传输的消息,也即
有前端当然就有后端。当前端完成中断消息的转换和发送时,中断消息中封装了关于生成的中断的各种信息,系统中需要有相应的后端结构对这些信息进行进一步处理,最后通告 CPU 运行对应位置的中断服务程序,下面的阐述中我们把实现「中断消息处理」的结构称为
如
中断类型 | 中断处理前端 | 中断处理后端 |
---|---|---|
外部设备中断 | I/O APIC | LAPIC 的 ISR, IRR, TPR, PPR 和 EOI 等寄存器组 |
处理器间中断 | LAPIC 的 ICR 寄存器 | LAPIC 的 ISR, IRR, TPR, PPR 和 EOI 等寄存器组 |
处理器内部中断 | LAPIC 的 LVT 寄存器组 | LAPIC 的 ISR, IRR, TPR, PPR 和 EOI 等寄存器组 |
可以看到,APIC 系统的中断处理的前后端就藏在了 I/O APIC 和 LAPIC 两个片子中,下面我们分别分析 I/O APIC 和 LAPIC。我们首先从外部设备中断的处理前端 —— I/O APIC 入手进行分析。
I/O APIC
和 PIC 相比,I/O APIC 最大的作用在于中断分发。当 I/O APIC 收到外部设备的中断信号时,它根据它内部的
Intel 发布的第一款 APIC 芯片是 Intel 82489DX,它是一个包含了 I/O APIC 和 LAPIC 的独立的芯片。对于一个老式的双处理器的系统来说,如果使用 82489DX 构建 APIC 架构,则一共需要 3 个 —— 2 个作为 LAPIC,1 个作为 I/O APIC。后来,LAPIC 被做进了处理器中,只有 I/O APIC 需要独立的芯片支持,因此就诞生了后来的 Intel 82093AA 芯片 inteldoc_82093AA,它是专用于 I/O APIC 功能的芯片 ic_evolution。
要理解 I/O APIC 是如何工作的,那么关键就在于 PRT 表。
Bit | 描述 |
---|---|
63:56
[8-bits] |
Destination Field,目的字段,R/W (可读写)。根据 Destination Mode 字段 (见下) 的不同,该字段值的意义不同,它有两个意义:
|
55:17
[39-bits] |
Reserved,保留未用; |
16
[1-bit] |
Interrupt Mask,中断屏蔽位,R/W:
|
15
[1-bit] |
Trigger,触发模式,R/W,指明该管脚的中断由什么方式触发:
|
14
[1-bit] |
Remote IRR,远程 IRR,RO (只读)。只对 Level 触发的中断有效,当该中断是 Edge 触发时,该值代表的意义位定义。当对应引脚的中断是 Level 触发的中断时,该值的含义如下:
|
13
[1-bit] |
Interrupt Input Pin Polarity (INTPOL),远程 IRR,中断管脚的极性,R/W。指定该管脚的有效电平是高电平还是低电平:
|
12
[1-bit] |
Delivery Status,传送状态,RO:
|
11
[1-bit] |
Destination Mode,目的地模式,R/W:
|
10:8
[3-bits] |
Delivery Mode,传送模式,R/W。用于指定该中断以何种方式发送给目的 LAPIC,各种方式需要和相应的触发方式配合,可选的模式如下:
|
7:0
[8-bit] |
Interrupt Vector,中断向量,R/W,指定该引脚产生的中断对应的中断向量,范围从 |
当 I/O APIC 某个管脚接收到中断信号后,会根据该管脚对应的 RTE,格式化出一条中断消息,发送给某个 (或多个) CPU 的 LAPIC。从上表我们可以看出,该消息包含了一个中断的所有信息。
🤔 杂谈: Remote IRR 的意义
这里我们简单讨论一下 Remote IRR 字段的意义,以及为什么它仅对 Level 触发的中断有效。
Remote IRR 实际应该叫“Monitor Remote IRR”,用于监控对应中断管脚的状态。如 INTIN#
以异或的逻辑驱动 I/O APIC 的消息发送单元,异或结果为 1 时发送消息。此时,I/O APIC 发送消息分两种:level-assert 和 level-deassert:
- 当 Remote IRR 为 0,
INTIN#
为 1 时,发送 level-assert 消息,LAPIC 收到后将 IRR 置位; - 当 Remote IRR 为 1,
INTIN#
为 0 时,发送 level-deassert 消息,LAPIC 收到后将 IRR 复位;
关于 LAPIC 的 IRR 寄存器详见下面介绍 LAPIC 部分的内容。
Remote IRR 如此设计是为了保证: 在 Level 触发中断共享的情况下,CPU 服务完所有中断。
如 INTIN1
,该引脚的中断触发模式为 Level。我们的场景是:Devive A 先把管脚拉至有效电平 (INTIN1
有效电平为高电平),Devive B 在一段时间后也同样动作 (此时 Devive A 仍然在有效电平)。过程分析如下:
- 当 Devive A 拉高引脚电平时,也即
INTIN1
由 0 跳变到 1 时,该引脚对应的 Remote IRR 此时为 0,因此 异或结果为 1,I/O APIC 发送 level-assert 消息通知中断发生,并且 I/O APIC 会置位 Remote IRR; - 在 LAPIC 接收到消息后,LAPIC 将 IRR 中对应的 bit 置位;
- 当 CPU 处理完 Devive A 的中断后,会发送
EOI
到 I/O APIC。此时 Remote IRR 被复位; - 由于 Devive B 的中断还没处理,因此引脚
INTIN1
仍然为高电平,这导致 Remote IRR 和INTIN1
的异或结果又为 1,故又一条 level-assert 中断消息产生; - 在所有中断处理完后,
INTIN1
被复位,在 LAPIC 向 I/O APIC 写EOI
之前,Remote IRR 仍为 1,这导致 Remote IRR 和INTIN1
的异或结果又为 1,故发送 level-deassert 消息,表明当前 Level 触发的中断引脚上已经没有中断请求了,LAPIC 复位 IRR 中对应的 bit; - 在 LAPIC 为 Device B 的中断写
EOI
时 ,Remote IRR 被复位。此时 Remote IRR 和INTIN 1
均为 0 (低电平),因此 I/O APIC 不发送任何消息。
而对于 Edge 触发的中断,由于中断管脚不会一直处于有效电平,因此也就不需要 Remote IRR 来辅助。
Local APIC
在收到 I/O APIC 的中断消息后,LAPIC 会将该中断交给 CPU 进行处理。和 I/O APIC 相比,LAPIC 具有更多的寄存器以及更加复杂的机制。
LAPIC 可以接受的中断信号/消息
除了来自 I/O APIC 到达 LAPIC 的中断消息,实际上,LAPIC 还可以接收 本地
根据 Intel 官方编程手册 intel_document_volume_3a_c10,LAPIC 可以接收以下几种类型的中断消息 (p.s.
下面我们对 LAPIC 中的各个部件的功能进行分析。
中断类型 | 描述 |
---|---|
本地中断源 |
|
CPU Core 本地连接的 I/O 设备产生的 |
APIC 架构允许 I/O 设备通过和处理器的 LINT[1:0] 引脚相连来产生本地中断信号,也可以在这两个引脚上连接 8259 等中断控制芯片来控制外部设备的中断 |
APIC 计时器产生的 |
本地的 APIC 计时器在计数器到达设置的值后将会触发中断信号 |
Performance monitoring counter 产生的中断 | Intel P6,奔腾和至强处理器可以被设置为当 performance-monitoring 计数器溢出的时候,向处理器发送中断信号 |
温度传感器产生的 |
|
APIC Internal Error 产生的 |
当对 Local APIC 的操作产生错误 (e.g. 访问一个不存在的寄存器) 时产生的中断信号 |
外部中断源 |
|
外部连接的 I/O 设备的 |
外部连接的 I/O 设备会把它们产生的中断信号发送给 I/O APIC,后者会把中断信号转换为 |
Interrupt-processor Interrupts (IPIs) 的 |
Intel 处理器允许 CPU Core 在 System Bus 上将 |
本地中断处理前端 : LVT
针对本地
处理器间中断处理前端 : ICR
针对处理器间中断,CPU Core 可以通过向其本地的 ICR (Interrupt Command Register) 写入相应的信息,来实现中断消息的发送。ICR 的具体格式如下所示:
处理后端 : IRR 和 ISR 寄存器
首先我们对 LAPIC 的 IRR 和 ISR 寄存器进行介绍。
IRR 的功能和 PIC 的 IRR 功能类似,代表 LAPIC 已接收中断,但还未交 CPU 处理。
ISR 的功能和 PIC 的 ISR 功能类似,代表 CPU 已开始处理中断,但还未完成。与 PIC 有所不同的 是,当 CPU 正在处理某中断时,同类型中断如果发生,相应的 IRR bit 会再次被置位 (p.s. PIC 模式下,同类型的中断被屏蔽);如果某中断被 pending 在 IRR 中,同类型的中断发生, 则 ISR 中相应的 bit 会被置位。这说明在 APIC 系统中,同一类型中断最多可以被计数两次。超过两次时,不同架构处理不一样。对于 Pentium 系列 CPU 和 P6 架构,中断消息会被 LAPIC 拒绝;对于 Pentium4 和 Xeon 系列,新来的中断消息对应的中断向量会和 IRR 中对应的 bit 重叠。
当然,我们上面讨论的内容都是基于 I/O APIC 中 RTE 表项的 Delivery Mode 为 Fixed 或者 Lowest Priority 时,也即 Vector 字段有效时所作出的。
上图还有个 TMR 寄存器,即 Trigger Mode Register,用于表示当前正在处理的中断 (i.e. ISR 中记录的中断) 的触发模式。1 为 Level,0 为 Edge。对于 Level 触发的中断,当中断结束,系统软件写 EOI
时,会被广播到所有 I/O APIC,消息中含有中断的向量值,I/O APIC 收到后检查自己的 PRT 表,把相应 RTE 的 Remote IRR 位清零。
对于 LAPIC 的 IRR,Intel 给出的 x86 编程手册 intel_document_volume_3a_c10 中指出:“当 CPU 准备处理中断时,IRR 中最高优先级的 bit 被清零,ISR 中对应 bit 被置位”。实际上,根据 Multi-Processor Computer System With Interrupt Controllers Providing Remote Reading us_patent_multi_processor_interrrupt 一文中指出的 APIC 的实现,只有对于 Edge 触发的中断,ISR 对应 bit 被置位时,IRR 相应 bit 才会被复位。对于 Level 触发的中断,IRR 中的 bit 会被保留到中断结束时,系统软件写 EOI
,LAPIC 收到 I/O APIC 发出的 level-deassert 消息后才清零。
这里有一个小细节是,这里说同类型中断发生两次,会同时 Pending 在 ISR 和 IRR 的对应 bit 中。问题是,前面我们介绍的 Remote IRR 的异或逻辑保证了在写 EOI
前,新的 Level 引脚的中断消息并不会被 I/O APIC 发送给 Local APIC。是的,这里要补充一点: 这种 pending 两次的机制,只对 Edge 触发中断有效。
处理后端 : EOI 寄存器
与 PIC 一样,LAPIC 同样需要系统软件写 EOI
来知会中断处理的完成,不同的是,LAPIC 中的 EOI 是一个 32-bits 寄存器:
系统软件在中断处理完成时,需要对 EOI 寄存器写 0 来通告底层硬件当前中断处理完成。
TPR 和 PPR 寄存器
Intel 处理器对中断向量进行了优先级的划分。上文说到 Intel 处理器一共支持 256 个中断,也即 8-bits。中断向量的 [7:4]
称为中断的 [3:0]
即中断在其 Interrupt-priority Class 中的相对位置,是中断分级的子类。值得注意的是,由于 Intel 把 0~31 号中断划分给 Intel 处理器自用,因此 Interrupt-priority Class 的合法取值范围为 2(0010
)~15(1111
),取值越大,优先级越高。
一般来说,我们直接食用的是 Interrupt-priority Class 作为一个中断向量的优先级,因此一个优先级中一共就包含了 16 个中断向量。
在 Local APIC 中,有两个与中断优先级相关的寄存器 —— TPR (Task-Priority Register) 和 PPR (Processor-Priority Register)。
TPR 用于被操作系统软件设置,其包含的值代表着当前操作系统[7:4]
的 Task-Priority Class 和 [3:0]
的 Task-Priority Sub-Class。
PPR 的值代表着当前处理器
-
PPR[7:4]
: 等于TPR[7:4]
和ISRV[7:4]
中更大的那一个; -
PPR[3:0]
产生规则如下:- 如果
TPR[7:4]
>ISRV[7:4]
,那么PPR[3:0]
=TPR[3:0]
; - 如果
TPR[7:4]
<ISRV[7:4]
,那么PPR[3:0]
= 0; - 如果
TPR[7:4]
=ISRV[7:4]
,那么PPR[3:0]
视处理器不同而不同;
- 如果
只有当中断向量值代表的优先级大于 TPR 中存储的值时,LAPIC 才有可能会将其交给 CPU 进行处理,否则会屏蔽它们,这里的屏蔽指的是 LAPIC 会接受它们,将他们 pending 到 IRR 中,但不会交给 CPU 进行处理。
只有当中断向量值代表的优先级大于 PPR 中存储的值时,对应的中断消息才会被提交给 CPU Core 进行中断并且对中断予以处理。
当然,我们上面的说明针对的是 I/O APIC 中 Delivery Mode 字段为 Fixed 或者 Lowest Prority 的 RTE 所对应的中断,也即 Vector 字段有效的中断。
我们上面在介绍 I/O APIC 时曾经说过,当 Delivery Mode 字段为 Lowest Prority 时,中断消息会被发送给 Destination Field 中指定的处理器中,优先级最低的一个进行处理,这里的优先级实际上指的就是各个处理器的 LAPIC 的 TRP 的值。我们下面举一个 Pentium4 和 Xeon 系列优先级仲裁的例子,说明谁是 Lowest Priority。假设有 CPU1、CPU2、CPU3 三个 CPU,相应的 TPR 值为:TPR1=5、TPR2=6、TPR3=10,I/O APIC 以 Lowest Priority 模式发送一条中断消息,该中断对应的优先级级别为 $3$,则 CPU1 具有最低优先级,接收该中断。此时,该中断被 Pending 到 IRR 中,但不会交给 CPU 处理,因为其优先级级别低于 TPR 值。
LAPIC 中断处理后端流程
现在我们对 LAPIC 中的中断处理后端的流程进行归纳。
对于 Pentium4、Xeon 系列,在收到
- 通过中断消息的 Destination Field 字段,确定该中断是否是发送给自己的;
- 如果该中断的 Delivery Mode 为 NMI、SMI、INIT、ExtINT、SIPI,则直接交由 CPU 处理;
- 如果不为 2 中所列的中断,则置位 IRR 中相应的 bit;
- 当中断被 pending 到 IRR 或 ISR 中后,根据 TPR 和 PPR 寄存器,判断当前最高优先级的中断是否能发送给 CPU 处理;
- 软件写
EOI
通知中断处理完成。如果中断为 Level 触发,该EOI
会通过总线消息广播到所有 I/O APIC。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 处理;
- 如果不为 2 中所列的中断,将中断 pending 到 IRR 或 ISR,若已经有相同的的中断 pending 到 IRR 和 ISR 上,则拒绝该中断消息,并通知 I/O APIC “retry”;
[同 Pentium4、Xeon] 当中断被 pending 到 IRR 或 ISR 中后,根据 TPR 和 PPR 寄存器,判断当前最高优先级的中断是否能发送给 CPU 处理;[同 Pentium4、Xeon] 软件写EOI
通知中断处理完成。如果中断为 Level 触发,该EOI
会通过总线消息广播到所有 I/O APIC。NMI、SMI、INIT、ExtINT、SIPI 类型中断无需写EOI
。
I/O APIC 发出的消息是如何找到 LAPIC 的?
上面两个流程的第一步都是 LAPIC 确定是否由自己接收中断。前面我们提到,I/O APIC RTE 中的 Destination Field 用于指定由哪个 LAPIC 接收,并且分为 Physical 和 Logical 两种模式。对于 LAPIC,两种模式有着不同的意义。
Physical Mode
在该模式下,RTE 中的 Destination Field 表示的是具体的 LAPIC ID,下面我们对 LAPIC 的 ID 进行讨论。
对于 LAPIC 来说,系统在 RESET 后,都会分配一个唯一的 ID 用作标识。我们可以通过 LAPIC ID 寄存器得到它。下图所示为其格式:
在系统上电初始化的时候,系统会自动地向每一个 LAPIC 分配一个全局唯一的 APIC ID,各个 LAPIC 所被分配的 ID 也被操作系统和 BIOS 用于作为各个处理器核心的 ID。
系统软件可以在 EAX 寄存器中填入 $1$ 的情况下,运行 CPUID
指令,软件将可以从 EBX 的 [31:24]
中获取到当前运行的处理器核心所对应的 LAPIC 的 ID。对于某些 CPU 来说,其允许软件对 LAPIC 的 ID 进行修改,但是 Intel 的官方手册建议不要这样做。不论怎么修改,运行 CPUID
指令获取的始终是 LAPIC 被系统初始化赋予的 LAPIC ID。
I/O APIC 的 ID
APIC ID 分为 LAPIC ID 和 I/O APIC ID。前者唯一的标识系统中某个 LAPIC,后者唯一标识某个 I/O APIC。
与 LAPIC ID 在系统上电时被自动分配不同,I/O APIC ID 在系统上电时被统一清零,由操作系统或者 BIOS 负责验证 I/O APIC ID 是否唯一,并且在冲突时进行重新分配。分配的原则视系统中断架构不同而定:
- 对于使用系统总线进行中断消息通告的 Pentium4 和 Xeon 处理器,I/O APIC 之间分配的 ID 不重复即可,I/O APIC 的 ID 可以与 LAPIC 的 ID 相同。原因在于在此种系统中,LAPIC 需要使用 LAPIC ID 参与前端总线竞争,而 I/O APIC 却不用,因为是北桥代理它竞争总线,因此 I/O APIC ID 只用于区分多个 I/O APIC,它和 LAPIC ID 不在一个上下文;
- 对于使用 APIC Bus 的老式处理器,I/O APIC ID 要用于竞争总线,不能和 LAPIC ID 冲突,因此操作系统/BIOS 为 I/O APIC 分配 ID 的原则是从系统中所有 LAPIC ID 后最小的数字开始分配。
Logical Mode
在该模式下,中断消息中的 Destination Field 包含的不是 LAPIC ID,而是被称为
LDR 的格式如
LDR 的格式由 DFR 指定,DFR 如
DFR,Destination Format Register,目的地格式寄存器。该寄存器包含一个 4-bits 的 Model 字段,用于指定 LDR 中的 Logical APIC ID 用何种方式与 MDA 匹配。通过这两个寄存器的配合,Logical 模式又被分为了 Flat 与 Cluster 两种模式:
- Flat 模式: DFR 的 Model 值为
1111b
,此时,LAPIC 将 MDA 与 LDR 的 Logical APIC ID 做按位与,如结果不为 0 则接收中断。Logical APIC ID 中每个 bit 代表一个 LAPIC,故 8 bits 最多代表 8 个 CPU; -
Cluster 模式: DFR 的 Model 值为
0000b
。Cluster 模式又分为两种模式:Flat Cluster 模式和 Hierarchical Cluster 模式:-
Flat Cluster 模式: 该模式只支持 P6 架构和 Pentium 系列 CPU,并假定所有 APIC 通过 APIC BUS 进行通信。该模式将 MDA 编码为两个部分,高 4 bits 为簇号,低 4 bits 标识 LAPIC 在该簇内的 ID (每个bit代表一个LAPIC,故一个簇最多有4 个 LAPIC)。与之对应,LDR 的 Logical APIC ID 也被编码成同样两个部分。
工作在该模式时,LAPIC 先将 MDA 的高 4 bits 和 Logical APIC ID 的高 4 bits 比 较,以确定自己是否是中断的目的簇。若是,将 MDA 的低 4 bits 与 Logical APIC ID 的低 4 bits 按位与,若值不为 0 则接收中断,否则拒绝。
通过这种方法,高 4 bits 的簇号可以表示 15 个簇,低 4 bits 的 ID 可以代表簇内的 4 个 CPU,最多可以支持 60 个 CPU。但由于 APIC BUS 的限制,具体的说是 APIC Arb ID(APIC 仲裁 ID)的限制,该模式最多只支持 15 个 CPU。
- Hierarchical Cluster 模式: 支持 P6 架构和 Pentium 系列,以及 Xeon、Pentium4 系 列。该模式通过为每个簇引入一个 “簇管理器”,将 Flat Cluster 模式中平等的簇构成一个具有等级结构的分级网络,并最多支持 60 个 CPU。
-
对于 Flat Cluster 模式,我们下面给出一个例子 (此例中,中断为 Fix delivery mode。关于 Lowest Prority 的例子见 subsubsubsection_tpr_ppr)。假设有三个 CPU 的 Logical 模式配置为: CPU1 的 LDR 值为 0000 0001b
,CPU2 的 LDR 值为 0001 0010b
,CPU3 的 LDR 值为 0000 0100b
,并且此时 DFR 的 Model 值为 0000b
,也即 Flat Cluster 模式,则此时 CPU1、CPU3 为一簇,CPU2 为另一簇。若 I/O APIC 发出一条中断消息,其 Destination Mode 为 1,Destination Field 值为 0000 00001b
。三个 LAPIC 收到该消息后,CPU1、CPU3 通过 Destination Field 的高 4 bits 判断出该消息目的地为本簇,再将自身 Logical APIC ID 的低 4 bits 与 Destination Field 低 4 bits 按位与,最终 CPU1 接收该中断消息,CPU2、CPU3 丢弃。
PCI(e) 设备中断
TODO
通用中断层
本章我们将对通用中断层的相关数据结构以及接口进行分析。正如我们上面所说,通用中断层屏蔽了底层与架构相关的代码和厂商开发的设备驱动代码之间的联系,向上提供了顶层中断相关接口供厂商设备驱动调用,向下提供了若干与中断控制器相关的函数接口供具体底层平台予以实现。因此,理解通用中断层里面的工作原理,将有助于我们理解 Linux 内核处理中断的整个流程。
相关数据结构
通用中断层中的重要数据结构如