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. 不确定时优先选择信号量(更安全)
相关推荐
旖旎夜光1 小时前
Linux(13)(中)
linux·网络
飞睿科技1 小时前
乐鑫智能开关方案解析:基于ESP32-C系列的低功耗、高集成设计
嵌入式硬件·物联网·esp32·智能家居·乐鑫科技
风指引着方向1 小时前
图编译优化全链路:CANN graph-engine 仓库技术拆解
c语言
威迪斯特1 小时前
CentOS图形化操作界面:理论解析与实践指南
linux·运维·centos·组件·图形化·桌面·xserver
一方热衷.2 小时前
在线安装对应版本NVIDIA驱动
linux·运维·服务器
独自归家的兔2 小时前
ubuntu系统安装dbswitch教程 - 备份本地数据到远程服务器
linux·运维·ubuntu
ONE_SIX_MIX2 小时前
ubuntu 24.04 用rdp连接,桌面黑屏问题,解决
linux·运维·ubuntu
龙飞052 小时前
Systemd -systemctl - journalctl 速查表:服务管理 + 日志排障
linux·运维·前端·chrome·systemctl·journalctl
*小海豚*2 小时前
在linux服务器上DNS正常,但是java应用调用第三方解析域名报错
java·linux·服务器
June`2 小时前
muduo项目排查错误+测试
linux·c++·github·muduo网络库