Linux中断、中断子系统、内核同步互斥机制

一、中断

1. 概念

中断:CPU 运行程序时,突发事件到来,CPU 暂停当前程序,跳转处理中断事件,处理完毕后返回原程序继续执行。

  • 分类:内部中断(CPU 内部产生)、外部中断(来自 CPU 外部外设)
  • 作用:处理异步事件、随机事件、非顺序逻辑场景
  • 查看系统中断信息命令:cat /proc/interrupts

2. 中断相关函数接口

函数名 功能
request_irq 注册中断处理程序,绑定中断号与中断服务函数
free_irq 释放已经注册的中断资源
disable_irq 禁止指定中断
disable_irq_nosync 禁止中断,立即返回,不等待当前中断处理结束
enable_irq 使能指定中断
local_irq_save 禁用当前 CPU 所有中断,保存中断状态,适用于 SMP 多核 CPU
local_irq_disable 禁止当前 CPU 全部中断,适用于多核
local_irq_restore 恢复之前保存的中断状态,适用于多核
devm_request_irq 资源自动管理的中断注册函数,设备卸载自动释放中断

3. 开发操作步骤

① 设备树 DTS 适配
复制代码
puutkey {
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "puut-key";
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_key>;
    key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */
    interrupt-parent = <&gpio1>;
    interrupts = <18 IRQ_TYPE_EDGE_RISING>;
};
  • interrupt-parent:指定中断父控制器。GPIO 引脚产生外部中断,先提交给 GPIO 控制器,GPIO 控制器再连接 GIC 通用中断控制器。
  • interrupts:描述引脚编号、触发方式
    • IRQ_TYPE_EDGE_RISING:上升沿触发
    • IRQ_TYPE_EDGE_FALLING:下降沿触发
    • IRQ_TYPE_EDGE_BOTH:双边沿触发

gpio 控制器节点示例:

复制代码
gpio1: gpio@0209c000 {
    compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
    reg = <0x0209c000 0x4000>;
    interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
                 <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
    gpio-controller;
    #gpio-cells = <2>;
    interrupt-controller; /* GPIO可以作为其他外设的中断父控制器 */
    #interrupt-cells = <2>;
};
② 驱动开发步骤
  • 通过of_irq_get、gpio_to_irq从设备树解析获取中断号,GPIO 切换至中断输入模式

  • 调用request_irq申请中断,绑定中断处理函数

  • 编写中断处理函数,注意返回值:

    enum irqreturn {
    IRQ_NONE, /* 不是本驱动处理的中断,转交其他驱动 /
    IRQ_HANDLED, /
    中断正常处理完成,内核不再传递 /
    IRQ_WAKE_THREAD /
    唤醒threaded irq线程处理 */
    };

  • 编写上层应用程序测试

中断驱动完整回顾步骤
  • 设备树节点添加interrupt-parent、interrupts属性
  • 添加 pinctrl 引脚配置
  • 设备节点引用 pinctrl、gpio、中断配置,status 设为 okay
  • misc_register注册杂项设备
  • of 函数解析获取设备树节点
  • of 接口获取 gpio 编号
  • 向内核申请 GPIO 资源
  • of_irq_get /gpio_to_irq 获取中断号
  • request_irq申请注册中断

二、Linux 中断子系统

中断处理讲究快进快出,内核将中断拆分为顶半部(Top Half) 、底半部(Bottom Half),平衡实时性与工作量。

  • 顶半部(上半部):硬件中断上下文,紧急硬件操作;不可休眠、不可被新中断打断,执行时间极短。仅做寄存器读写、唤醒底半部。
  • 底半部(下半部):处理耗时、非紧急任务,可以被新中断打断。分为软中断、tasklet、workqueue 工作队列三类。
机制 运行环境 能否休眠 调度时机 并行性 适用场景
顶半部 中断上下文 ❌ 不能 立即执行 独占 CPU 紧急硬件操作
软中断 softirq 中断上下文 ❌ 不能 顶半部返回前 多核可并行 网络、块设备、定时器,内核底层使用
tasklet 中断上下文 ❌ 不能 顶半部返回前 同类型 tasklet 不能并行 驱动最常用,轻量底半部,按键、GPIO 中断
workqueue 工作队列 进程上下文 ✅ 允许 内核线程调度 完全并行 耗时操作、需要休眠的任务

1. 软中断 softirq

内核底层机制,属于底半部,运行在中断上下文;资源有限,驱动开发几乎不用自定义软中断。

2.tasklet(驱动最常用,软中断封装)

  • 特点:简单安全,不能休眠;同一个 tasklet 不会多核并行执行。
  • 使用:DECLARE_TASKLET定义 tasklet;顶半部调用tasklet_schedule触发调度。
  • 适用:按键处理、GPIO 中断、快速数据读取,不需要休眠的场景。

3.workqueue 工作队列

唯一允许休眠的底半部,运行在内核线程上下文,可以调用 msleep、mutex、kmalloc、copy_to_user。

  • 使用:INIT_WORK初始化工作;schedule_work触发调度。
  • 适用:延时、内存分配、I2C/SPI 读写等需要休眠的耗时操作。

上下文区分

  1. 硬件中断上下文(顶半部):收到硬件中断立即执行,不可休眠
  2. 软中断 /tasklet 上下文:属于中断上下文,不可休眠
  3. 进程上下文(workqueue):内核线程,可以休眠

三、内核同步互斥机制(并发与竞态)

并发访问共享资源会产生竞态,需要互斥保护,常用 5 种:原子操作、自旋锁、信号量、互斥体 (mutex)、完成量 completion

1. 原子操作

保证对整型变量修改排他执行,无锁,适用于简单变量计数。

函数 功能
atomic_set 设置原子变量值
atomic_read 读取原子变量
atomic_add/atomic_sub 原子变量增减
atomic_inc/atomic_dec 自增、自减
位操作 set_bit /clear_bit/test_and_set_bit 位原子操作

2. 自旋锁 spinlock

临界区持有锁期间 CPU 循环忙等,不进入休眠。中断上下文推荐 ,禁止在持有自旋锁时休眠。 核心 API:spin_lock_init、spin_lock、spin_unlock、spin_lock_irqsave(关中断 + 获取锁)

3. 信号量 semaphore

可计数信号量,获取不到锁时进程休眠,适合资源数量大于 1 的场景。API:sema_init、down、up。

4. 互斥体 mutex

信号量特例,计数固定为 1,同一时间只能一个进程持有;只能进程上下文使用,不可用于中断上下文。API:mutex_init、mutex_lock、mutex_unlock。

5. 完成量 completion

专门用于一个线程等待另外一个线程完成事件 ,常用于中断通知任务。API:init_completion、wait_for_completion、complete。

各类锁对比简表

类型 等待方式 能否休眠 适用上下文
自旋锁 spinlock 忙等待 ❌ 不可休眠 中断上下文、进程上下文
信号量 semaphore 睡眠等待 ✅ 可休眠 进程上下文
互斥体 mutex 睡眠等待 ✅ 可休眠 进程上下文,独占访问
完成量 completion 事件等待 ✅ 可休眠 进程上下文,事件通知场景

总结

  1. 原子操作:简单变量保护,无锁开销。
  2. 自旋锁:短临界区,中断上下文使用,不可休眠。
  3. 信号量:资源计数,进程上下文,可休眠。
  4. mutex 互斥体:独占资源,进程上下文,不可在中断使用。
  5. 完成量:专门用于任务等待事件完成,中断通知线程场景。
相关推荐
姓刘的哦1 小时前
GT911触摸芯片
linux·stm32
Android系统攻城狮1 小时前
Linux Gstreamer深度解析之gst_audio_ring_buffer_read调用流程与实战(七十五)
linux·运维·服务器·gstreamer音视频·gstreamer音视频进阶
一条破秋裤2 小时前
04_控制GPIO通用化_从寄存器到gpiod
linux
星恒随风2 小时前
Linux进程信号(二):深入理解信号阻塞、未决、sigaction、中断机制、可重入函数与SIGCHLD
linux·运维·服务器·笔记·学习
额额额对了2 小时前
Linux 驱动实战:ADXL345 驱动
linux·运维·服务器
Android系统攻城狮10 小时前
Linux Gstreamer深度解析之gst_audio_ring_buffer_delay调用流程与实战(七十二)
linux·运维·服务器·gstreamer音视频·音视频进阶
快乐的韭菜根根11 小时前
ubuntu 安装slurm并配置
linux·运维·ubuntu
Tronlong创龙12 小时前
SBC‑TLT153 嵌入式 Linux 应用开发,交叉编译与 GDB 调试实践(上)
嵌入式硬件·嵌入式开发·硬件开发·工业控制·工业开发板
倒头就睡的小比特12 小时前
Linux基础指令3
linux·服务器