Linux驱动开发实战(十三):RGB LED驱动并发控制——自旋锁与信号量对比详解

Linux驱动开发实战(十三):RGB LED驱动并发控制------自旋锁与信号量对比详解


文章目录


前言

在Linux驱动开发中,并发控制是一个核心问题。当多个进程同时访问同一个硬件设备时,如果没有合理的保护机制,就会出现数据竞争、资源冲突等问题。

想象一下这个场景:

  • 进程A正在控制RGB LED显示红色
  • 进程B同时也想控制LED显示蓝色
  • 没有保护机制的话,LED的状态会混乱
    本文将通过一个RGB LED驱动实例,用最通俗易懂的方式对比讲解Linux内核中两种最常用的并发控制机制:

自旋锁(Spinlock) - 忙等待机制

信号量(Semaphore) - 睡眠等待机制


一、生动比喻:理解自旋锁与信号量

单人卫生间问题

假设公司只有一个卫生间(共享资源),多个员工(进程/线程)需要使用它。如何保证同一时刻只有一个人在使用?


自旋锁:门口原地转圈等

小明的做法(自旋锁方式):

小明急需使用卫生间,走到门口

如果门锁着(资源被占用):

站在门口,疯狂拧门把手

"能开吗?不能!"

"能开吗?不能!"

"能开吗?不能!"

... 不停尝试 ...

一旦门开了,立刻冲进去!

如果门开着(资源空闲):

直接进去,反锁门
特点分析:

✅ 反应超快:门一开,立刻进去,零延迟

✅ 适合急事:如果前面的人很快出来(1-2秒),这种方式最高效

❌ 消耗体力:一直在门口转圈,不停拧门把手,累得满头大汗

❌ 不适合长时间:如果前面的人蹲半小时,小明会虚脱

内核对应:

c 复制代码
spin_lock(&lock);      // 疯狂拧门把手,直到打开
// 使用卫生间(临界区代码)
spin_unlock(&lock);    // 开门出来

信号量:排队休息等

小红的做法(信号量方式):

小红需要使用卫生间,走到门口

如果门锁着(资源被占用):

在旁边的休息室坐下(进入睡眠)

???(什么都不做,睡觉)

前面的人出来后会叫醒她:"小红,轮到你了!"

小红醒来,走进卫生间

如果门开着(资源空闲):

直接进去,反锁门
特点分析:

✅ 节省体力:在休息室睡觉,完全不消耗体力(CPU资源)

✅ 适合长时间:前面的人蹲多久都没关系,反正我在睡觉

❌ 反应慢:需要被叫醒→站起来→走到卫生间(上下文切换开销)

❌ 短时间不划算:如果前面的人1秒就出来,这套流程反而慢

内核对应:

c 复制代码
down(&sem);           // 尝试进入,如果不行就睡觉
// 使用卫生间(临界区代码)
up(&sem);             // 开门出来,叫醒下一个人

二、实战代码解析:自旋锁版本

核心数据

c 复制代码
spinlock_t s_lock;   // 自旋锁变量
char flag;           // 设备占用标志:0=空闲,非0=占用

为什么需要flag?

  • 自旋锁本身只保护临界区,不记录状态
  • flag用于记录设备是否被占用
  • 自旋锁保护flag变量的读写操作

初始化:驱动加载时

c 复制代码
static int __init led_platform_driver_init(void)
{
    int DriverState;
    
    /* 初始化自旋锁 */
    spin_lock_init(&s_lock);
    
    DriverState = platform_driver_register(&led_platform_driver);
    printk(KERN_ALERT "\tDriverState is %d\n", DriverState);
    return 0;
}
}

打开设备时的自旋锁保护

c 复制代码
static int led_chr_dev_open(struct inode *inode, struct file *filp)
{
    /* 获取自旋锁(原子操作开始) */
    spin_lock(&s_lock);
    
    if(flag) {
        /* 设备正在被使用,释放锁并返回错误 */
        spin_unlock(&s_lock);
        printk("\n driver on using!  open failed !!!\n");
        return -EBUSY;  // 返回设备忙错误
    }
    else {
        /* 设备空闲,标记为占用 */
        flag++;
    }
    
    /* 释放自旋锁(原子操作结束) */
    spin_unlock(&s_lock);
    
    printk("\n open form driver \n");
    return 0;
}

关闭设备时释放标志

c 复制代码
static int led_chrdev_release(struct inode *inode, struct file *filp)
{
    /* 获取自旋锁 */
    spin_lock(&s_lock);
    
    if(flag)
        flag--;  // 清除占用标志
    
    /* 释放自旋锁 */
    spin_unlock(&s_lock);
    
    printk("KERN_ALERT  \n finished  !!!\n");
    return 0;
}

实验现象

如果第二次太快打开就会直接返回打开失败

三、实战代码解析:信号量版本代码解析

关键变量

c 复制代码
struct semaphore sem;  // 定义信号量结构体

信号量初始化

c 复制代码
static int __init led_platform_driver_init(void)
{
    int DriverState;
    
    /* 初始化信号量,初始值为1(二值信号量,相当于互斥锁) */
    sema_init(&sem, 1);
    
    DriverState = platform_driver_register(&led_platform_driver);
    printk(KERN_ALERT "\tDriverState is %d\n", DriverState);
    return 0;
}

说明:sema_init(&sem, 1) 将信号量初始化为1,表示资源可用,只允许一个进程访问。

打开设备时获取信号量

c 复制代码
static int led_chr_dev_open(struct inode *inode, struct file *filp)
{
    /* 获取信号量,如果信号量值为0,进程将睡眠等待 */
    down(&sem);
    
    printk("\n open form driver \n");
    return 0;
}

关闭设备时释放信号量

c 复制代码
static int led_chrdev_release(struct inode *inode, struct file *filp)
{
    /* 释放信号量,唤醒等待队列中的进程 */
    up(&sem);
    
    printk("KERN_ALERT  \n finished  !!!\n");
    return 0;
}

说明:up(&sem) 将信号量值+1,如果有进程在等待,则唤醒它。

实验现象

在上一个命令没执行完就执行下一个命令的话(很急)

要等待上一个没执行完的命令执行完再执行下一个命令(别急)


总结

信号量:像银行取号,拿到号就等着,轮到你自然会叫你

自旋锁:像查快递柜,有就拿,没有就走,下次再来看

适用场景

  • 信号量:文件I/O、网络、设备独占、长临界区
  • 自旋锁:中断处理、计数器、状态检查、短临界区
  1. 持有自旋锁时绝不能睡眠
  2. 中断上下文绝不能用信号量
  3. 自旋锁临界区要尽可能短
  4. 不确定时优先选择信号量(更安全)
相关推荐
VekiSon21 小时前
51单片机——GPIO、按键、中断、定时器与PWM
单片机·嵌入式硬件·51单片机
Xの哲學21 小时前
Linux 软中断深度剖析: 从设计思想到实战调试
linux·网络·算法·架构·边缘计算
林鸿风采21 小时前
在Alpine Linux上部署docker,并配置开机自启
linux·docker·eureka·alpine
纳祥科技21 小时前
NX3302,3进1出HDMI/DVI视频切换器芯片,兼容LT8631UX
单片机·嵌入式硬件·音视频
析木不会编程21 小时前
51单片机程序下载逻辑
单片机·嵌入式硬件·51单片机
羊小猪~~21 小时前
【QT】-- QT基础类
开发语言·c++·后端·stm32·单片机·qt
自激振荡器21 小时前
第二天:IIC总线协议
单片机·嵌入式·通信协议·iic总线协议
l1t1 天前
在arm64 Linux系统上编译tdoku-lib的问题和解决
linux·运维·服务器·c语言·cmake
secondyoung1 天前
Git使用:rebase用法
c语言·经验分享·git·vscode
kklovecode1 天前
C语言之头文件,宏和条件编译
c语言·开发语言·算法