Linux 中断处理机制详解:上下半部、内核线程与中断线程化
一、概述
Linux 中断处理是操作系统响应硬件事件的核心机制。由于中断处理需要快速响应,但某些中断处理又需要较长时间,因此 Linux 采用了中断上下半部机制。本文将详细介绍中断处理的演进历程、实现方式以及现代的中断线程化技术。
二、中断处理的基本概念
2.1 中断处理的挑战
硬件中断发生
↓
中断处理程序 (ISR)
↓
问题:
1. 必须快速执行(禁止中断期间)
2. 不能睡眠(atomic context)
3. 某些任务耗时较长(如 I/O 操作)
4. 可能阻塞其他中断
核心矛盾:
- 中断处理必须快速完成
- 实际工作可能很耗时
2.2 中断上下文的限制
c
// 中断上下文中的限制
void irq_handler(int irq, void *dev_id)
{
// ✓ 可以做的:
// - 读写寄存器
// - 操作硬件
// - 使用 spinlock
// - 调用 schedule_work()
// ✗ 不能做的:
// - 睡眠 (msleep, wait_event)
// - 使用 mutex
// - 调用可能睡眠的函数
// - 长时间循环等待
}
三、中断上下半部机制
3.1 设计理念
┌─────────────────────────────────────┐
│ 中断上半部 (Top Half) │
│ - 快速执行 │
│ - 在中断上下文中运行 │
│ - 禁止中断 │
│ - 做最少必要的工作 │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 中断下半部 (Bottom Half) │
│ - 延迟执行 │
│ - 在进程上下文中运行(部分) │
│ - 允许中断 │
│ - 完成耗时的实际工作 │
└─────────────────────────────────────┘
3.2 上半部实现
c
#include <linux/interrupt.h>
// 上半部:中断处理函数
static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
struct my_device *dev = dev_id;
u32 status;
// 1. 读取中断状态
status = readl(dev->base + STATUS_REG);
// 2. 清除中断标志(快速操作)
writel(status, dev->base + STATUS_REG);
// 3. 如果没有需要处理的中断
if (!(status & IRQ_MASK))
return IRQ_NONE;
// 4. 保存必要数据
dev->irq_status = status;
// 5. 调度下半部执行
schedule_work(&dev->work);
// 6. 返回,表示中断已处理
return IRQ_HANDLED;
}
// 注册中断
int my_device_probe(struct platform_device *pdev)
{
struct my_device *dev;
int irq, ret;
irq = platform_get_irq(pdev, 0);
ret = request_irq(irq, my_interrupt_handler,
IRQF_SHARED, "my-device", dev);
if (ret) {
dev_err(&pdev->dev, "Failed to request IRQ\n");
return ret;
}
return 0;
}
四、中断下半部的实现方式
4.1 Softirq(软中断)
特点:
- 最底层的下半部机制
- 编译时静态分配
- 在中断上下文中运行(软中断上下文)
- 同一类型的软中断可在多个 CPU 并行执行
c
// 软中断类型定义(内核已定义)
enum
{
HI_SOFTIRQ=0, // 高优先级 tasklet
TIMER_SOFTIRQ, // 定时器
NET_TX_SOFTIRQ, // 网络发送
NET_RX_SOFTIRQ, // 网络接收
BLOCK_SOFTIRQ, // 块设备
IRQ_POLL_SOFTIRQ, // IO poll
TASKLET_SOFTIRQ, // 普通 tasklet
SCHED_SOFTIRQ, // 调度
HRTIMER_SOFTIRQ, // 高精度定时器
RCU_SOFTIRQ, // RCU
NR_SOFTIRQS
};
// 软中断处理函数示例(网络子系统)
static void net_rx_action(struct softirq_action *h)
{
struct list_head *list = this_cpu_ptr(&softnet_data.poll_list);
while (!list_empty(list)) {
struct napi_struct *n = list_first_entry(list, struct napi_struct, poll_list);
// 处理网络数据包
n->poll(n, weight);
}
}
// 触发软中断
void raise_softirq(unsigned int nr)
{
// 标记软中断待处理
or_softirq_pending(1UL << nr);
}
4.2 Tasklet
特点:
- 基于软中断实现
- 动态分配
- 同一个 tasklet 不会并行执行(但不同 tasklet 可以)
- 在中断上下文中运行
c
#include <linux/interrupt.h>
struct my_device {
struct tasklet_struct tasklet;
void __iomem *base;
u32 data;
};
// Tasklet 处理函数
static void my_tasklet_handler(unsigned long data)
{
struct my_device *dev = (struct my_device *)data;
// 这里可以做耗时的工作
// 但仍然不能睡眠(仍在中断上下文)
// 处理保存的数据
printk("Processing data: 0x%x\n", dev->data);
// 访问硬件
u32 result = readl(dev->base + DATA_REG);
// 进行复杂计算
// ...
}
// 中断上半部
static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
struct my_device *dev = dev_id;
// 读取数据
dev->data = readl(dev->base + DATA_REG);
// 调度 tasklet
tasklet_schedule(&dev->tasklet);
return IRQ_HANDLED;
}
// 初始化
static int my_device_probe(struct platform_device *pdev)
{
struct my_device *dev;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
// 初始化 tasklet
tasklet_init(&dev->tasklet, my_tasklet_handler, (unsigned long)dev);
// 注册中断
request_irq(irq, my_interrupt_handler, 0, "my-device", dev);
return 0;
}
// 清理
static int my_device_remove(struct platform_device *pdev)
{
struct my_device *dev = platform_get_drvdata(pdev);
// 杀死 tasklet
tasklet_kill(&dev->tasklet);
free_irq(dev->irq, dev);
return 0;
}
4.3 工作队列 (Work Queue)
特点:
- 在进程上下文中运行(可以睡眠)
- 可以使用 mutex、msleep 等
- 适合需要睡眠的操作
c
#include <linux/workqueue.h>
struct my_device {
struct work_struct work;
void __iomem *base;
u32 data;
};
// 工作队列处理函数
static void my_work_handler(struct work_struct *work)
{
struct my_device *dev = container_of(work, struct my_device, work);
// 这里可以做任何操作,包括睡眠
printk("Work handler: processing data 0x%x\n", dev->data);
// 可以睡眠
msleep(10);
// 可以使用 mutex
mutex_lock(&dev->lock);
// ...
mutex_unlock(&dev->lock);
// 可以进行 I/O 操作
i2c_transfer(dev->client->adapter, msgs, 2);
}
// 中断上半部
static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
struct my_device *dev = dev_id;
// 读取数据
dev->data = readl(dev->base + DATA_REG);
// 调度工作队列
schedule_work(&dev->work);
return IRQ_HANDLED;
}
// 初始化
static int my_device_probe(struct platform_device *pdev)
{
struct my_device *dev;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
// 初始化工作队列
INIT_WORK(&dev->work, my_work_handler);
// 或使用延迟工作队列
// INIT_DELAYED_WORK(&dev->dwork, my_work_handler);
return 0;
}
// 延迟工作队列示例
static void schedule_delayed_work_example(struct my_device *dev)
{
// 延迟 100ms 后执行
schedule_delayed_work(&dev->dwork, msecs_to_jiffies(100));
}
4.4 三种方式对比
| 特性 | Softirq | Tasklet | Work Queue |
|---|---|---|---|
| 运行上下文 | 软中断上下文 | 软中断上下文 | 进程上下文 |
| 能否睡眠 | 否 | 否 | 是 |
| 并发性 | 同类型可并发 | 同实例不并发 | 可并发 |
| 分配方式 | 静态 | 动态 | 动态 |
| 使用场景 | 高性能网络/块设备 | 普通中断处理 | 需要睡眠的操作 |
| 延迟 | 最低 | 低 | 较高 |
| 复杂度 | 高 | 中 | 低 |
五、内核线程
5.1 内核线程概念
内核线程是运行在内核空间的线程,没有用户空间映射。
┌─────────────────────────────────────┐
│ 用户空间 │
│ 用户进程1 用户进程2 用户进程3 │
└─────────────────────────────────────┘
↕ 系统调用
┌─────────────────────────────────────┐
│ 内核空间 │
│ ┌────────┐ ┌────────┐ ┌────────┐│
│ │内核线程1│ │内核线程2│ │内核线程3││
│ │kworker │ │ksoftirqd│ │kthreadd││
│ └────────┘ └────────┘ └────────┘│
└─────────────────────────────────────┘
5.2 创建内核线程
c
#include <linux/kthread.h>
struct my_device {
struct task_struct *thread;
wait_queue_head_t wait;
bool thread_running;
u32 data;
};
// 内核线程函数
static int my_kernel_thread(void *data)
{
struct my_device *dev = data;
printk("Kernel thread started\n");
while (!kthread_should_stop()) {
// 等待事件
wait_event_interruptible(dev->wait,
!list_empty(&dev->data_list) || kthread_should_stop());
if (kthread_should_stop())
break;
// 处理数据
// 这里可以睡眠、使用 mutex 等
mutex_lock(&dev->lock);
// 处理队列中的数据
while (!list_empty(&dev->data_list)) {
struct data_item *item = list_first_entry(&dev->data_list,
struct data_item, list);
list_del(&item->list);
// 处理数据
process_data(item);
kfree(item);
}
mutex_unlock(&dev->lock);
}
printk("Kernel thread stopped\n");
return 0;
}
// 创建内核线程
static int my_device_probe(struct platform_device *pdev)
{
struct my_device *dev;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
// 初始化等待队列
init_waitqueue_head(&dev->wait);
// 创建内核线程
dev->thread = kthread_run(my_kernel_thread, dev, "my_kthread");
if (IS_ERR(dev->thread)) {
dev_err(&pdev->dev, "Failed to create kernel thread\n");
return PTR_ERR(dev->thread);
}
return 0;
}
// 中断处理:唤醒内核线程
static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
struct my_device *dev = dev_id;
struct data_item *item;
// 分配数据项
item = kmalloc(sizeof(*item), GFP_ATOMIC);
item->data = readl(dev->base + DATA_REG);
// 添加到队列
mutex_lock(&dev->lock);
list_add_tail(&item->list, &dev->data_list);
mutex_unlock(&dev->lock);
// 唤醒内核线程
wake_up_interruptible(&dev->wait);
return IRQ_HANDLED;
}
// 停止内核线程
static int my_device_remove(struct platform_device *pdev)
{
struct my_device *dev = platform_get_drvdata(pdev);
// 停止内核线程
if (dev->thread) {
kthread_stop(dev->thread);
dev->thread = NULL;
}
return 0;
}
5.3 常见内核线程
bash
# 查看系统中的内核线程
ps aux | grep '\[.*\]'
# 常见内核线程:
[kthreadd] # 内核线程管理
[ksoftirqd/0] # CPU 0 的软中断处理
[kworker/0:0] # CPU 0 的工作队列
[migration/0] # CPU 0 的进程迁移
[watchdog/0] # CPU 0 的看门狗
[kswapd0] # 内存回收
[kblockd] # 块设备 I/O
六、中断线程化 (Threaded IRQ)
6.1 中断线程化的概念
中断线程化是 Linux 现代中断处理机制,将中断处理自动分为硬中断和线程化处理两部分。
硬件中断
↓
硬中断处理 (handler)
↓
唤醒中断线程
↓
中断线程 (thread_fn) - 在进程上下文中运行
优势:
- 自动实现上下半部分离
- 线程中可以睡眠
- 可以使用实时调度策略
- 简化驱动开发
6.2 基本使用
c
#include <linux/interrupt.h>
// 硬中断处理函数(可选)
static irqreturn_t my_hardirq_handler(int irq, void *dev_id)
{
struct my_device *dev = dev_id;
u32 status;
// 读取状态
status = readl(dev->base + STATUS_REG);
// 快速检查
if (!(status & IRQ_MASK))
return IRQ_NONE;
// 清除中断
writel(status, dev->base + STATUS_REG);
// 保存数据
dev->irq_status = status;
// 返回 IRQ_WAKE_THREAD 唤醒线程
return IRQ_WAKE_THREAD;
}
// 线程化中断处理函数
static irqreturn_t my_threaded_handler(int irq, void *dev_id)
{
struct my_device *dev = dev_id;
// 这里运行在进程上下文中,可以睡眠
printk("Threaded IRQ handler: status=0x%x\n", dev->irq_status);
// 可以使用 mutex
mutex_lock(&dev->lock);
// 可以睡眠
msleep(10);
// 可以进行 I/O 操作
if (dev->irq_status & DATA_READY) {
// 通过 I2C 读取数据
i2c_master_recv(dev->client, buffer, len);
}
mutex_unlock(&dev->lock);
return IRQ_HANDLED;
}
// 注册线程化中断
static int my_device_probe(struct platform_device *pdev)
{
struct my_device *dev;
int irq, ret;
irq = platform_get_irq(pdev, 0);
// 使用 request_threaded_irq
ret = request_threaded_irq(
irq, // 中断号
my_hardirq_handler, // 硬中断处理(可为 NULL)
my_threaded_handler, // 线程化处理
IRQF_ONESHOT, // 标志:硬中断处理完后禁用中断,直到线程处理完
"my-device", // 名称
dev // 设备数据
);
if (ret) {
dev_err(&pdev->dev, "Failed to request threaded IRQ\n");
return ret;
}
return 0;
}
6.3 中断线程化标志
c
// IRQF_ONESHOT:单次触发模式
// 硬中断处理后禁用中断,直到线程处理完成
// 适用于电平触发的中断
ret = request_threaded_irq(irq, NULL, handler,
IRQF_ONESHOT, "device", dev);
// IRQF_NO_THREAD:强制非线程化
// 即使使用 request_threaded_irq,也不创建线程
ret = request_threaded_irq(irq, handler, NULL,
IRQF_NO_THREAD, "device", dev);
// IRQF_SHARED:共享中断
// 多个设备共享同一中断线
ret = request_threaded_irq(irq, NULL, handler,
IRQF_ONESHOT | IRQF_SHARED, "device", dev);
6.4 只使用线程化处理
c
// 如果不需要硬中断处理,可以只提供线程处理函数
static irqreturn_t my_irq_thread(int irq, void *dev_id)
{
struct my_device *dev = dev_id;
// 读取数据(可能需要 I/O 操作)
u8 data;
i2c_smbus_read_byte_data(dev->client, REG_DATA);
// 处理数据
process_sensor_data(dev, data);
return IRQ_HANDLED;
}
// 注册时硬中断处理函数传 NULL
int probe(...)
{
ret = request_threaded_irq(irq,
NULL, // 无硬中断处理
my_irq_thread, // 只有线程处理
IRQF_ONESHOT,
"sensor", dev);
}
6.5 实战案例:触摸屏驱动
c
struct touchscreen_dev {
struct i2c_client *client;
struct input_dev *input;
struct mutex lock;
int irq;
};
// 线程化中断处理
static irqreturn_t ts_irq_thread(int irq, void *dev_id)
{
struct touchscreen_dev *ts = dev_id;
struct touch_point points[10];
int num_points, i;
mutex_lock(&ts->lock);
// 通过 I2C 读取触摸点数据(会睡眠)
num_points = ts_read_points(ts, points, 10);
// 上报触摸事件
for (i = 0; i < num_points; i++) {
input_report_abs(ts->input, ABS_MT_POSITION_X, points[i].x);
input_report_abs(ts->input, ABS_MT_POSITION_Y, points[i].y);
input_mt_sync(ts->input);
}
input_sync(ts->input);
mutex_unlock(&ts->lock);
return IRQ_HANDLED;
}
static int ts_probe(struct i2c_client *client)
{
struct touchscreen_dev *ts;
int ret;
ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
mutex_init(&ts->lock);
// 注册线程化中断(适合 I2C 设备)
ret = devm_request_threaded_irq(&client->dev,
client->irq,
NULL, // 无硬中断处理
ts_irq_thread, // 线程处理
IRQF_ONESHOT, // 必须使用 ONESHOT
"touchscreen",
ts);
return ret;
}
七、中断处理方式演进
7.1 演进历程
传统方式 (2.0-2.4)
↓
上下半部分离 (2.6)
├─ Tasklet
├─ Softirq
└─ Work Queue
↓
内核线程化
↓
中断线程化 (2.6.30+)
└─ request_threaded_irq
7.2 选择建议
c
// 决策树
if (处理非常快速 && 不需要睡眠) {
// 使用传统中断
request_irq(irq, handler, flags, name, dev);
}
else if (需要睡眠 || I2C/SPI通信) {
// 使用线程化中断
request_threaded_irq(irq, NULL, thread_fn,
IRQF_ONESHOT, name, dev);
}
else if (需要低延迟 && 不睡眠) {
// 使用 tasklet
request_irq(irq, handler, flags, name, dev);
// 在 handler 中调用 tasklet_schedule()
}
else if (可以接受延迟 && 需要睡眠) {
// 使用工作队列
request_irq(irq, handler, flags, name, dev);
// 在 handler 中调用 schedule_work()
}
7.3 性能对比
| 方式 | 延迟 | CPU占用 | 可睡眠 | 适用场景 |
|---|---|---|---|---|
| 纯中断 | 最低 | 低 | 否 | 快速GPIO |
| Tasklet | 低 | 中 | 否 | 网络数据包 |
| Work Queue | 中 | 中 | 是 | 普通I/O |
| 线程化中断 | 中 | 中 | 是 | I2C/SPI设备 |
| 内核线程 | 高 | 高 | 是 | 复杂后台任务 |
八、实战示例:网卡驱动
8.1 传统方式 (NAPI)
c
// 网卡中断处理(上半部)
static irqreturn_t eth_interrupt(int irq, void *dev_id)
{
struct net_device *ndev = dev_id;
struct eth_priv *priv = netdev_priv(ndev);
u32 status;
status = readl(priv->base + INT_STATUS);
if (status & INT_RX_DONE) {
// 禁用 RX 中断
writel(0, priv->base + INT_ENABLE);
// 调度 NAPI poll
napi_schedule(&priv->napi);
}
return IRQ_HANDLED;
}
// NAPI poll 函数(下半部,软中断上下文)
static int eth_poll(struct napi_struct *napi, int budget)
{
struct eth_priv *priv = container_of(napi, struct eth_priv, napi);
int work_done = 0;
// 处理接收的数据包
while (work_done < budget) {
struct sk_buff *skb = eth_rx_packet(priv);
if (!skb)
break;
netif_receive_skb(skb);
work_done++;
}
if (work_done < budget) {
// 完成 NAPI,重新使能中断
napi_complete(napi);
writel(INT_RX_DONE, priv->base + INT_ENABLE);
}
return work_done;
}
8.2 完整示例:传感器驱动
c
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/iio/iio.h>
struct sensor_dev {
struct i2c_client *client;
struct iio_dev *indio_dev;
struct mutex lock;
int irq;
u8 buffer[6];
};
// 线程化中断处理
static irqreturn_t sensor_irq_thread(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct sensor_dev *sensor = iio_priv(indio_dev);
s16 data[3];
int ret;
mutex_lock(&sensor->lock);
// 通过 I2C 读取传感器数据(会睡眠)
ret = i2c_smbus_read_i2c_block_data(sensor->client,
REG_DATA,
6,
sensor->buffer);
if (ret < 0) {
dev_err(&sensor->client->dev, "Failed to read data\n");
goto out;
}
// 解析数据
data[0] = (s16)(sensor->buffer[0] | (sensor->buffer[1] << 8));
data[1] = (s16)(sensor->buffer[2] | (sensor->buffer[3] << 8));
data[2] = (s16)(sensor->buffer[4] | (sensor->buffer[5] << 8));
// 推送数据到 IIO 缓冲区
iio_push_to_buffers_with_timestamp(indio_dev, data, iio_get_time_ns(indio_dev));
out:
mutex_unlock(&sensor->lock);
return IRQ_HANDLED;
}
static int sensor_probe(struct i2c_client *client)
{
struct iio_dev *indio_dev;
struct sensor_dev *sensor;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*sensor));
if (!indio_dev)
return -ENOMEM;
sensor = iio_priv(indio_dev);
sensor->client = client;
mutex_init(&sensor->lock);
// 配置 IIO 设备
indio_dev->name = "my-sensor";
indio_dev->modes = INDIO_DIRECT_MODE;
// 注册线程化中断
ret = devm_request_threaded_irq(&client->dev,
client->irq,
NULL,
sensor_irq_thread,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
"sensor-irq",
indio_dev);
if (ret) {
dev_err(&client->dev, "Failed to request IRQ\n");
return ret;
}
// 注册 IIO 设备
ret = devm_iio_device_register(&client->dev, indio_dev);
return ret;
}
static const struct i2c_device_id sensor_id[] = {
{ "my-sensor", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, sensor_id);
static struct i2c_driver sensor_driver = {
.driver = {
.name = "my-sensor",
},
.probe = sensor_probe,
.id_table = sensor_id,
};
module_i2c_driver(sensor_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Sensor Driver with Threaded IRQ");
九、调试技巧
9.1 查看中断统计
bash
# 查看系统中断
cat /proc/interrupts
# 输出示例:
# CPU0 CPU1 CPU2 CPU3
# 1: 9 0 0 0 IO-APIC 1-edge i8042
# 8: 0 0 0 0 IO-APIC 8-edge rtc0
# 9: 0 0 0 0 IO-APIC 9-fasteoi acpi
# 12: 155 0 0 0 IO-APIC 12-edge i8042
# 45: 12345 6789 4567 8901 PCI-MSI 524288-edge eth0
# 查看软中断统计
cat /proc/softirqs
# 查看中断线程
ps aux | grep 'irq/'
# irq/45-eth0 # 网卡的中断线程
# irq/46-sensor # 传感器的中断线程
9.2 使用 ftrace 跟踪
bash
# 使能中断跟踪
cd /sys/kernel/debug/tracing
echo 1 > events/irq/enable
echo 1 > tracing_on
# 查看跟踪结果
cat trace
# 输出示例:
# irq_handler_entry: irq=45 name=eth0
# irq_handler_exit: irq=45 ret=handled
# softirq_entry: vec=3 [action=NET_RX]
# softirq_exit: vec=3 [action=NET_RX]
9.3 测量中断延迟
c
// 在驱动中测量
static irqreturn_t my_handler(int irq, void *dev_id)
{
ktime_t start, end;
s64 delta;
start = ktime_get();
// 处理中断
// ...
end = ktime_get();
delta = ktime_to_ns(ktime_sub(end, start));
printk("IRQ latency: %lld ns\n", delta);
return IRQ_HANDLED;
}
十、最佳实践
10.1 设计原则
c
// ✓ 好的做法
static irqreturn_t good_handler(int irq, void *dev_id)
{
// 1. 快速读取硬件状态
u32 status = readl(base + STATUS);
// 2. 清除中断
writel(status, base + STATUS);
// 3. 保存必要数据
save_data(status);
// 4. 调度下半部
schedule_work(&work);
return IRQ_HANDLED;
}
// ✗ 不好的做法
static irqreturn_t bad_handler(int irq, void *dev_id)
{
// ✗ 在中断中睡眠
msleep(10);
// ✗ 长时间循环
for (i = 0; i < 1000000; i++) {
process();
}
// ✗ 使用 mutex
mutex_lock(&lock);
// ✗ I/O 操作
i2c_transfer(...);
return IRQ_HANDLED;
}
10.2 选择建议总结
1. GPIO 按键、简单 UART:
→ 传统中断 + Tasklet
2. I2C/SPI 传感器:
→ 线程化中断 (request_threaded_irq)
3. 网卡、块设备:
→ 传统中断 + NAPI/Softirq
4. 复杂后台任务:
→ 工作队列 或 内核线程
5. 实时性要求高:
→ 传统中断 + 高优先级内核线程
十一、总结
11.1 核心要点
- 中断上半部:快速、不睡眠、禁止中断
- 中断下半部:延迟、可睡眠(work queue)、允许中断
- Softirq:最底层机制,网络/块设备使用
- Tasklet:基于 softirq,简单易用
- Work Queue:进程上下文,可睡眠
- 线程化中断:现代推荐方式,自动分离上下半部
11.2 演进趋势
过去:手动实现上下半部分离
↓
现在:线程化中断自动分离
↓
未来:更智能的中断管理和调度
参考资料:
- Linux Device Drivers, 3rd Edition
- Linux Kernel Development by Robert Love
- Documentation/core-api/workqueue.rst
- Documentation/core-api/kernel-api.rst