Linux驱动开发 中断上下文机制详解 中断上下文API使用详解
在 Linux 驱动开发中,中断处理被分为上半部(Top Half)和下半部(Bottom Half)。这是为了减少中断处理对系统响应时间的影响,因为中断通常会打断系统正常执行的代码,快速响应中断是必要的,但不需要立刻处理所有相关工作。通过分离上半部和下半部,能够将时间敏感的工作放在上半部,非时间敏感的工作放在下半部,从而提高系统效率。
上半部(Top Half)
上半部是中断发生时,立即执行的部分,通常是中断处理程序。在中断发生后,处理器会调用注册的中断处理函数。在上半部执行时,通常会完成以下任务:
- 快速读取硬件寄存器或数据,以防数据丢失。
- 清除或禁用中断源,防止中断重复触发。
- 安排需要较长时间的任务(例如数据处理)到下半部执行。
上半部的特点:
- 运行在中断上下文中,禁止中断和进程切换。
- 执行时间应该尽量短。
- 不能睡眠或调用可能导致睡眠的函数。
下半部(Bottom Half)
下半部是将非时间紧迫的任务延迟执行的机制。下半部是在普通的进程上下文中执行,因此不会阻塞其他中断或任务。Linux 提供了几种机制来实现下半部,常见的有以下几种:
-
软中断(SoftIRQ):
- 是一种较为底层的下半部机制,通常在网络子系统或块设备驱动中使用。
- 软中断是在硬件中断上下文中触发的,但可以在稍后处理,减少中断处理的时间。
-
tasklet:
- 是基于软中断的上层抽象,较为简单易用。
- 适合用于非对称多处理器(SMP)环境,因为一个 tasklet 不能在多个 CPU 上并发执行。
-
工作队列(Workqueue):
- 工作队列允许将任务放到系统的工作队列中,由内核线程处理。
- 可以在进程上下文中执行,因此可以进行休眠或调用可能导致休眠的函数,适用于需要较长时间的任务处理。
下半部的特点:
- 在进程上下文中执行,因此可以使用阻塞的函数。
- 不会阻塞其他中断,可以长时间执行较为复杂的任务。
使用例子
为了更好地理解 Linux 驱动开发中断上半部和下半部的概念,下面提供一个完整的例程,展示如何使用上半部和下半部(使用 tasklet)。
示例代码:上半部和下半部(tasklet)
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#define IRQ_NUM 19 // 假设中断号为19
// tasklet 函数,用于下半部
void my_tasklet_func(unsigned long data);
DECLARE_TASKLET(my_tasklet, my_tasklet_func, 0);
// 上半部中断处理程序
irqreturn_t my_interrupt_handler(int irq, void *dev_id) {
// 上半部快速处理中断
printk(KERN_INFO "Interrupt %d: Top half executed\n", irq);
// 调度 tasklet (下半部)
tasklet_schedule(&my_tasklet);
return IRQ_HANDLED;
}
// 下半部 tasklet 函数
void my_tasklet_func(unsigned long data) {
// 下半部处理相对耗时任务
printk(KERN_INFO "Tasklet: Bottom half executed\n")