2. Linux中断子系统架构:中断控制器(GIC)、中断描述符、中断处理流程(上半部/下半部)

在嵌入式实时系统中,中断是CPU响应外部或内部异步事件的硬件机制。Linux内核的中断子系统设计精巧,旨在平衡实时响应与通用处理的需求。本章节深入剖析中断子系统的三大核心组件:中断控制器(以GIC为例)、中断描述符管理以及经典的中断处理流程(上半部/下半部)。

2.1 中断控制器:GIC (Generic Interrupt Controller)

GIC是ARM架构中标准的中断控制器,负责管理来自多个中断源的中断请求,并将其路由到合适的CPU核心。理解GIC是掌握Linux中断子系统的基础。

核心概念: GIC的主要功能包括中断使能/禁能、中断优先级管理、中断分发与路由、以及中断状态跟踪(pending、active、active and pending)。

2.1.1 GIC架构分层

GIC逻辑上分为两个主要模块:分发器(Distributor)和CPU接口(CPU Interface)。

  • 分发器(Distributor): 全局控制单元。负责配置每个中断的优先级、目标CPU、触发方式(电平/边沿),并控制中断的使能与禁能。它接收所有中断源,并根据配置将最高优先级的中断发送给CPU接口。
  • CPU接口(CPU Interface): 每个CPU核心拥有一个独立的CPU接口。它负责与CPU核心交互,处理中断优先级屏蔽、中断抢占逻辑,并向CPU核心发送IRQ/FIQ信号。
GIC架构分层与中断路由 GIC架构分层与中断路由 中断源 0..N 分发器 优先级 / 路由 / 使能 CPU接口 0 优先级屏蔽 / 抢占 CPU接口 1 优先级屏蔽 / 抢占 CPU 0 CPU 1 说明: GICv2/v3/v4架构中,分发器统一管理所有中断配置,CPU接口负责与具体核心交互。 中断路由策略(如1:N、1:1)由分发器根据亲和性配置决定。

2.1.2 中断类型与ID

GIC管理的中断分为三种类型,每种类型在系统中扮演不同角色:

中断类型 ID范围 描述 典型用途
SGI (Software Generated Interrupt) 0 - 15 软件触发的中断,用于核间通信(IPI)。 调度、TLB刷新、CPU唤醒
PPI (Private Peripheral Interrupt) 16 - 31 私有外设中断,每个CPU核心独有。 本地定时器、性能监测单元
SPI (Shared Peripheral Interrupt) 32 - 1019 共享外设中断,可路由到任意CPU核心。 UART、I2C、网络控制器
工程实践提示: 在中断线程化移植中,SPI类型的设备中断是主要关注对象。合理配置SPI的目标CPU亲和性(/proc/irq/<irq_num>/smp_affinity)可以显著提升多核系统的负载均衡。

2.2 中断描述符:irq_desc

在Linux内核中,每个中断源(无论是硬件中断还是软件中断)都由一个中断描述符(struct irq_desc)表示。它是中断子系统的核心数据结构,串联起硬件中断与软件处理逻辑。

2.2.1 irq_desc结构体核心字段

irq_desc结构体定义在include/linux/irqdesc.h中,其关键字段如下:

struct irq_desc {
    struct irq_common_data  irq_common_data;
    struct irq_data         irq_data;
    unsigned int __percpu   *kstat_irqs;
    irq_flow_handler_t      handle_irq;      // 中断流处理函数
    struct irqaction        *action;         // 中断动作链表
    unsigned int            status_use_accessors;
    struct list_head        pending;         // 待处理中断链表
    struct irq_affinity_notify *affinity_notify;
    int                     parent_irq;
    struct module           *owner;
    const char              *name;
    wait_queue_head_t       wait_for_threads; // 等待线程完成
    struct proc_dir_entry   *dir;
    struct cpumask          *percpu_enabled;
    // ... 其他字段
};

2.2.2 中断描述符管理机制

内核通过一个全局的irq_desc数组(或基数树)来管理所有中断描述符。中断号(IRQ number)是访问该数组的索引。

  • 分配与初始化: 在系统启动或设备驱动注册时,通过irq_alloc_descs()或irq_create_mapping()分配并初始化中断描述符。
  • 中断流处理: handle_irq字段指向一个高层的中断流处理函数(如handle_level_irq、handle_edge_irq),它负责处理中断控制器的硬件交互(如应答、屏蔽)。
  • 中断动作链表: action字段指向一个struct irqaction链表。每个irqaction代表一个注册的中断处理程序(上半部)。多个处理程序可以共享同一个中断号。
中断描述符与中断动作链表结构 中断描述符与中断动作链表结构 struct irq_desc irq_data.hwirq: 32 handle_irq: handle_level_irq status: IRQ_ACTIVE action: -------> name: "uart_irq" wait_for_threads: ... struct irqaction handler: uart_isr flags: IRQF_SHARED struct irqaction handler: uart_aux_isr thread_fn: uart_thread 关键点: 1. 一个irq_desc可以挂载多个irqaction,实现中断共享。 2. thread_fn字段是中断线程化的入口,与handler(上半部)分离。

2.3 中断处理流程:上半部与下半部

为了在中断上下文中最小化关中断时间,Linux将中断处理拆分为两个阶段:上半部(Top Half)和下半部(Bottom Half)。这是实时系统设计中的核心思想。

2.3.1 上半部(Top Half)

上半部在中断上下文中执行,具有以下特点:

  • 原子性: 执行期间,当前CPU核心的中断被屏蔽(取决于中断控制器的优先级设置)。
  • 快速性: 必须快速完成,通常只做最必要的硬件操作,如读取状态寄存器、清除中断标志、将数据拷贝到内存缓冲区。
  • 不可阻塞: 不能调用可能导致睡眠的函数(如mutex_lock、kmalloc(GFP_KERNEL))。

2.3.2 下半部(Bottom Half)

下半部在稍后的时间点执行,此时中断已经重新使能。它负责处理上半部遗留的、不那么紧急的工作。Linux提供了多种下半部机制:

机制 执行上下文 特点 适用场景
软中断 (Softirq) 软中断上下文 静态分配,可重入,同类型软中断可在多核并行执行。 网络收发、块设备层、高吞吐场景
Tasklet 软中断上下文 基于软中断实现,同一Tasklet串行执行,简单易用。 一般设备驱动下半部
工作队列 (Workqueue) 进程上下文 允许睡眠,由内核线程执行,可调度。 需要阻塞操作的下半部(如I2C/SPC传输)
中断线程化 (Threaded IRQ) 进程上下文 为每个中断创建一个内核线程,完全在进程上下文处理。 实时性要求高、处理逻辑复杂的设备

2.3.3 完整中断处理流程

从硬件中断信号触发到软件处理完成,Linux内核经历以下关键步骤:

  1. 硬件中断到达: 外设向GIC发送中断信号。
  2. GIC分发与路由: GIC根据优先级和亲和性配置,将中断路由到目标CPU核心。
  3. CPU异常向量: CPU核心响应IRQ异常,跳转到内核预置的异常向量表(vectors)。
  4. 进入中断上下文: 内核保存当前上下文,调用handle_arch_irq或gic_handle_irq。
  5. 调用流处理函数: 根据中断号找到对应的irq_desc,调用其handle_irq(如handle_level_irq)。
  6. 执行上半部: 遍历action链表,执行所有注册的handler函数。
  7. 标记下半部: 如果handler返回IRQ_WAKE_THREAD,则唤醒对应的中断线程(线程化中断);或者触发软中断/Tasklet。
  8. 退出中断上下文: 恢复上下文,使能中断。
  9. 执行下半部: 在适当的时机(如do_softirq、内核线程调度),执行下半部处理函数。
Linux中断处理完整流程 Linux中断处理完整流程 1. 硬件中断到达 2. GIC分发与路由 3. CPU异常向量表 4. 调用流处理函数 5. 执行上半部 (handler) 6a. 唤醒中断线程 6b. 触发软中断/Tasklet 说明:步骤5(上半部)执行期间,当前CPU中断被屏蔽。步骤6a/6b在进程上下文或软中断上下文执行,中断已使能。
实时性陷阱: 传统的上半部/下半部机制中,上半部执行时间不可控,且软中断/Tasklet的优先级高于普通进程,可能导致实时任务被延迟。中断线程化将下半部移至进程上下文,允许被实时调度器管理,是解决此问题的关键手段。

2.4 本章小结

本章深入剖析了Linux中断子系统的三大支柱:

  • GIC中断控制器: 作为硬件中枢,负责中断的收集、优先级排序与分发。理解SGI/PPI/SPI的分类是进行中断亲和性调优的基础。
  • 中断描述符(irq_desc): 内核中中断的“身份证”,串联起硬件中断号与软件处理逻辑。其action链表和thread_fn字段是实现中断线程化的关键数据结构。
  • 中断处理流程: 上半部快速处理硬件事件,下半部完成剩余工作。下半部机制的演进(从软中断到中断线程化)反映了实时性需求的不断提升。