Linux驱动开发——并发和竞态(原子操作方式的使用⑤)

简介: Linux驱动开发——并发和竞态(原子操作方式的使用⑤)

文章目录

解决竞态引起异常方法之原子操作

原子操作特点

位原子操作

使用方式比较:

整形原子操作

使用方式比较:

示例代码


解决竞态引起异常方法之原子操作

原子操作特点

原子操作能够解决所有的竞态问题。

Linux内核原子操作分为两类:位原子操作和整形原子操作 。


位原子操作

位原子操作 = 位操作的过程具有原子性 = 对共享资源进行位操作的过程中不允许发生CPU资源的切换。

应用场景:如果在代码中发现需要对共享资源进行位操作,可以考虑使用内核提供的位原子操作相关的函数,调用这些内核提供的函数对共享资源进行位操作,整个过程是不会发送CPU资源切换的。(应用场景相对之前其他方式很狭窄)


Linux内核提供的位原子操作的相关宏或者函数:

set_bit/clear_bit/change_bit/test_bit/各种组合函数


void set_bit(int nr, void *addr)
//功能:将addr地址内数据的第nr位置1(nr从0开始)
void clear_bit(int nr, void *addr)
//功能:将addr地址内数据的第nr位清0(nr从0开始)
void change_bit(int nr, void *addr)
//功能:将addr地址内数据的第nr位反转(nr从0开始)
int test_bit(int nr, void *addr)
//功能:获取addr地址内数据的第nr位的值(nr从0开始)


  • 切记:以上函数不要被表面给欺骗,它们的内在核心在于在addr 地址内数据进行位操作过程不允许发生CPU资源切换, 以上函数的操作对象一定是共享资源,虽说也可以利用以上函数对非共享资源进行位操作,但是致命的后果就是代码执行效率降低

使用方式比较:

原子操作相对之前其他几种方式,使用场景很小,仅仅是对单个共享资源可用或者是对共享资源进行位操作可用。

例如:我们将一段简单的共享资源操作进行各种方式的修改就能看出使用方式不同的情况:


  • 没有竞态问题解决处理的原代码:
int open_cnt = 1; //共享资源
open_cnt &= ~(1 << 1); //位操作减一

  • 采用中断屏蔽
local_irq_save
  open_cnt &= ~(1 << 1); 
  local_irq_restore

  • 采用衍生自旋锁
  •  
spin_lock_irqsave
      open_cnt &= ~(1 << 1); 
      spin_unlock_irqrestore

  • 采用信号量
down
      open_cnt &= ~(1 << 1); 
      up

  • 采用位原子操作
  •  
clear_bit(1, &open_cnt)

整形原子操作

整形原子操作 = 整形数的操作具有原子性 = 对驱动代码中的共享资源进行整形操作的过程中,不允许发生CPU资源的切换。


应用场合:如果将来驱动代码中发现有对共享资源进行整形操作(+/-/++/–等),并且考虑到竞态问题,可以考虑使用linux内核提供的整形原子操作。


linux内核对于整形原子操作专门提供了一个数据类型:atomic_t, 严重类似int型


  • atomic_t数据类型定义的变量称之为整形原子变量,例如:atomic_t open_cnt;

如果对整形原子操作进行访问,必须用内核提供的相关操作宏或者函数,调用这写函数才能保证原子性:


//各种组合函数
atomic_add/atomic_sub/atomic_inc/atomic_dec/
atomic_dec_and_test
//对整形变量进行减1操作,然后判断其值是否为0,
//如果为0,返回真,如果非0,返回假


使用方式比较:

  • 没有竞态问题解决处理的原代码:
int open_cnt = 1; //共享资源
--open_cnt;


  • 采用中断屏蔽

local_irq_save
  --open_cnt;
  local_irq_restore


  • 采用衍生自旋锁

spin_lock_irqsave
  --open_cnt;
  spin_unlock_irqrestore


  • 采用信号量
down(&sema);
  --open_cnt;
  up(&sema);


  • 采用整形原子操作
//定义初始化一个整形原子变量为1
  atomic_t open_cnt = ATOMIC_INIT(1); 
  //类似int open_cnt = 1
  atomic_dec(&open_cnt); 
  //类似--open_cnt,此操作具有原子性


  • 切记切记:整形原子操作表面看似是整形操作,但认清本质是对整形操作的过程具有原子性,不会发生CPU资源的切换。

示例代码

  • led_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
static atomic_t open_cnt = ATOMIC_INIT(1); //记录LED打开的状态开关
static int led_open(struct inode *inode, 
                        struct file *file)
{
    if(!atomic_dec_and_test(&open_cnt)) {
        printk("设备已被打开!\n");
        atomic_inc(&open_cnt);
        return -EBUSY;//返回设备忙错误码
    }
    printk("设备打开成功!\n");
    return 0; //open返回成功
}
static int led_close(struct inode *inode, 
                        struct file *file)
{
    atomic_inc(&open_cnt);
    return 0;
}
//定义初始化硬件操作接口对象
static struct file_operations led_fops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .release = led_close
};
//定义初始化混杂设备对象
static struct miscdevice led_misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "myled",
    .fops = &led_fops
};
static int led_init(void)
{
    misc_register(&led_misc);
    return 0;
}
static void led_exit(void)
{
    misc_deregister(&led_misc);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");


  • led_test.c 和 Makefile同前几篇文章中一样,并无改动。


  • 执行结果:


20200101084935147.png


20200101084935147.png

相关文章
|
1月前
|
缓存 Linux 开发者
Linux内核中的并发控制机制:深入理解与应用####
【10月更文挑战第21天】 本文旨在为读者提供一个全面的指南,探讨Linux操作系统中用于实现多线程和进程间同步的关键技术——并发控制机制。通过剖析互斥锁、自旋锁、读写锁等核心概念及其在实际场景中的应用,本文将帮助开发者更好地理解和运用这些工具来构建高效且稳定的应用程序。 ####
45 5
|
1月前
|
Linux 数据库
Linux内核中的锁机制:保障并发操作的数据一致性####
【10月更文挑战第29天】 在多线程编程中,确保数据一致性和防止竞争条件是至关重要的。本文将深入探讨Linux操作系统中实现的几种关键锁机制,包括自旋锁、互斥锁和读写锁等。通过分析这些锁的设计原理和使用场景,帮助读者理解如何在实际应用中选择合适的锁机制以优化系统性能和稳定性。 ####
61 6
|
2月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
114 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
3月前
|
存储 Linux 开发工具
如何进行Linux内核开发【ChatGPT】
如何进行Linux内核开发【ChatGPT】
|
4月前
|
Java Linux API
Linux设备驱动开发详解2
Linux设备驱动开发详解
56 6
|
4月前
|
消息中间件 算法 Unix
Linux设备驱动开发详解1
Linux设备驱动开发详解
61 5
|
4月前
|
移动开发 监控 网络协议
在Linux中,如何查看 http 的并发请求数与其 TCP 连接状态?
在Linux中,如何查看 http 的并发请求数与其 TCP 连接状态?
|
3月前
|
Linux
linux内核原子操作学习
linux内核原子操作学习
|
4月前
|
网络协议 Linux
在Linux中,如何查看 http 的并发请求数与其 TCP 连接状态?
在Linux中,如何查看 http 的并发请求数与其 TCP 连接状态?