软件通过中断号识别中断,每个中断号唯一对应一个中断源。 中断有以下4种类型。
(1)软件生成的中断(Software Generated Interrupt,SGI):中断号0~15,通常用来实现处理器间中断(Inter-Procesor Interrupt,IPI)。这种中断是由软件写分发器的软件生成中断寄存器(GICD_SGIR)生成的。
(2)私有外设中断(Private Peripheral Interrupt,PPI):中断号16~31。处理器私有的中断源,不同处理器的相同中断源没有关系,比如每个处理器的定时器。
(3)共享外设中断(Shared Peripheral Interrupt,SPI):中断号32~1020。这种中断可以被中断控制器转发到多个处理器。
(4)局部特定外设中断(Locality-specific Peripheral Interrupt,LPI):基于消息的中断。 GIC vI和GIC v2不支持LPI。
中断可以是边沿触发(edge-triggered),也可以是电平触发(level-triggered)。边沿触发是在电压变化的一瞬间触发,电压由高到低变化触发的中断称为下降沿触发,电压由低到高变化触发的中断称为上升沿触发。电平触发是在高电压或低电压保持的时间内触发, 低电压触发的中断称为低电平触发,高电压触发的中断称为高电平触发。
中断有以下4种状态。
(1)Inactive:中断源没有发送中断。
(2)Pending:中断源已经发送中断,等待处理器处理。
(3)Active:处理器已经确认中断,正在处理。
(4)Active and pending:处理器正在处理中断,相同的中断源又发送了一个中断。
中断的状态转换过程如下。
(1)Inactive->Pending:外围设备发送了中断。
(2)Pending->Active:处理器确认了中断。
(3)Active->Inactive:处理器处理完中断。
对于中断控制器的每个中断源,向中断域添加硬件中断号到Linux中断号的映射时, 内核分配一个Linux中断号和一个中断描述符irg_desc,中断描述符有两个层次的中断处理函数。
(1)第一层处理函数是中断描述符的成员handle_irq()。
(2)第二层处理函数是设备驱动程序注册的处理函数。中断描述符有一个中断处理链表(irq_desc.action),每个中断处理描述符(irq_action)保存设备驱动程序注册的处理函数。因为多个设备可以共享同一个硬件中断号,所以中断处理链表可能挂载多个中断处理描述符。
怎么存储Linux中断号到中断描述符的映射关系?有两种实现方式。
(1)如果中断编号是稀疏的(即不连续),那么使用基数树(radix tree)存储。需要开启配置宏CONFIG SPARSE IRQ。
(2)如果中断编号是连续的,那么使用数组存储。
kernel/irq/irqdesc.C
#ifdef CONFIG_SPARSE_IRQ
static RADIX_TREE(irg_desc_tree, GFP_KERNEL);
#else
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[0. ...NR_IRQS-1] = {
.handle_irq = handle bad irq,
.depth = 1,
.lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
}
};
#endif
ARM64架构默认开启配置宏CONFIG_SPARSE_iRQ,使用基数树存储。