按中断来源分类:
按是否可屏蔽分类:
ARM多核处理器最常用的中断控制器:GIC(Gerneric Interrupt Controller)
SGI(Software Generated Interrupt):软件产生的中断,可以用于多核的核间通信,一个CPU可以通过
写GIC的寄存器给另外一个CPU产生中断。多核调度用的IPI_WAKEUP、IPI_TIMER、
IPI_RESCHEDULE、IPI_CALLFUNC、IPI_CALLFUNCSINGLE、IPI_CPUSTOP、IPI_IRQWORK、
IPI_COMPLETION都是由SGI产生的。
PPI(Private Peripheral Interrupt):某个CPU私有外设的中断,这类外设的中断只能发给绑定的那个
CPU。
SPI(Shared Peripheral Interrupt):共享外设的中断,这类外设的中断可以路由到任何一个CPU。
intirq_set_affinity(unsignedint irq, conststruct cpumask *m); //SPI类型中断通过此函数设定中断触发的CPU核//默认情况下,中断都是由CPU0产生的irq_set_affinity(irq, cpumask_of(i)); //将中断irq设定到CPUi上中断处理过程是关全局中断的,中断处理过程中是不能响应其他中断的,因为Linux内核是不允许中断嵌套的。
当系统中断很多的,系统延迟性就会很大(因此中断处理会关全局中断),为此linux系统将中断设计成上半部(顶半部)和下半部(底半部)
中断其实是一种异步的事件处理机制,可以提高系统的并发处理能力。
中断处理需要快进快出,为了在中断快进快出和中断任务量之间找到一个平衡点,Linux将中断处理程序分为两个部分:
intrequest_irq(unsignedint irq, irq_handler_t handler, unsignedlong flags, constchar *name, void *dev);//申请中断//irq 要申请的硬件中断号//handler 向系统注册的中断处理函数,顶半部处理函数//flags中断处理属性,可以是以下值:// IRQF_TRIGGER_RISING //上升沿触发// IRQF_TRIGGER_FALLING //下降沿触发// IRQF_TRIGGER_HIGH //高电平触发// IRQF_TRIGGER_LOW //低电平触发//如果flags参数设置了IRQF_SHARED,表示多个设备共享中断//dev 传递给中断服务函数handler的私有数据,一般为设备的设备结构体或者NULL//返回0表示成功,返回-EINVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享#define IRQF_TRIGGER_NONE 0x00000000#define IRQF_TRIGGER_RISING 0x00000001#define IRQF_TRIGGER_FALLING 0x00000002#define IRQF_TRIGGER_HIGH 0x00000004#define IRQF_TRIGGER_LOW 0x00000008#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)#define IRQF_TRIGGER_PROBE 0x00000010顶半部中断处理函数原型:
typedefirqreturn_t(*irq_handler_t)(int, void *);typedefintirqreturn_t;voidfree_irq(unsignedint irq,void *dev_id);voiddisable_irq(int irq); //等待中断处理完成voiddisable_irq_nosync(int irq); //立即返回,不会等待中断处理完成voidenable_irq(int irq); //使能中断下面函数或宏以来CPU体系结构。
#define local_irq_save(flags) ... //禁止中断宏voidlocal_irq_disable(void); //禁止中断函数 #define local_irq_restore(flags) ...voidlocal_irq_enable(void);使用模板:
voidxxx_tasklet_func(unsignedlong arg){}DECLARE_TASKLET(xxx_tasklet, xxx_tasklet_func, 0);//中断顶半部处理函数irqreturn_tirq_handler_t(int, void *){ ... tasklet_schedule(&xxx_tasklet) ...}
tasklet调度运行:

structwork_structmy_wq;/* 定义一个工作队列 */voidmy_wq_func(struct work_struct *work); /* 定义一个处理函数 */INIT_WORK(&my_wq, my_wq_func);/* 初始化工作队列并将其与处理函数绑定 */schedule_work(&my_wq); /* 调度工作队列执行 */cancel_work_sync() //取消workstructworkqueue_struct *myqueue;myqueue = create_workqueue("my_workqueue"); // 为每个CPU创建线程 myqueue = create_singlethread_workqueue("my_workqueue"); // 仅创建一个全局线程 voidmy_work_handler(struct work_struct *work);structwork_structmy_work;INIT_WORK(&my_work, my_work_handler); // 初始化工作项及处理函数voidmy_work_handler(struct work_struct *work){ printk(KERN_INFO "Work executed on CPU %d\n", smp_processor_id()); msleep(100); // 运行在进程上下文,允许睡眠 }queue_work(myqueue, &my_work); // 将工作项添加到专用队列,立即执行// 延迟执行任务structdelayed_workmy_delayed_work;INIT_DELAYED_WORK(&my_delayed_work, my_delayed_handler);queue_delayed_work(myqueue, &my_delayed_work, msecs_to_jiffies(100)); // 延迟100ms执行 
| 特性 | 专用工作队列 | 共享工作队列(system_wq) |
|---|---|---|
| 线程资源 | 独立线程池,隔离性强 | 共享内核默认线程,可能与其他任务竞争 |
| 并行性 | 可自定义并行策略(多线程/单线程) | 系统全局多线程处理 |
| 适用场景 | 高频、高优先级或需独立资源控制的任务 | 简单、低频任务 |
| 资源消耗 | 较高(需额外线程) | 较低(复用系统资源) |
enum{ HI_SOFTIRQ=0, //高优先级小任务软中断 TIMER_SOFTIRQ, //定时器软中断 NET_TX_SOFTIRQ, // 网络发送报文的软中断 NET_RX_SOFTIRQ, //网络接收报文软中断 BLOCK_SOFTIRQ, //块设备软中断 IRQ_POLL_SOFTIRQ, //支持I/O轮询的块设备软中断 TASKLET_SOFTIRQ, // 低优先级小任务 SCHED_SOFTIRQ, // 调度软中断,用于在处理器之间负载均衡 HRTIMER_SOFTIRQ, /* 废弃,Unused, but kept as tools rely on the numbering. Sigh! */ RCU_SOFTIRQ, //RCU软中断 /* Preferable RCU should always be the last softirq */ NR_SOFTIRQS};structsoftirq_action{void (*action)(struct softirq_action *);};externvoidopen_softirq(int nr, void (*action)(struct softirq_action *)); //nr为选择为上述枚举其中之一externvoidraise_softirq(unsignedint nr); request_thread_dirq(unsignedint irq, irqhandlert handler,irq_handler_t thread_fn,unsignedlong flags, constchar *name, void *dev);devm_request_threaded_irq(struct device *dev, unsignedint irq, irqhandlert handler, irq_handler_t thread_fn,unsignedlong irqflags, constchar *devname,void *devid);//handler函数执行于中断上下文,设置为NULL,内核将执行默认的处理函数//thread_fn函数为内核线程执行函数用法和request_irq()相同,只是多了一个线程处理函数thread_fn, irqflags 支持设置 IRQF_ONESHOT,内核会在中断上下文屏蔽相应的中断,内核执行完线程thread_fn,会重新使能该中断。
禁止/使能软中断和tasklet的底半部机制的函数:
local_bh_disable()local_bh_enable()softirq通过raise_softirq()触发,执行时机包括硬中断返回后和内核线程ksoftirqd唤醒时。tasklet通过tasklet_schedule()调度,最终依赖TASKLET_SOFTIRQ类型的softirq执行。4. 适用场景
| 特性 | softirq | tasklet |
|---|---|---|
| 并行性 | 支持跨CPU并行执行 | 同一类型串行执行 |
| 动态注册 | 不支持(静态预定义) | 支持动态注册 |
| 可重入性要求 | 必须可重入 | 无需考虑可重入 |
| 适用场景 | 高频、高性能需求(如网络、定时器) | 简单延迟任务(如驱动中的非关键操作) |
| 开发复杂度 | 高(需处理同步) | 低(内核自动同步) |
软中断和tasklet都是工作于软中断上下文,属于原子上下文,不允许睡眠。
内核很多地方都采用了softirq,驱动编写者不会也不宜直接使用softirq。
工作队列工作于进程上下文,允许睡眠。
include/linux/irq.h
//中断属性类型 类似STM32外部有低电平、高电平,上升沿or下降沿触发
enum { IRQ_TYPE_NONE = 0x00000000, IRQ_TYPE_EDGE_RISING = 0x00000001, IRQ_TYPE_EDGE_FALLING = 0x00000002, IRQ_TYPE_EDGE_BOTH = (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING), IRQ_TYPE_LEVEL_HIGH = 0x00000004, IRQ_TYPE_LEVEL_LOW = 0x00000008, IRQ_TYPE_LEVEL_MASK = (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH), IRQ_TYPE_SENSE_MASK = 0x0000000f, IRQ_TYPE_DEFAULT = IRQ_TYPE_SENSE_MASK, IRQ_TYPE_PROBE = 0x00000010, IRQ_LEVEL = (1 << 8), IRQ_PER_CPU = (1 << 9), IRQ_NOPROBE = (1 << 10), IRQ_NOREQUEST = (1 << 11), IRQ_NOAUTOEN = (1 << 12), IRQ_NO_BALANCING = (1 << 13), IRQ_MOVE_PCNTXT = (1 << 14), IRQ_NESTED_THREAD = (1 << 15), IRQ_NOTHREAD = (1 << 16), IRQ_PER_CPU_DEVID = (1 << 17), IRQ_IS_POLLED = (1 << 18), IRQ_DISABLE_UNLAZY = (1 << 19),}; my_key { /* 我加的设备树红色led的设备树子节点 */#address-cells = <1>; #size-cells = <1>; compatible = "my_key"; pinctrl-names = "default"; /* 加上跟pinctrl子系统相关的属性 */ pinctrl-0 = <&pinctrl_my_key>; /* 加上跟pinctrl子系统相关的属性*/ my_key_gpio = <&gpio5 1 GPIO_ACTIVE_LOW>; /* 添加跟gpio子系统相关的 */ status = "okay"; interrupt-parent = <&gpio5>; //设置按键的中断控制器是gpio5 interrupts = <1 IRQ_TYPE_EDGE_BOTH>; //1 - gpio5_1引脚,IRQ_TYPE_EDGE_BOTH-中断设置为双边沿触发 };include/linux/of_irq.h
unsignedintirq_of_parse_and_map(struct device_node *node, int index)//根据设备树节点获取中断号include/linux/gpio.h
staticinlineintgpio_to_irq(unsignedint gpio)//设置gpio设置为中断状态,返回中断号