在Linux内核开发中,中断处理是驱动开发的核心环节。中断上下文分为两个关键部分:
1. 中断上文(Top Half) ⚡
kmalloc(GFP_KERNEL)
)2. 中断下文(Bottom Half) ⏳
tasklet
等延迟执行机制// 注册中断处理程序
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags, const char *name, void *dev);
// 注销中断
void free_irq(unsigned int irq, void *dev_id);
参数 | 说明 |
---|---|
irq |
中断号(通过platform_get_irq() 获取) |
handler |
中断处理函数(原型:irqreturn_t (*)(int, void *) ) ️ |
flags |
重要标志:IRQF_SHARED (共享中断)、IRQF_TRIGGER_RISING (触发类型) |
name |
/proc/interrupts 中显示的名称 |
dev_id |
设备标识符,共享中断时必须唯一 |
struct tasklet_struct {
struct tasklet_struct *next; // 链表指针 ⛓️
unsigned long state; // 状态位(TASKLET_STATE_SCHED等)
atomic_t count; // 禁用计数器
void (*func)(unsigned long); // 处理函数 ️
unsigned long data; // 用户数据
};
静态初始化
// 默认激活状态 ✅
DECLARE_TASKLET(my_tasklet, tasklet_handler, 0x1234);
// 初始禁用状态 ⛔
DECLARE_TASKLET_DISABLED(my_tasklet_disabled, handler, data);
动态初始化
struct tasklet_struct my_tasklet;
tasklet_init(&my_tasklet, handler_func, 0x5678); // 动态绑定
函数 | 作用 |
---|---|
tasklet_schedule() |
调度tasklet执行 |
tasklet_disable() |
禁用tasklet(可嵌套调用) ⏸️ |
tasklet_enable() |
启用tasklet ▶️ |
tasklet_kill() |
永久终止tasklet,确保不再运行 ⚰️ |
#include
#include
static struct tasklet_struct my_tasklet;
static int irq_num;
// Tasklet处理函数 ️
void my_tasklet_handler(unsigned long data) {
printk("Processing tasklet, data: %lu\n", data);
/* 执行耗时操作(如DMA启动、数据处理) ⚡ */
}
// 中断处理函数 ⚡
irqreturn_t irq_handler(int irq, void *dev) {
/* 1. 快速处理硬件操作 */
iowrite32(0xFF, reg_addr);
/* 2. 调度tasklet */
tasklet_schedule(&my_tasklet);
return IRQ_HANDLED;
}
static int __init mydrv_init(void) {
// 获取中断号
irq_num = platform_get_irq(pdev, 0);
// 注册中断
request_irq(irq_num, irq_handler, IRQF_SHARED, "mydrv", &mydrv);
// 初始化tasklet ⚙️
tasklet_init(&my_tasklet, my_tasklet_handler, 0x1234);
return 0;
}
static void __exit mydrv_exit(void) {
free_irq(irq_num, &mydrv); // 注销中断
tasklet_kill(&my_tasklet); // 安全终止taskelt ⚠️
}
module_init(mydrv_init);
module_exit(mydrv_exit);
原子性保障
spin_lock_irqsave
)保护共享资源 ️性能优化
// 在频繁中断场景下使用NAPI机制
if (data_ready) {
tasklet_schedule(&rx_tasklet);
disable_irq_nosync(irq_num); // 减少中断风暴 ️
}
调试技巧
# 查看中断统计
cat /proc/interrupts
# 追踪tasklet状态
echo 1 > /sys/kernel/debug/tracing/events/irq/enable
常见错误 ❌
tasklet_kill
导致死锁:禁止在中断上下文调用 dev_id
区分设备 场景 | 推荐方案 |
---|---|
高频中断(网络驱动) | tasklet +NAPI组合 |
需要睡眠的操作 | 工作队列(workqueue) |
实时性要求高 | 线程化中断(request_threaded_irq)⏱️ |
通过合理运用中断上下文机制,开发者可以在保证系统实时性的同时,高效完成硬件交互和数据处理任务。
tasklet_kill
的误用是新手常见雷区 ,务必在进程上下文操作!掌握这些核心原理,你将能构建出稳定高效的 Linux 内核驱动!