2. Linux中断子系统架构

中断子系统是Linux内核与硬件交互的核心枢纽。本章深入剖析中断控制器(GIC)、中断描述符(irq_desc)以及完整的中断处理流程,为理解后续网络协议栈软中断协作机制奠定坚实基础。

2.1 中断控制器(GIC)架构与角色

在ARM多核处理器系统中,通用中断控制器(Generic Interrupt Controller, GIC)是管理所有中断源的核心硬件单元。GIC负责中断的优先级仲裁、分发到指定CPU核心以及中断屏蔽/使能控制。

GIC版本演进: GICv2(经典架构,支持最多8个PE)、GICv3(引入独立重分发器,支持更多PE和LPI)、GICv4(面向虚拟化,支持直接注入虚拟中断)。
关键概念: GIC将中断分为三类——SPI(共享外设中断,如网卡、USB)、PPI(私有外设中断,每个CPU独有,如本地定时器)、LPI(基于消息的中断,GICv3+引入,用于大量外设场景)。
GICv3 架构核心组件与中断流向 外设中断源 外设中断源 外设中断源 中断分发器 (Distributor) CPU接口0 CPU接口1 CPU接口2 重分发器 (Redistributor) GIC中断流向: 外设 → 分发器(优先级仲裁、使能/屏蔽) → 重分发器(每个CPU核心独立实例) → CPU接口(向指定核心发送IRQ信号) GICv3中,重分发器负责LPI中断管理和CPU本地中断配置,实现更灵活的中断路由。

2.2 中断描述符(irq_desc)与中断号映射

Linux内核使用中断描述符(irq_desc)数据结构来抽象和管理每个中断源。每个irq_desc对应一个唯一的软件中断号(IRQ number),内核通过该中断号索引到对应的描述符,进而执行中断处理。

字段 类型 说明
irq_data struct irq_data 硬件中断号、芯片级操作函数指针
action struct irqaction * 中断处理动作链表(共享中断时多个handler)
status_use_accessors unsigned int 中断状态标志(IRQ_INPROGRESS、IRQ_DISABLED等)
depth unsigned int 中断禁用深度计数(用于嵌套禁用/使能)
lock raw_spinlock_t 保护描述符的自旋锁
name const char * 中断名称(如 "eth0"、"timer")
中断号映射注意: 硬件中断号(HW IRQ)与软件中断号(Linux IRQ)之间通过irq_domain进行映射。设备树或ACPI表描述硬件中断连接关系,内核在初始化时建立映射表。驱动开发中,platform_get_irq()返回的是软件中断号,而非硬件中断号。
中断描述符与中断号映射关系 硬件中断号 (HW IRQ: 32, 33, ...) irq_domain (映射表) 软件中断号 (Linux IRQ: 16, 17, ...) irq_desc[] irq_desc[16] irq_desc[17] ... 映射过程:设备树/ACPI → irq_domain建立HW IRQ到Linux IRQ的映射 → 驱动通过Linux IRQ索引irq_desc数组 irq_desc中action链表挂载驱动注册的中断处理函数(handler)

2.3 中断处理流程:从硬件到软件

当外设触发中断时,Linux内核经历一套严谨的流程,从硬件信号到最终执行驱动注册的处理函数。整个流程分为上半部(硬中断)和下半部(软中断/tasklet/workqueue)两个阶段。

2.3.1 上半部(硬中断上下文)

  1. 硬件信号到达: 外设拉高中断请求线,GIC仲裁后发送IRQ信号到指定CPU核心。
  2. CPU异常向量跳转: CPU响应IRQ,跳转到异常向量表中的irq_handler入口。
  3. 保存现场: 内核保存当前进程的寄存器状态,切换到中断栈。
  4. 调用handle_arch_irq: 架构相关的中断入口函数,读取GIC的IAR寄存器获取硬件中断号。
  5. irq_domain转换: 将硬件中断号转换为Linux软件中断号。
  6. 执行handle_irq_desc: 根据软件中断号找到irq_desc,调用其handle_irq函数(如handle_level_irq或handle_edge_irq)。
  7. 调用action链表: 遍历irq_desc->action链表,执行驱动注册的handler函数(硬中断处理函数)。
  8. EOI(End of Interrupt): 写GIC的EOI寄存器,通知中断控制器处理完成。
硬中断上下文限制: 硬中断处理函数运行在中断上下文,不可睡眠、不可持有互斥锁、不可访问用户空间。必须快速完成(通常微秒级),耗时操作应委托给下半部。

2.3.2 下半部(软中断上下文)

硬中断处理函数通常只做最紧急的工作(如清除中断标志、拷贝数据),然后触发软中断(softirq)或tasklet来执行剩余处理。网络协议栈的NET_RX_SOFTIRQ和NET_TX_SOFTIRQ就是典型的软中断实例。

Linux中断处理完整流程(上半部 + 下半部) 外设中断 GIC仲裁 CPU异常入口 保存现场 handle_arch_irq irq_domain转换 handle_irq_desc 驱动handler 下半部(软中断/tasklet) 虚线表示驱动handler通过触发软中断(如raise_softirq(NET_RX_SOFTIRQ))将剩余工作委托给下半部

2.4 中断处理流程关键代码路径

以下代码展示了从异常入口到驱动handler的核心调用链(以ARM64架构为例,简化版):

// arch/arm64/kernel/irq.c
asmlinkage void __exception_irq_entry handle_arch_irq(struct pt_regs *regs)
{
    unsigned long irqnr;
    // 读取GIC IAR寄存器获取硬件中断号
    irqnr = gic_read_iar();
    // 转换为Linux软件中断号并调用handle_domain_irq
    handle_domain_irq(gic_data.domain, irqnr, regs);
}

// kernel/irq/irqdesc.c
int handle_domain_irq(struct irq_domain *domain, unsigned int hwirq, struct pt_regs *regs)
{
    struct irq_desc *desc;
    unsigned int irq = irq_find_mapping(domain, hwirq); // 映射转换
    desc = irq_to_desc(irq); // 获取irq_desc
    generic_handle_irq_desc(desc); // 调用desc->handle_irq
}

// kernel/irq/chip.c
void handle_level_irq(struct irq_desc *desc)
{
    raw_spin_lock(&desc->lock);
    mask_ack_irq(desc); // 屏蔽并应答中断
    if (unlikely(desc->istate & IRQS_ONESHOT))
        goto out;
    // 遍历action链表执行handler
    handle_irq_event(desc);
    raw_spin_unlock(&desc->lock);
}

// kernel/irq/handle.c
irqreturn_t handle_irq_event(struct irq_desc *desc)
{
    struct irqaction *action;
    irqreturn_t ret = IRQ_NONE;
    // 遍历共享中断的action链表
    for (action = desc->action; action; action = action->next)
        ret |= action->handler(irq, action->dev_id);
    return ret;
}
驱动开发关键点: 驱动通过request_irq()或request_threaded_irq()注册handler。对于网络设备,通常注册一个轻量级硬中断handler,在其中调用napi_schedule()触发NAPI轮询(基于软中断),实现高效收包。

2.5 中断处理流程总结表

阶段 上下文 主要操作 耗时要求 可调用函数
上半部(硬中断) 中断上下文 清除中断标志、拷贝数据、触发下半部 微秒级 writel()、napi_schedule()、tasklet_schedule()
下半部(软中断) 软中断上下文 网络协议处理、数据包分发 毫秒级 skb_copy()、netif_receive_skb()
下半部(tasklet) 软中断上下文 通用延迟处理 毫秒级 kfree()、wake_up()
下半部(workqueue) 进程上下文 可睡眠的复杂操作 无严格限制 mutex_lock()、kmalloc()
性能陷阱: 硬中断处理时间过长会导致中断嵌套丢失、CPU响应延迟增加。网络高吞吐场景下,应优先使用NAPI(基于软中断)而非纯硬中断模式,以减少中断次数、提升吞吐量。