linux 内核中断子系统
interrupt-parent :属性值为“gpio1”,因为 KEY0 所使用的 GPIO 为GPIO1_IO18,也就是设置 KEY0 的 GPIO 中断控制器为 gpio1。interrupts 属性:设置中断源,第一个 cells 的 18 表示 GPIO1 组的 18号 IO。IRQ_TYPE_EDGE_BOTH 定义在文件 include/linux/irq.h 中。
设备树文件
key{
compatible = "alientek,key";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_key>;
key-gpios = <&gpio1 18 GPIO_ACTIVE_HIGH>;
//
interrupt-parent = <&gpio1>;
interrupts = <18 IRQ_TYPE_EDGE_BOTH>;
status = "okay";
};
interrupt-parent :属性值为“gpio1”,因为 KEY0 所使用的 GPIO 为GPIO1_IO18,也就是设置 KEY0 的 GPIO 中断控制器为 gpio1。
interrupts 属性:设置中断源,第一个 cells 的 18 表示 GPIO1 组的 18号 IO。IRQ_TYPE_EDGE_BOTH 定义在文件 include/linux/irq.h 中
中断
/include/linux/interrupt.h
/*
* These flags used only by the kernel as part of the
* irq handling routines.
*
* IRQF_SHARED - allow sharing the irq among several devices
* IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
* IRQF_TIMER - Flag to mark this interrupt as timer interrupt
* IRQF_PERCPU - Interrupt is per cpu
* IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
* IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
* registered first in an shared interrupt is considered for
* performance reasons)
* IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished.
* Used by threaded interrupts which need to keep the
* irq line disabled until the threaded handler has been run.
* IRQF_NO_SUSPEND - Do not disable this IRQ during suspend. Does not guarantee
* that this interrupt will wake the system from a suspended
* state. See Documentation/power/suspend-and-interrupts.txt
* IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set
* IRQF_NO_THREAD - Interrupt cannot be threaded
* IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device
* resume time.
* IRQF_COND_SUSPEND - If the IRQ is shared with a NO_SUSPEND user, execute this
* interrupt handler after suspending interrupts. For system
* wakeup devices users need to implement wakeup detection in
* their interrupt handlers.
*/
#define IRQF_SHARED 0x00000080
#define IRQF_PROBE_SHARED 0x00000100
#define __IRQF_TIMER 0x00000200
#define IRQF_PERCPU 0x00000400
#define IRQF_NOBALANCING 0x00000800
#define IRQF_IRQPOLL 0x00001000
#define IRQF_ONESHOT 0x00002000
#define IRQF_NO_SUSPEND 0x00004000
#define IRQF_FORCE_RESUME 0x00008000
#define IRQF_NO_THREAD 0x00010000
#define IRQF_EARLY_RESUME 0x00020000
#define IRQF_COND_SUSPEND 0x00040000
中断API
获取中断号
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
dev:设备节点
index:索引号,interrupts 属性可能包含多条中断信息,通过 index 指定要获取的信息。
返回值:中断号。
申请中断
request_irq 函数用于申请中断,request_irq函数可能会导致睡眠,不能在中断上下文或者其他禁止睡眠的代码段中使用 request_irq 函数。request_irq 函数会激活(使能)中断,不需要手动去使能中断
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
irq:要申请中断的中断号。
handler:中断处理函数,当中断发生以后就会执行此中断处理函数。
flags:中断标志
| 标志 | 描述 |
|---|---|
| IRQF_SHARED | 多个设备共享一个中断线,共享的所有中断都必须指定此标志。如果使用共享中断的话,request_irq 函数的 dev 参数就是唯一区分他们的标志。 |
| IRQF_ONESHOT | 单次中断,中断执行一次就结束。 |
| IRQF_TRIGGER_NONE | 无触发。 |
| IRQF_TRIGGER_RISING | 上升沿触发。 |
| IRQF_TRIGGER_FALLING | 下降沿触发。 |
| IRQF_TRIGGER_HIGH | 高电平触发。 |
| IRQF_TRIGGER_LOW | 低电平触发。 |
name:中断名字,设置以后可以在/proc/interrupts 文件中看到对应的中断名字。
dev:如果将 flags 设置为 IRQF_SHARED 的话,dev 用来区分不同的中断,一般情况下将
dev 设置为设备结构体,dev 会传递给中断处理函数 irq_handler_t 的第二个参数。
返回值:0 中断申请成功,其他负值 中断申请失败,如果返回-EBUSY 的话表示中断已经被申请了。
释放中断
free_irq 函数释放掉相应的中断。如果中断不是共享的,那么 free_irq 会删除中断处理函数并且禁止中断。
void free_irq(unsigned int irq, void *dev)
irq:要释放的中断。
dev:如果中断设置为共享(IRQF_SHARED)的话,此参数用来区分具体的中断。共享中断只有在释放最后中断处理函数的时候才会被禁止掉。
返回值:无。
中断处理函数
使用 request_irq 函数申请中断的时候需要设置中断处理函数,中断处理函数格式如下所示:
irqreturn_t (*irq_handler_t) (int, void *)
第一个参数是要中断处理函数要相应的中断号。第二个参数是一个指向 void 的指针,需要与 request_irq 函数的 dev 参数保持一致。用于区分共享中断的不同设备,dev 也可以指向设备数据结构。
中断处理函数的返回值为 irqreturn_t 类型,irqreturn_t 类型定义
irqreturn_t 结构
enum irqreturn {
IRQ_NONE = (0 << 0),
IRQ_HANDLED = (1 << 0),
IRQ_WAKE_THREAD = (1 << 1),
};
typedef enum irqreturn irqreturn_t;
一般中断服务函数返回值使用如下形式:return IRQ_RETVAL(IRQ_HANDLED)
中断使能与禁止函数
void enable_irq(unsigned int irq)
void disable_irq(unsigned int irq)
void disable_irq_nosync(unsigned int irq)
local_irq_enable()
local_irq_disable()
local_irq_save(flags)
local_irq_restore(flags)
中断号
每个中断都有一个中断号,通过中断号即可区分不同的中断
2、上半部下半部
2.1、软中断
- 软中断不能睡眠:软中断处理函数运行在中断上下文中,不能调用可能睡眠的函数
- 软中断适合处理高频、轻量级的任务。对于复杂的任务,考虑使用tasklet或工作队列。
- 优先级:软中断有固定的优先级,数字越小优先级越高
- 并发性:同一个软中断可以在不同的 CPU 上同时运行,因此必须做好锁保护
- 使用场景:对性能要求极高的场景,如网络子系统、块设备子系统。一般不推荐驱动程序开发者直接使用。
/* 在include/linux/interrupt.h中 */
enum {
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ,
MY_SOFTIRQ, /* 自定义软中断 */
NR_SOFTIRQS
};
/*
nr:要开启的软中断,上面中选一个。
action:软中断对应的处理函数。
返回值:没有返回值。
*/
void open_softirq(int nr, void (*action)(struct softirq_action *))
/*
nr:要触发的软中断
返回值:没有返回值
*/
void raise_softirq(unsigned int nr)
/*软中断函数*/
static void my_softirq_handler(struct softirq_action *action)
2.2、tasklet
基于软中断Softirq
/*定义*/
struct tasklet_struct t;
/*
初始化
t:要初始化的 tasklet
func:tasklet 的处理函数。
data:要传递给 func 函数的参数
*/
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data);
/*
宏 DECLARE_TASKLET 来一次性完成 tasklet 的定义和初始化
*/
DECLARE_TASKLET(name, func, data)
/*
是中断处理函数中调用 tasklet_schedule 函数
t:要调度的 tasklet,
*/
void tasklet_schedule(struct tasklet_struct *t)
2.3、共享工作队列
工作队列是另外一种下半部执行方式,工作队列在进程上下文执行,工作队列将要推后的
工作交给一个内核线程去执行,因为工作队列工作在进程上下文,因此工作队列允许睡眠或重
新调度。因此如果你要推后的工作可以睡眠那么就可以选择工作队列,否则的话就只能选择软
中断或 tasklet。
- 运行在进程上下文,可以休眠。而软中断和 Tasklet 在中断上下文中,不能休眠
- 可以抢占:受内核调度器管理。
/*定义*/
struct work_struct w;
/*初始化
_work 表示要初始化的工作,_func 是工作对应的处理函数
*/
#define INIT_WORK(_work, _func)
/*
使用 DECLARE_WORK 宏一次性完成工作的创建和初始化*/
#define DECLARE_WORK(n, f)
/*
中断处理函数中调用
work:要调度的工作。
返回值:0 成功,其他值 失败。
*/
bool schedule_work(struct work_struct *work)
2.4、自定义工作队列
自定义工作队列是由驱动程序或子系统自己创建和拥有的工作队列,它有专属的内核线程。
API 接口
#include <linux/workqueue.h>
/* 1. 创建自定义工作队列 */
struct workqueue_struct *my_wq;
my_wq = alloc_workqueue("my_queue_name", flags, max_active);
/* 2. 调度工作项到自定义工作队列 */
int queue_work(struct workqueue_struct *wq, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay);
/* 3. 刷新(等待)工作队列中的所有工作项完成 */
void flush_workqueue(struct workqueue_struct *wq);
/* 4. 销毁自定义工作队列 */
void destroy_workqueue(struct workqueue_struct *wq);
2.5、延迟工作队列
#include <linux/workqueue.h>
/* 定义并初始化一个延迟工作 */
static DECLARE_DELAYED_WORK(my_delayed_work, my_delayed_work_fn);
或者
/* 动态初始化一个延迟工作项 */
struct delayed_work my_dwork;
INIT_DELAYED_WORK(&my_dwork, my_delayed_work_fn);
/* 调度延迟工作项到共享工作队列
dwork:要执行的任务对象(延迟工作)的指针。
delay:延迟时间(单位是内核时钟节拍数 jiffies)
@delay: 延迟的时间,单位是 jiffies(内核时间单位)
*/
static inline bool schedule_delayed_work(struct delayed_work *dwork, unsigned long delay)
static inline bool queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay)
/* 取消已调度的延迟工作项 */
bool cancel_delayed_work(struct delayed_work *dwork);
bool cancel_delayed_work_sync(struct delayed_work *dwork); // 同步等待取消完成,更安全
/* 注意:刷新和销毁队列的API与普通工作队列相同 */
flush_workqueue(struct workqueue_struct *wq);
destroy_workqueue(struct workqueue_struct *wq)
/* 修改一个已经调度但尚未执行的延迟工作的到期时间 */
int mod_delayed_work(struct workqueue_struct *wq,struct delayed_work *dwork,unsigned long delay);
2.4、中断线程化
Primary Handler:一个非常快速的可选函数,在硬中断上下文中运行,用于验证中断源并进行最快速的硬件应答。如果它能够完全处理中断,则返回 IRQ_HANDLED;否则返回 IRQ_WAKE_THREAD 来唤醒线程处理函数。
Thread Handler:在内核线程中运行的主要处理函数,负责完成所有耗时的操作。它可以休眠。
- 可以调用 msleep(), schedule(), mutex_lock() 等可能休眠的函数
- 可以执行较长时间的操作而不会影响系统响应性
- 可以通过 sched_setscheduler() 设置实时优先级
/*
irq中断号
handler:Primary Handler(硬中断处理函数)。如果为 NULL,完全线程化(primary_handler为NULL),它直接返回 IRQ_WAKE_THREAD。
thread_fn:Thread Handler(线程处理函数)。
flags:中断标志。需要添加 IRQF_ONESHOT,确保线程处理函数完成前不会再次触发 Primary Handler。
name:中断名称(显示在 /proc/interrupts)
dev:传递给线程函数的私有数据指针
*/
int request_threaded_irq(unsigned int irq,
irq_handler_t handler, // Primary Handler
irq_handler_t thread_fn, // Thread Handler
unsigned long flags,
const char *name,
void *dev);
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)