3. 中断处理基础:Linux中断子系统架构、中断类型与上下文
在CSI摄像头数据流中,每一帧图像到达时都会触发中断。理解Linux中断子系统的分层架构、中断类型以及上下文切换规则,是优化实时响应延迟的前提。本节将系统梳理中断处理的基础知识,为后续的线程化中断、CPU亲和性绑定和优先级继承等高级优化打下根基。
3.1 Linux中断子系统整体架构
Linux中断子系统采用分层设计,从上到下依次为:设备驱动层、通用中断层(Generic IRQ Layer)、中断控制器驱动层(IRQ Chip Driver)以及硬件中断控制器。这种分层使得驱动开发者无需关心底层中断控制器的具体实现,只需通过标准API注册中断处理函数即可。
架构要点:通用中断层(
kernel/irq/)负责中断号管理、中断流控、中断线程化、中断亲和性设置等核心功能;中断控制器驱动层则封装了GIC(Generic Interrupt Controller)、APIC等硬件差异。
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)。
3.4 中断处理流程与数据流路径
当CSI摄像头硬件产生中断后,数据流依次经过以下阶段:
- 硬件中断触发:摄像头完成一行或一帧数据采集,通过MIPI CSI-2接口向SoC发送中断信号。
- 中断控制器分发:GIC将中断路由到指定CPU核心,CPU自动保存当前上下文并跳转到中断向量表。
- 通用中断层处理:调用
handle_irq_desc(),执行流控处理函数(如handle_level_irq或handle_edge_irq)。 - 驱动硬中断处理:执行
request_irq()注册的顶半部(top half),通常只读取状态寄存器、清除中断标志、将数据标记为“待处理”。 - 底半部调度:根据驱动设计,调度软中断、tasklet或唤醒中断线程化处理函数。
- 底半部执行:在进程上下文或软中断上下文中完成图像数据搬运、格式转换、帧缓冲提交等耗时操作。
常见陷阱:在硬中断中调用
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)提供友好的调度环境。