1. 中断处理概述
1.1 中断的基本概念
中断(Interrupt)是硬件或软件向处理器发送的一种异步信号,用于通知处理器发生了需要立即关注的事件。中断机制是现代操作系统响应外部事件的核心手段,它允许CPU在等待事件时执行其他任务,从而大幅提升系统吞吐量。
核心要点:中断的本质是“打断”CPU当前正在执行的指令流,强制跳转到一个预先定义好的处理函数(中断服务例程,ISR)中去执行。
根据中断来源,可将其分为以下两类:
| 中断类型 | 触发源 | 典型示例 |
|---|---|---|
| 硬件中断(Hardware IRQ) | 外设控制器(网卡、磁盘、定时器) | 网卡收到数据包、磁盘完成读写 |
| 软件中断(Software Interrupt) | CPU指令主动触发(int指令、异常) | 系统调用、缺页异常、除零错误 |
在Linux内核中,中断处理的核心数据结构是 irq_desc,每个中断号对应一个描述符,其中包含了该中断的触发方式、处理函数链表、状态标志等信息。
// include/linux/irqdesc.h (简化结构)
struct irq_desc {
struct irq_data irq_data;
unsigned int __percpu *kstat_irqs;
irq_flow_handler_t handle_irq;
struct irqaction *action; // 中断处理函数链表
unsigned int status_use_accessors;
const char *name;
} ____cacheline_internodealigned_in_smp;
1.2 中断上下文(Interrupt Context)
当CPU进入中断处理时,执行环境从“进程上下文”切换到“中断上下文”。这是理解中断底半部机制的基础。
关键区分:中断上下文与进程上下文的最大区别在于——中断上下文中不可睡眠,不能调用可能引起阻塞的函数(如
kmalloc(GFP_KERNEL)、mutex_lock()),因为中断上下文没有关联的进程控制块(task_struct)可供调度器切换。
中断上下文具有以下特征:
- 不可睡眠:中断处理程序必须快速执行,不能等待资源。
- 不可被相同中断号抢占:同一IRQ线上的中断默认是禁止嵌套的。
- 拥有独立的栈:每个CPU在初始化时会分配一个中断栈(通常为4KB或8KB),与进程内核栈分离。
- 访问限制:只能使用
GFP_ATOMIC标志分配内存。
以下SVG图展示了中断发生时CPU上下文的切换流程:
1.3 上半部(Top Half)与下半部(Bottom Half)的分工原则
中断处理程序必须快速执行,否则会导致系统响应延迟甚至丢数据。然而,很多设备在中断发生后需要完成大量处理工作(如网络协议栈解析、数据拷贝)。为此,Linux内核将中断处理拆分为两个阶段:
| 特性 | 上半部(Top Half) | 下半部(Bottom Half) |
|---|---|---|
| 执行时机 | 中断触发后立即执行 | 稍后调度执行(软中断/工作队列) |
| 中断状态 | 关中断(当前IRQ线) | 开中断(可被其他中断打断) |
| 可睡眠 | ❌ 不可睡眠 | 取决于机制(tasklet不可,工作队列可) |
| 执行时长 | 极短(微秒级) | 相对较长(毫秒级) |
| 典型操作 | 读取硬件状态、清除中断标志、拷贝数据 | 协议处理、数据解析、唤醒等待进程 |
常见误区:初学者常将大量处理逻辑直接放在上半部中,导致中断延迟过高、丢包甚至系统崩溃。正确的做法是:上半部只做最紧急的硬件操作,其余工作推迟到下半部执行。
Linux内核提供了多种下半部机制,按优先级和复杂度排列如下:
- 软中断(SoftIRQ):内核预定义的有限集合(如NET_RX_SOFTIRQ、TIMER_SOFTIRQ),优先级高,不可睡眠。
- tasklet:基于软中断实现,动态注册,同一tasklet在同一CPU上串行执行。
- 工作队列(Workqueue):运行在进程上下文,可睡眠,适合耗时任务。
以下SVG图展示了上半部与下半部的协作关系:
1.4 中断处理的设计原则总结
在实际的嵌入式驱动开发中,遵循以下原则可以有效避免竞态条件和性能问题:
- 最小化原则:上半部只做必须立即完成的操作,如读取硬件FIFO、清除中断挂起位。
- 延迟原则:将数据解析、协议处理、I/O操作等推迟到下半部。
- 同步原则:上下半部之间通过原子操作、自旋锁或内存屏障保护共享数据。
- 优先级原则:紧急程度高的下半部使用软中断或tasklet,可容忍延迟的使用工作队列。
调试提示:在排查中断相关问题时,首先检查上半部是否执行了过多操作。可以通过
/proc/interrupts 查看中断计数,使用 ftrace 跟踪中断处理耗时。如果发现中断处理时间超过100微秒,通常意味着需要将部分工作移到下半部。