中断线程化

中断知识点-中断线程化


前言

首先还是特别有必要从概念上来理解这个知识点,如果概念都没有理解清除,何从去理解这个知识点存在的意义呢?
我自己看很多文档,特别第一次看的时候,都是云里雾里,很难理解这到底是个什么东西,当我自己熟悉了理解了发现如此简单,一个简单机制而已。

这里简单理解下:

  • 前面我们了解的知识点:中断触发了 中断函数,可以理解为中断上文。 那么中断下文可以通过 tasklet - 共享queue-自定义queue-queue队列管理 来实现延时阻塞处理。
  • 那么 中断线程话机制是:在中断函数中断上文的同步触发的情况下,中断下文基于内核机制在线程或者进程中同步处理。甚至中断上文触发函数都不要了 设为null,直接触发中断下文的 中断线程化机制 处理 延时、阻塞业务。

下面先看一下程序,加深理解,简单的驱动中断现成化程序

参考资料

Linux中断线程化
中断的线程化处理
中断线程化,洞悉万象-从理论抽象级到代码实现级
添加链接描述RK3568驱动指南|第五期-中断-第49章 中断线程化实验

一、中断线程化程序

中断线程化接口函数-request_threaded_irq

int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id);

参数说明

  • irq: 中断号, 表示要请求的中断线路。
  • handler: 是在发生中断时首先要执行的处理程序, 非常类似于顶半部, 该函数最后会返
    回 IRQ_WAKE_THREAD 来唤醒中断, 一般 handler 设为 NULL, 用系统提供的默认处理。
  • thread_fn: 线程化的中断处理函数, 非常类似于底半部。 如果此处设置为 NULL 则表示
    没有使用中断线程化。
  • irqflags: 中断标志, 用于指定中断的属性和行为。
  • devname: 中断的名称, 用于标识中断请求的设备。
  • dev_id: 设备标识符, 用于传递给中断处理函数的参数。

函数返回值
函数返回一个整数值, 表示中断请求的结果。 如果中断请求成功, 返回值为 0, 否则返回
一个负数错误代码。

程序示例

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/workqueue.h>

int irq;

// 中断处理函数的底半部(线程化中断处理函数)
irqreturn_t test_work(int irq, void *args)
{
    // 执行底半部的中断处理任务
    msleep(1000);
    printk("This is test_work\n");
    return IRQ_RETVAL(IRQ_HANDLED);
}
// 中断处理函数的顶半部
irqreturn_t test_interrupt(int irq, void *args)
{
    printk("This is test_interrupt\n");
    // 将中断处理工作推迟到底半部
    return IRQ_WAKE_THREAD;
}

static int interrupt_irq_init(void)
{
    int ret;
    irq = gpio_to_irq(101); // 将GPIO映射为中断号
    printk("irq is %d\n", irq);
    // 用于请求并注册一个线程化的中断处理函数
    ret = request_threaded_irq(irq, test_interrupt, test_work, IRQF_TRIGGER_RISING, "test", NULL);
    if (ret < 0)
    {
        printk("request_irq is error\n");
        return -1;
    }

    return 0;
}

static void interrupt_irq_exit(void)
{
    free_irq(irq, NULL); // 释放中断
    printk("bye bye\n");
}

module_init(interrupt_irq_init);
module_exit(interrupt_irq_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("fangchen");

示例程序测试触摸,kernel 层日志如下:
在这里插入图片描述

二、驱动中中断队列与中断线程化的区别与比较

自己在看这个知识点时候,一直想前面task、队列、队列管理 机制都有了,这个线程化机制又是搞什么的,下面我们从不同角度看看。

基本概念对比

特性中断队列 (传统中断处理)中断线程化
执行上下文上半部在中断上下文,下半部多种形式全部在线程上下文(进程上下文)执行
可否睡眠上半部不能,部分下半部机制可以可以睡眠和调度
优先级中断优先级由硬件决定可调整线程优先级
实现复杂度需要区分上下半部编程模型更简单

这里就是需要理解的 执行上下文对比:中断线程化 直接在线程和进程中处理。

技术实现差异

中断队列实现

上半部处理:在request_irq()注册的处理函数中快速执行

下半部机制选择:

  • Softirq:内核预定义,执行快但不可睡眠

  • Tasklet:基于Softirq,同类型串行执行

  • Workqueue:内核线程执行,可睡眠

// 传统中断注册
request_irq(irq, handler, flags, name, dev);

// 工作队列使用示例
struct work_struct my_work;
INIT_WORK(&my_work, work_handler);

irqreturn_t handler(int irq, void *dev_id)
{
    schedule_work(&my_work);  // 调度工作队列
    return IRQ_HANDLED;
}

中断线程化实现

// 线程化中断注册
request_threaded_irq(irq, primary_handler, threaded_handler, flags, name, dev);

// 示例:完全线程化(primary_handler为NULL)
request_threaded_irq(irq, NULL, threaded_handler, flags, name, dev);

性能特征比较

指标中断队列中断线程化
响应延迟上半部响应极快有线程调度开销
吞吐量高频率中断处理更高效高频中断时线程切换开销大
确定性受其他中断影响优先级控制更好,确定性更高
CPU负载直接占用中断CPU时间可通过负载均衡分散到其他CPU

适用场景分析

适合使用中断队列的场景

  • 极高频率的中断(如网络数据包接收)
  • 对延迟极其敏感的硬件控制
  • 不需要复杂处理逻辑的中断
  • 内核没有配置线程化支持的场景

适合使用中断线程化的场景

  • 需要睡眠或等待资源的操作
  • 实时性要求高的应用(PREEMPT_RT补丁)
  • 复杂的中断处理逻辑
  • 需要精确控制处理优先级的场景
  • 用户空间驱动程序(如UIO)

三、知识扩展-irqreturn_t 返回值问题

irqreturn_t 是中断处理函数的返回值类型,用于指示中断是否被成功处理。以下是它的定义和具体应用场景

返回值类型

irqreturn_t 是一个枚举类型,定义在 <linux/interrupt.h> 中:

typedef enum irqreturn {
    IRQ_NONE        = (0 << 0),    // 中断未处理
    IRQ_HANDLED     = (1 << 0),    // 中断已处理
    IRQ_WAKE_THREAD = (1 << 1),    // 需要唤醒线程处理
} irqreturn_t;

各返回值的含义及应用场景

IRQ_NONE

  • 含义 当前中断处理函数未处理该中断。
  • 场景:共享中断线(多个设备共享同一个 IRQ 号)时,如果当前设备的中断未被触发,应返回 IRQ_NONE,以便内核继续调用其他设备的中断处理函数。
    如下:
if (device_interrupt_condition_not_met) {
    return IRQ_NONE; // 不是本设备的中断
}

IRQ_HANDLED

  • 含义:中断已由当前处理函数成功处理。
  • 场景:非共享中断线(独占 IRQ)时,直接返回 IRQ_HANDLED;共享中断线中,确认是本设备中断并完成处理后返回。
    如下:
handle_device_irq();
return IRQ_HANDLED; // 中断已处理

IRQ_WAKE_THREAD

  • 含义:中断需要由内核线程进一步处理(耗时操作)
  • 场景:中断处理分为上半部(快速响应)和下半部(耗时处理)。如果上半部需要唤醒线程(如 tasklet 或工作队列)继续处理,则返回 IRQ_WAKE_THREAD;必须通过 request_threaded_irq() 注册中断处理函数时才能使用此返回值;
    示例:
if (need_threaded_processing) {
    return IRQ_WAKE_THREAD; // 唤醒线程处理下半部
}

注意事项

  • 共享中断:必须检查中断来源,非本设备时返回 IRQ_NONE。
  • 线程化中断:使用 request_threaded_irq() 注册时才能返回 IRQ_WAKE_THREAD。
  • 性能考虑:上半部应尽可能快,耗时操作放到下半部(线程、tasklet 等)。

总结

  • 梳理了解 驱动中断中的 中断线程化机制
  • 对比task、软中断、队列 机制的不同,各自不同的使用场景

建议

  • 纯性能考量:高频中断优先考虑传统中断队列
  • 功能需求:需要复杂处理/阻塞操作时选择线程化
  • 实时性要求:实时系统优先使用线程化中断
  • 开发便捷性:线程化编程模型更简单直接
  • 资源占用:线程化占用更多内存(每个中断一个线程栈)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野火少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值