1. 中断基础概念

中断(Interrupt)是嵌入式系统中最重要的硬件-软件通信机制之一。它允许外设或处理器内部事件在发生时立即通知CPU,而无需CPU持续轮询(Polling)设备状态。理解中断的基础概念是编写正确、高效Linux设备驱动的前提。

核心观点: 中断机制将CPU从“主动查询”变为“被动响应”,极大提升了系统实时性和CPU利用率。在Linux驱动中,中断申请(request_irq)是设备驱动初始化的关键步骤。

1.1 中断的定义

中断是一种硬件机制,当某个事件(如按键按下、数据接收完成、定时器溢出)发生时,硬件会向CPU发送一个电信号(中断请求线,IRQ Line)。CPU在完成当前指令后,会暂停正在执行的程序,保存上下文,跳转到一个预先定义好的中断服务程序(Interrupt Service Routine, ISR)去处理该事件。处理完成后,CPU恢复之前被中断的程序继续执行。

中断处理流程可概括为以下步骤:

  1. 事件触发: 外设或内部模块产生中断事件。
  2. 信号发送: 中断控制器(如GIC、NVIC)将中断信号路由到CPU核心。
  3. CPU响应: CPU保存当前执行现场(PC、寄存器等),进入中断模式。
  4. 向量查找: CPU根据中断号查找中断向量表,获取ISR入口地址。
  5. ISR执行: 执行中断服务程序,处理事件(如读取数据、清除中断标志)。
  6. 恢复现场: 执行中断返回指令,恢复被中断的程序继续运行。
关键术语: 中断号(IRQ Number)、中断向量表(Vector Table)、中断服务程序(ISR)、中断控制器(Interrupt Controller)、中断上下文(Interrupt Context)。
中断处理流程示意图 外设事件触发 中断控制器路由 CPU保存现场 查找中断向量表 ISR执行处理 恢复现场继续执行 硬件自动完成 软件执行

1.2 中断类型:IRQ 与 FIQ

在ARM架构(特别是Cortex-A系列)中,中断分为两种类型:IRQ(Interrupt Request) 和 FIQ(Fast Interrupt Request)。两者在优先级、响应速度和资源占用上存在显著差异。

特性 IRQ(普通中断) FIQ(快速中断)
优先级 较低(FIQ优先级高于IRQ) 较高(可抢占IRQ)
向量表位置 0x00000018(ARM模式) 0x0000001C(ARM模式)
寄存器保存 需保存/恢复所有被破坏的寄存器 有专用寄存器(r8~r12),减少压栈开销
响应延迟 相对较长 更短(快速响应)
典型用途 普通外设中断(UART、I2C、SPI等) 高实时性需求(DMA、定时器、紧急事件)
中断嵌套 可被FIQ抢占 不可被IRQ抢占
注意: 在Linux内核中,绝大多数设备驱动使用IRQ。FIQ通常保留给对延迟极度敏感的场景(如音频DMA、实时控制)。在设备树中,中断类型通过中断控制器驱动和中断描述符的flags字段来区分。
IRQ 与 FIQ 响应对比 IRQ 事件触发 保存所有寄存器 执行ISR FIQ 事件触发 使用专用寄存器 快速执行ISR 延迟对比 寄存器开销差异 FIQ 响应速度更快 可抢占 IRQ

1.3 中断在嵌入式系统中的作用

中断机制是嵌入式系统实时性和多任务处理能力的基石。在Linux驱动的上下文中,中断的作用体现在以下几个方面:

  • 提高CPU利用率: 无需CPU轮询等待外设状态,CPU可以在等待期间执行其他任务或进入低功耗模式。
  • 实时响应: 对时间敏感的事件(如电机编码器脉冲、网络数据包到达)可以立即得到处理。
  • 事件驱动编程: 驱动代码以“事件触发-处理”的模式组织,逻辑清晰,耦合度低。
  • 低功耗支持: 系统可以在空闲时进入休眠,中断作为唤醒源将CPU从休眠中唤醒。
  • 硬件抽象: 中断控制器和中断号在设备树中描述,驱动通过标准API(如request_irq)申请中断,与具体硬件解耦。
驱动开发视角: 在设备树中,中断通过 interrupts 属性和 interrupt-parent 属性描述。驱动通过 platform_get_irq() 或 of_irq_get() 获取中断号,然后调用 request_irq() 注册中断处理函数。这是后续章节的核心内容。
中断在嵌入式系统中的作用 中断机制 核心调度器 提高CPU利用率 实时响应 事件驱动编程 低功耗唤醒 中断是连接硬件事件与软件处理的桥梁

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

理解中断上下文(Interrupt Context)与进程上下文(Process Context)的区别,对于编写安全的驱动代码至关重要。

特性 进程上下文 中断上下文
可睡眠 是(可以调用 msleep、wait_event 等) 否(禁止睡眠,不能调用可能阻塞的函数)
持有锁 可以使用互斥锁(mutex) 只能使用自旋锁(spinlock)
访问用户空间 可以(copy_from_user / copy_to_user) 禁止(无法访问用户空间)
调度 可以被调度器抢占 不可被调度(除非中断嵌套)
典型操作 文件读写、系统调用、内存分配 清除中断标志、拷贝FIFO数据、唤醒等待队列
驱动开发铁律: 中断服务程序(ISR)中绝对不能调用可能导致睡眠的函数(如 kmalloc(GFP_KERNEL)、mutex_lock()、copy_from_user())。违反此规则会导致内核崩溃或死锁。如果需要执行耗时操作,应使用底半部机制(Tasklet、Workqueue、 threaded IRQ)。

1.5 中断号与中断控制器

在嵌入式系统中,中断号(IRQ Number)是标识每个中断源的唯一编号。中断控制器(如ARM GIC、TI OMAP INTC)负责管理多个中断源的路由、优先级和使能控制。

在设备树中,中断控制器通过以下属性描述:

  • interrupt-controller:空属性,标记该节点为中断控制器。
  • #interrupt-cells:指定中断描述符需要的单元数(通常为2:中断号 + 触发类型)。
  • interrupt-parent:指向父中断控制器的phandle。

设备节点通过 interrupts 属性声明自己使用的中断:

// 设备树示例:UART设备中断描述
uart0: serial@10000000 {
    compatible = "vendor,uart";
    reg = <0x10000000 0x1000>;
    interrupt-parent = <&gic>;       // 指向GIC中断控制器
    interrupts = <0 33 4>;           // SPI类型,中断号33,高电平触发
};
提示: 中断号的映射规则因中断控制器而异。对于ARM GIC,第一个字段表示中断类型(0=PPI,1=SPI),第二个字段是中断号(SPI从32开始),第三个字段是触发类型(1=上升沿,4=高电平)。这些细节将在后续章节的设备树属性解析中详细展开。

通过本节的学习,你已经掌握了中断的基本概念、IRQ/FIQ的区别、中断在系统中的作用以及中断上下文的约束。这些知识是后续学习中断申请和设备树中断属性解析的基础。