1. 学习目标
- 理解 Linux 中断的作用
理解:
硬件如何通知 CPU
例如:
按键按下
UART 收到数据
网络收到数据
定时器到期
- 理解 Linux 中断处理流程
能够解释完整流程:
Hardware
│
▼
IRQ
│
▼
Interrupt Controller
│
▼
CPU
│
▼
ISR(中断处理函数)
│
▼
Driver
- 能编写基础中断驱动
掌握:
request_irq
free_irq
irq_handler
- 理解 Top Half / Bottom Half
理解:
Top Half
Bottom Half
以及:
tasklet
workqueue
- 能分析驱动中的中断问题
例如:
中断丢失
中断风暴
中断共享
2. 什么是中断
中断是:
硬件通知 CPU 的一种机制
如果没有中断,CPU 必须:
while(1)
{
检查设备
}
这种方式叫:
Polling(轮询)
效率极低
中断方式
设备主动通知 CPU:
设备
│
IRQ信号
│
CPU
CPU 立即处理
3. Linux 中断整体结构
Linux 中断系统结构:
Hardware Device
│
▼
Interrupt Controller
│
▼
IRQ Number
│
▼
Kernel IRQ Subsystem
│
▼
Driver ISR
例如:
GPIO
UART
Network
Timer
4. 中断号(IRQ)
每个中断都有编号:
IRQ number
例如:
IRQ 0 timer
IRQ 1 keyboard
IRQ 14 disk
驱动需要:
注册 IRQ
5. 中断处理函数
驱动必须提供:
ISR(Interrupt Service Routine)
函数格式:
irqreturn_t handler(int irq, void *dev)
示例:
irqreturn_t my_irq_handler(int irq, void *dev_id)
{
printk("interrupt occurred\n");
return IRQ_HANDLED;
}
返回值:
IRQ_HANDLED
IRQ_NONE
6. 中断注册
驱动使用:
request_irq()
注册中断。
函数:
int request_irq(unsigned int irq,
irq_handler_t handler,
unsigned long flags,
const char *name,
void *dev);
参数说明:
示例
request_irq(irq_num,
my_irq_handler,
IRQF_SHARED,
"my_device",
dev);
7. 释放中断
驱动卸载时:
free_irq()
示例:
free_irq(irq_num, dev);
8. 中断执行上下文
中断运行在:
Interrupt Context
特点:
不能睡眠
不能调度
必须快速完成
因此:
不能使用 mutex
不能使用 msleep
9. Top Half / Bottom Half
Linux 将中断处理分为两部分:
Top Half
Bottom Half
原因:
中断必须尽快完成。
Top Half
Top Half 就是:
ISR
特点:
执行很快
只做必要工作
例如:
读取中断状态
清除中断
保存数据
Bottom Half
耗时工作放到:
Bottom Half
例如:
数据处理
协议解析
复杂计算
10. Linux Bottom Half 机制
Linux 提供三种 Bottom Half:
softirq
tasklet
workqueue
softirq
特点:
高性能
内核使用
例如:
网络协议栈
驱动很少直接使用。
tasklet
特点:
基于 softirq
简单易用
示例:
DECLARE_TASKLET(my_tasklet, tasklet_func, data);
调度:
tasklet_schedule(&my_tasklet);
workqueue
特点:
运行在进程上下文
可以睡眠
示例:
schedule_work(&work);
11. 中断处理流程
完整流程:
Hardware interrupt
│
▼
CPU
│
▼
ISR (Top Half)
│
▼
Schedule Bottom Half
│
▼
tasklet / workqueue
│
▼
Driver processing
12. 共享中断
Linux 支持:
共享 IRQ
多个设备共用同一个 IRQ。
注册时需要:
IRQF_SHARED
ISR 必须检查:
是不是自己的中断
示例:
if (!device_interrupt)
return IRQ_NONE;
13. 驱动示例
简单中断驱动:
static irqreturn_t my_irq_handler(int irq, void *dev_id)
{
printk("interrupt\n");
return IRQ_HANDLED;
}
static int __init my_init(void)
{
request_irq(irq_num,
my_irq_handler,
IRQF_SHARED,
"my_irq",
NULL);
return 0;
}
static void __exit my_exit(void)
{
free_irq(irq_num,NULL);
}
14. 驱动常见问题
中断丢失
原因:
没有清除中断状态
中断风暴
原因:
中断一直触发
例如:
GPIO状态未清除
ISR执行太长
问题:
阻塞其他中断
解决:
使用 bottom half
15. 总结
Linux 中断核心结构:
Hardware
│
IRQ
│
CPU
│
ISR (Top Half)
│
Bottom Half
│
Driver
最重要原则:
ISR 必须快速
复杂逻辑放 Bottom Half
16. Q&A
16.1 基础理解
1 为什么需要中断?
2 中断和 polling 的区别?
16.2 中断结构
3 IRQ 是什么?
4 request_irq 做了什么?
16.3 上下文
5 为什么 ISR 不能睡眠?
6 为什么 ISR 不能使用 mutex?
16.4 Bottom Half
7 为什么需要 Top Half / Bottom Half?
8 tasklet 和 workqueue 的区别?