Linux并发与竞争的处理2-------自旋锁

产生的原因:Linux是一个多任务操作系统,肯定会存在多个任务共同操作同一段内存或者设备的情况,对于多个任务甚至中断都能访问的资源(共享资源)需要保护。也就是要处理对共享资源的并发访问

同时访问某个数据:同时就是并发 他们之间存在着竞争关系

并发的原因:多任务系统,中断,抢占,多核,线程 等竞争资源他们之间存在竞争关系

如何处理:即在他们可以共同访问的数据段中保证一次只要一个线程访问 即原子访问(这么称呼是因为原子是一个整体 不拆分)

保护的是:数据 是多个线程都会访问的共享数据(可以是全局变量)

——————————————————————————————

原子操作只能对整形变量或者位进行保护,但实际环境中保护的不仅仅是整形数据 还要结构体变量
锁机制:当一个线程要访问某个共享资源的时候首先要先获取相应的锁,锁只能被一个线程持有,
只要此线程不释放持有的锁,那么其他的线程就不能获取此锁
所以:当有一段共享数据需要被AB访问,A拿到了锁的钥匙 所以A可以访问 B 此时在等待A把锁的释放 相当于在原地打转。线 程 B不会进入休眠状态或者说去做其他的处理 相当自旋
适合范围:适用于短时期的轻量级加锁,不然会浪费处理器时间,降低系统性能

本案例使用自旋锁 spinlock_ ---一次只允许一个应用程序可以使用 LED 灯。

注意:被自旋锁保护的临界区一定不能调用任何能够引起睡眠和阻塞的 API 函数,否则的话会可能会导致死锁现象的发生

原因是:自旋锁会自动禁止抢占,也就说当线程 A 得到锁以后会暂时禁止内核抢占

如果A 在持有锁期间进入了休眠状态,那么A 会自动放弃 CPU 使用权。此时线程 B 开始运行,线程 B 也想要获取锁,但是此时锁被 A 线程持有,而 且自旋锁的内核抢占还被禁止,线程B就移植在循环无法被调度出去,线程 A 就无法运行,锁也就无法释放, 死锁发生。

如果此时有中断

线程 A 先运行,并且获取到了 lock 这个锁,当线程 A 运行时候中断发生了,中断抢走了 CPU 使用权。中断服务函数也要获取 lock 这个锁, 但是这个锁被线程 A 占有着,中断就会一直自旋,等待锁有效。但是在中断服务函数执行完之前A也没有执行完 死锁了。
解决方法就:获取锁之前关闭本地中断

——————————————————————————————

本案例使用自旋锁---一次只允许一个应用程序可以使用 LED 灯。

--linux系统提供了一系列的API函数

——————————————————

/*gpioled设备结构体*/
/*该设备需要的*/
struct gpioled_dev
{
    dev_t devid; /*设备号 32位*/
    int major ;/*主设备号 高12位*/
    int minor ;/*次设备号 低20位*/
    /*cdev表示字符设备 结构体初始化就是初始化结构体变量*/
    struct cdev cdev ;//包含了操作函数集 和 设备号 包含头文件 #include <linux/cdev.h>
    struct class *class; /*类*/
    struct device *device; /*设备*/
    struct device_node *nd; //节点
    int led_gpio ;//led 所使用的 GPIO 编号

    //自旋锁
    int dev_status;//设备状态 标记驱动有没有被其他应用使用  

    //0表示设备可以使用 1 表示设备不可使用
    spinlock_t lock;//
    unsigned long irqflag;

};
struct gpioled_dev gpioled;

————————————

/*入口 -注册字符设备*/中注册设备号 /cdev 具体的字符设备操作集合 向内核添加字符设备 自动创建设备节点 不变

在驱动入口函数中:

1、  /*初始化自旋锁*/
        spin_lock_init(&gpioled.lock);
         gpioled.dev_status = 0;//标记驱动可被使用

————————————————————————

在操作函数集中

static struct file_operations gpioled_fops =
{
    .owner = THIS_MODULE ,
    .open = gpioled_open,
    .write = gpioled_write,
    .release = gpioled_release,
 
};

——————————————————————

static int gpioled_open(struct inode *inode, struct file *filp)
{
    //私有数据
    filp->private_data = &gpioled ;

    //对自旋锁进行操作

    /*先进行加锁*/

    //保存中断状态,禁止本地中断,并获取自旋锁。
    spin_lock_irqsave(&gpioled.lock,gpioled.irqflag);


    /*加锁解锁之间是临界区 既代码保护区 保护的是数据*/
    /*dev_status 的保护*/
    if(gpioled.dev_status)//驱动不可用 = 1
    {
         /*先解锁  再 退出*/
       
/将中断状态恢复到以前的状态,并且激活本地中断, 释放自旋锁
        spin_unlock_irqrestore(&gpioled.lock,gpioled.irqflag);
        return -EBUSY;

    }
    /*能用 之后确保使用1次所以加1 标记被使用*/
    gpioled.dev_status++;

    /*解锁*/

//将中断状态恢复到以前的状态,并且激活本地中断, 释放自旋锁
    spin_unlock_irqrestore(&gpioled.lock,gpioled.irqflag);

    return 0;
}

——————————————————————————

static int gpioled_release(struct inode *inode, struct file *filp)
{
    //可以通过私有数据访问struct gpioled_dev gpioled
    struct gpioled_dev *dev = (struct gpioled_dev*)filp->private_data;

    /*涉及到对数据的操作 所以也是要使用自旋锁*/
    /*先进行加锁*/

    spin_lock_irqsave(&dev->lock, dev->irqflag);

//表示应用程序的 执行完毕
 
  /*加锁解锁之间是临界区 既代码保护区 保护的是数据*/
    /*dev_status 的保护*/
    if(dev->dev_status)//驱动不可用 = 1
    {
        dev->dev_status--;/*标记驱动可以使用 在驱动打开的时候+1之后要-1*/

    }


    /*解锁*/
    spin_unlock_irqrestore(&dev->lock,dev->irqflag);  
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值