3. 中断处理基础:Linux中断子系统架构、中断类型与上下文

在CSI摄像头数据流中,每一帧图像到达时都会触发中断。理解Linux中断子系统的分层架构、中断类型以及上下文切换规则,是优化实时响应延迟的前提。本节将系统梳理中断处理的基础知识,为后续的线程化中断、CPU亲和性绑定和优先级继承等高级优化打下根基。

3.1 Linux中断子系统整体架构

Linux中断子系统采用分层设计,从上到下依次为:设备驱动层、通用中断层(Generic IRQ Layer)、中断控制器驱动层(IRQ Chip Driver)以及硬件中断控制器。这种分层使得驱动开发者无需关心底层中断控制器的具体实现,只需通过标准API注册中断处理函数即可。

架构要点:通用中断层(kernel/irq/)负责中断号管理、中断流控、中断线程化、中断亲和性设置等核心功能;中断控制器驱动层则封装了GIC(Generic Interrupt Controller)、APIC等硬件差异。
Linux中断子系统分层架构图 Linux中断子系统分层架构 设备驱动层 (Device Driver) request_irq() / devm_request_threaded_irq() 通用中断层 (Generic IRQ Layer) 中断号映射 · 流控处理 · 线程化 · 亲和性 中断控制器驱动层 (IRQ Chip Driver) irq_chip 结构体 · mask/unmask/ack 硬件中断控制器 (GIC / APIC / NVIC) 物理中断线 · 优先级 · 分发器 核心文件 kernel/irq/ include/linux/irq.h

3.2 中断类型:从硬中断到工作队列

Linux将中断处理分为多个层级,以平衡实时性与吞吐量。下表列出了五种核心中断类型及其关键属性:

中断类型 执行上下文 可睡眠 可被新中断抢占 典型用途
硬中断 (Hard IRQ) 中断上下文 否 否(除非设置 IRQF_DISABLED) 极简硬件应答,清除中断标志
软中断 (SoftIRQ) 软中断上下文 否 是(同类型串行) 网络收包、块设备层、定时器
tasklet 软中断上下文(基于 TASKLET_SOFTIRQ) 否 是(同类型串行) 小批量后处理,如 DMA 完成回调
工作队列 (Workqueue) 进程上下文(内核线程) 是 是(由调度器管理) 复杂数据处理、I2C/SPI 通信
中断线程化 (Threaded IRQ) 进程上下文(专用内核线程) 是 是(由调度器管理) 实时性要求高的设备驱动
CSI摄像头优化提示:硬中断中只做必要的寄存器读取和帧起始标记,将MIPI CSI协议解析、图像数据搬运放入中断线程化处理,可显著降低中断关闭时间,提升系统实时性。

3.3 中断上下文与进程上下文

理解两种上下文的区别,是编写正确中断处理程序的前提。中断上下文(Interrupt Context)与进程上下文(Process Context)在可调用API、锁机制、调度行为等方面存在本质差异:

  • 中断上下文:运行在中断栈上,不可睡眠,不可持有互斥锁(mutex),不可触发页面错误,不可访问用户空间。只能使用自旋锁(spinlock)和原子操作。
  • 进程上下文:运行在进程内核栈上,可睡眠,可持有互斥锁,可触发缺页中断,可访问用户空间。可使用信号量(semaphore)和完成量(completion)。
中断上下文与进程上下文对比图 中断上下文 vs 进程上下文 中断上下文 运行栈:中断栈 (per-CPU) 可睡眠:❌ 不可 锁类型:自旋锁 (spinlock) 用户空间访问:❌ 不可 缺页处理:❌ 不可 调度:❌ 不可触发调度 ⚠ 必须极短,否则丢帧 进程上下文 运行栈:进程内核栈 可睡眠:✅ 可以 锁类型:互斥锁 (mutex) 用户空间访问:✅ 可以 缺页处理:✅ 可以 调度:✅ 可主动/被动调度 ✅ 适合复杂图像处理

3.4 中断处理流程与数据流路径

当CSI摄像头硬件产生中断后,数据流依次经过以下阶段:

  1. 硬件中断触发:摄像头完成一行或一帧数据采集,通过MIPI CSI-2接口向SoC发送中断信号。
  2. 中断控制器分发:GIC将中断路由到指定CPU核心,CPU自动保存当前上下文并跳转到中断向量表。
  3. 通用中断层处理:调用 handle_irq_desc(),执行流控处理函数(如 handle_level_irq 或 handle_edge_irq)。
  4. 驱动硬中断处理:执行 request_irq() 注册的顶半部(top half),通常只读取状态寄存器、清除中断标志、将数据标记为“待处理”。
  5. 底半部调度:根据驱动设计,调度软中断、tasklet或唤醒中断线程化处理函数。
  6. 底半部执行:在进程上下文或软中断上下文中完成图像数据搬运、格式转换、帧缓冲提交等耗时操作。
常见陷阱:在硬中断中调用 printk()、kmalloc(GFP_KERNEL) 或 mutex_lock() 会导致内核崩溃或死锁。CSI驱动中尤其要注意不要在顶半部进行图像数据拷贝。

3.5 关键数据结构与API

中断子系统涉及的核心数据结构包括 irq_desc、irq_chip 和 irqaction。以下是一个典型的中断注册代码示例:

/* CSI摄像头中断注册示例 */
static irqreturn_t csi_irq_handler(int irq, void *dev_id)
{
    struct csi_device *csi = (struct csi_device *)dev_id;

    /* 顶半部:只读取状态并清除中断 */
    u32 status = readl(csi->regs + CSI_STATUS_REG);
    writel(status, csi->regs + CSI_STATUS_REG);

    /* 调度中断线程化处理 */
    return IRQ_WAKE_THREAD;
}

static irqreturn_t csi_irq_thread_fn(int irq, void *dev_id)
{
    struct csi_device *csi = (struct csi_device *)dev_id;

    /* 底半部:在进程上下文中搬运图像数据 */
    csi_frame_capture(csi);
    csi_dma_submit(csi);

    return IRQ_HANDLED;
}

static int csi_probe(struct platform_device *pdev)
{
    int irq = platform_get_irq(pdev, 0);
    /* 注册中断线程化处理 */
    return devm_request_threaded_irq(&pdev->dev, irq,
                                     csi_irq_handler,   /* 顶半部 */
                                     csi_irq_thread_fn, /* 底半部 */
                                     IRQF_TRIGGER_RISING,
                                     "csi_camera", csi);
}
优化要点:使用 devm_request_threaded_irq() 替代传统的 request_irq(),将耗时操作放入线程化底半部,硬中断执行时间可控制在微秒级,避免阻塞其他中断。

3.6 中断类型选择策略

针对CSI摄像头数据流的不同处理阶段,推荐的中断类型选择如下:

处理阶段 推荐中断类型 原因
硬件寄存器应答 硬中断 (Hard IRQ) 必须立即执行,不可延迟
DMA描述符更新 硬中断 或 软中断 操作极短,需原子性
图像数据搬运(memcpy) 中断线程化 (Threaded IRQ) 耗时较长,可睡眠等待DMA
图像格式转换 工作队列 (Workqueue) 复杂计算,可被调度器管理
帧缓冲提交到V4L2 工作队列 或 内核线程 可能涉及用户空间通知

通过合理选择中断类型,CSI摄像头驱动可以在保证不丢帧的前提下,将中断关闭时间压缩到最小,为实时操作系统(RT-Linux、PREEMPT_RT)提供友好的调度环境。