1.2现代的APIC
APIC虽号称现代,但也出现10几年了,PC机市场总是很晚才能接触到新的技术,前面说了,我的T42用的还是PIC呢。APIC相较于PIC来说,最大的优点是能适用于MP平台,当然,管脚多是它另一个优点。APIC由两部分组成,一个称为LAPIC(Local APIC,本地高级中断控制器),一个称为IOAPIC(I/O APCI,I/O高级中断控制器)。前者位于CPU中,在MP平台,每个CPU都有一个自己的LAPIC。后者通常位于南桥上,像PIC一样,连接各个产生中断的设备。在一个典型的具有多个处理器的PC平台,通常有一个IOAPIC和多个LAPIC,它们相互配合,形成一个中断的分发网络,图1-3显示了这个典型的情况:
图1-3 APIC模式(摘自《深入理解Linux内核》)
图中的中断控制器通信总线,是IOAPIC和LAPIC通信的桥梁,在Intel的P6架构和Pentium系列CPU中,它是一条单独的APIC总线。时代在进步,Pentium4和Xeon系列CPU出现后,APIC Bus已经不存在,系统的前端总线代替了它。
1.2.1 IOAPIC
话分两头,让我们先来看看IOAPIC。和PIC对比,IOAPIC最大的作用在于中断分发。根据其内部的PRT(Programmable Redirection Table)表,IOAPIC可以格式化出一条中断消息,发送给某个CPU的LAPIC,由LAPIC通知CPU进行处理。目前典型的IOAPIC具有24个中断管脚,每个管脚对应一个RTE(Redirection Table Entry,PRT表项)。与PIC不同的是,IOAPIC的管脚没有优先级,也就是说,连接在管脚上的设备是平等的。但这并不意味着APIC系统中没有硬件优先级。设备的中断优先级由它对应的vector决定,APIC将优先级控制的功能放到了LAPIC中,我们在后面会看到。
要搞清楚IOAPIC是怎么工作的,PRT表是关键,下表列出了RTE的格式:
Bit |
描述 |
63:56 |
Destination Field,目的字段,R/W(可读写)。根据Destination Filed(见下)值的不同,该字段值的意义不同,它有两个意义: Physical Mode(Destination Mode为0时): 其值为APIC ID,用于标识一个唯一的APIC。 Logical Mode(Destination Mode为1时):其值根据LAPIC的不同配置,代表一组CPU(具体见LAPIC相关内容) |
55:17 |
Reserved,预留未用。 |
16 |
Interrupt Mask,中断屏蔽位,R/W。置一时,对应的中断管脚被屏蔽,这时产生的中断将被忽略。清零时,对应管脚产生的中断被发送至LAPIC。 |
15 |
Trigger Mode,触发模式,R/W。指明该管脚的的中断由什么方式触发。 1:Level,电平触发 2:Edge,边沿触发 |
14 |
Remote IRR,远程IRR,RO(只读)。只对level触发的中断有效,当该中断是edge触发时,该值代表的意义未定义。 当中断是level触发时,LAPIC接收了该中断,该位置一,LAPIC写EOI时,该位清零。 |
13 |
Interrupt Input Pin Polarity(INTPOL),中断管脚的极性,R/W。指定该管脚的有效电平是高电平还是低电平。 0:高电平 1:低电平 |
12 |
Delivery Status,传送状态,RO。 0:IDEL,当前没有中断 1:Send Pending,IOAPIC已经收到该中断,但由于某种原因该中断还未发送给LAPIC 笔者:某种原因,例如IOAPIC没有竞争到总线 |
11 |
Destination Mode,目的地模式,R/W。 0:Physical Mode,解释见Destination Field 1:Logical Mode,同上 |
10:8 |
Delivery Mode,传送模式,R/W。用于指定该中断以何种方式发送给目的APIC,各种模式需要和相应的触发方式配合。可选的模式如下,字段相应的值以二进制表示: Fixed: 000b,发送给Destination Filed列出的所有CPU,level、edge触发均可。 Lowest Priority:001b,发送给Destination Filed列出的CPU中,优先级最低的CPU(CPU的优先级见LAPIC相关内容)。Level、edge均可 SMI:010b,System Management Interrupt,系统管理中断。只能为edge触发,并且vector字段写0 NMI:100b,None Mask Interrupt,不可屏蔽中断。发送给Destination Field列出的所有CPU,Vector字段值被忽略。NMI是edge触发,Trigger Mode字段中的值对NMI无影响,但建议配置成edge。 INIT:101b,发送给Destination Filed列出的所有CPU,LAPIC收到后执行INIT中断(详细信息参考相关CPU spec中INIT中断一节)。触发模式同NMI。 ExtINT:111b,发送给Destination Filed列出的所有CPU。CPU收到该中断后,认为这是一个PIC发送的中断请求,并回应INTA信号(该INTA脚连接到的是与该管脚相连的PIC上,而非IOAPIC上) 笔者:ExtINT用于PIC接在APIC上的情况,见后面的Virtual Wire Mode |
7:0 |
Interrupt Vector,中断向量,R/W。指定该中断对应的vector,范围从10h到FEh(x86架构前16个vector被系统预留,见后面相关内容) |
表1-1 RTE格式
当IOAPIC某个管脚接收到中断信号后,会根据该管脚对应的RTE,格式化出一条中断消息,发送给某个CPU的LAPIC。从上表我们可以看出,该消息包含了一个中断的所有信息。
题外话 —— Remote IRR有什么用?
Bluesky_jxc同学曾经问我为啥Remote IRR对edge触发无用?我说可能是实现相关吧。嗯,通常解释不清楚的问题都可以推到实现相关的头上。这个问题可以画3个等号:“Remote IRR有何用?” == “为什么edge触发不用Remote IRR?” == “为什么level触发的EOI要广播到所有IOAPIC?”
这确实是个实现问题,Intel公司的P.K.Nizar等人在《Multi-Processor Computer System With Interrupt Controllers Providing Remote Reading》中论述了这个问题。这是一篇讲早期APIC系统电路设计的文章,那时还不叫APIC,而是MPIC(Multi-PIC),扯远了。
Remote IRR实际应该叫“Monitor Remote IRR”,用于监控对应中断管脚的状态。它与中断管脚INTIN#以异或的逻辑驱动IOAPIC的消息单元。异或结果为1时,发送消息。消息分两种:level-assert和level-deassert。当Remote IRR为0,INTIN#为1,发送level-assert消息,LAPIC收到后将IRR对应bit置一。Remote IRR为1,INTIN#为0,发送level-deassert消息,LAPIC收到后将IRR对应bit清零。
Remote IRR还可以保证level触发中断共享情况下,CPU服务完所有中断。
举个例子,有两个PCI设备(Dev A、Dev B)共享一个中断管脚INTIN1,Dev A先把管脚拉至有效电平(INTIN1为1),Dev B在一段时间后也同样动作(此时Dev A仍然在有效电平)。当INTIN1由0跳变到1时,其对应的Remote IRR bit为0。IOAPIC发送Level Assert消息通知中断发生,Remote IRR置1。当CPU处理完Dev A的中断后,发送EOI到IOAPIC。此时Remote IRR清0,由于Dev B的中断还没处理,INTIN1仍然为1,故又一条中断消息产生。在所有中断处理完后,INTIN1清0,Remote IRR为1,发送level-deassert消息,LAPIC清零IRR对应bit。此时INTIN1对应的中断全部处理完。在LAPIC为Dev B写EOI时,Remote IRR清零。当CPU正在处理中断时,INT1为1,Remote IRR为1,没有中断消息。
对于edge触发中断,由于中断管脚不会一直处于有效电平,故不需要Remote IRR。EOI广播的原因,上面的论述已有暗示,不废话了。