Linux 中断处理机制详解:上下半部、内核线程与中断线程化

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 核心要点

  1. 中断上半部:快速、不睡眠、禁止中断
  2. 中断下半部:延迟、可睡眠(work queue)、允许中断
  3. Softirq:最底层机制,网络/块设备使用
  4. Tasklet:基于 softirq,简单易用
  5. Work Queue:进程上下文,可睡眠
  6. 线程化中断:现代推荐方式,自动分离上下半部

11.2 演进趋势

复制代码
过去:手动实现上下半部分离
    ↓
现在:线程化中断自动分离
    ↓
未来:更智能的中断管理和调度

参考资料:

  • Linux Device Drivers, 3rd Edition
  • Linux Kernel Development by Robert Love
  • Documentation/core-api/workqueue.rst
  • Documentation/core-api/kernel-api.rst
相关推荐
机器人行业研究员2 小时前
防爆六维力传感器的本质安全,破解高危环境自动化难题
运维·自动化
浪漫血液&2 小时前
Linux基础指令(简易版)
linux·服务器
云计算老刘2 小时前
1. Cockpit 管理服务器;2. Linux 软件包管理
linux·运维·服务器·云原生·云计算
海域云SeaArea_3 小时前
ubuntu22.01安装NVIDIA-Docker
运维·docker·容器
未来的旋律~3 小时前
nginx
运维·网络·nginx
从零点3 小时前
STM32电机运动控制的设计
stm32·嵌入式硬件
小苏兮3 小时前
【把Linux“聊”明白】进程的概念与状态
linux·运维·服务器·学习
wsad05324 小时前
Ubuntu 24.04 更换国内软件源(以阿里云为例)
linux·ubuntu·阿里云
楼田莉子4 小时前
C++/Linux小项目:自主shell命令解释器
linux·服务器·开发语言·c++·后端·学习