【Linux】并发解决(上)-中断屏蔽,原子操作

在这里插入图片描述

🔥博客主页:PannLZ
🎋系列专栏:《Linux系统之路》
😘欢迎关注:👍点赞🙌收藏✍️留言

文章目录

    • 并发解决
      • 1.中断屏蔽
      • 2.原子操作
        • 2.1整形原子操作
        • 2.2位原子操作
        • 原子变量使用例子


并发解决

1.中断屏蔽

单CPU范围内避免竞态的一种简单而有效的方法是在进入临界区之前屏蔽系统的中断,但是在驱动编程中不值得推荐,驱动通常需要考虑跨平台特点而不假定自己在单核上运行。

CPU一般都具备屏蔽中断和打开中断的功能,这项功能可以保证正在执行的内核执行路径不被中断处理程序所抢占,防止某些竞态条件的发生。具体而言,中断屏蔽将使得中断与进程之间的并发不再发生,而且,由于Linux内核的进程调度等操作都依赖中断来实现,内核抢占进程之间的并发也得以避免了。

中断屏蔽的使用方法为:

local_irq_disable() /* 屏蔽中断*/
. . .
critical section /* 临界区*/
. . .
local_irq_enable() /* 开中断*/

local_irq_disable()和local_irq_enable()都只能禁止和使能本CPU内的中断,因此,并不能解决SMP多CPU引发的竞态。因此,单独使用中断屏蔽通常不是一种值得推荐的避免竞态的方法(换句话说,驱动中使用`local_irq_disable/enable()通常意味着一个bug),它适合与下文将要介绍的自旋锁联合使用。

2.原子操作

原子操作可以保证对一个整型数据的修改是排他性的。

Linux内核提供了一系列函数来实现内核中的原子操作,这些函数又分为两类,分别针对整型变量进行原子操作。位和整型变量的原子操作都依赖于底层CPU的原子操作,与CPU架构密切相关。

对于ARM处理器而言,底层使用LDREXSTREX指令,比如atomic_inc()底层的实现会调用到atomic_add(),其代码如下:

static inline void atomic_add(int i, atomic_t *v)
{
unsigned long tmp;
int result;
prefetchw(&v->counter);
__asm__ __volatile__("@ atomic_add\n"
"1: ldrex %0, [%3]\n"
" add %0, %0, %4\n"
" strex %1, %0, [%3]\n"
" teq %1, #0\n"
" bne 1b"
: "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
: "r" (&v->counter), "Ir" (i)
: "cc");
}

LDREX(Load with )用于读取内存中的值,并标记对该段内存的独占访问

STREX(Store with Exclusive Access)在更新内存数值时,会检查该段内存是否已经被标记为独占访问,并以此来决定是否更新内存中的值

STREX Rx, Ry, [Rz]

如果执行这条指令的时候发现已经被标记为独占访问了,则将寄存器Ry中的值更新到寄存器Rz指向的内存,并将寄存器Rx设置成0。指令执行成功后,会将独占访问标记位清除1。而如果执行这条指令的时候发现没有设置独占标记,则不会更新内存,且将寄存器Rx的值设置成1。(也就是将操作结果(成功0或失败1)写入Rx寄存器)

这两个指令的精髓就是,无论有多少个处理器,有多少个地方会申请对同一个内存段进行操作,保证只有最早的更新可以成功,这之后的更新都会失败。失败了就证明对该段内存有访问冲突了1。

BNE是一个条件跳转指令。如果上一条指令的结果不为零(即Z标志位为0),那么程序会跳转到标签1b所在的代码位置。在这里,1b是一个标签,b表示向后跳转,1表示跳转到第一个出现的这样的标签

2.1整形原子操作
//1.设置原子变量的值
void atomic_set(atomic_t *v, int i); /* 设置原子变量的值为i */
atomic_t v = ATOMIC_INIT(0); /* 定义原子变量v并初始化为0 *///2.获取原子变量的值
atomic_read(atomic_t *v); /* 返回原子变量的值*///3.原子变量加/减
void atomic_add(int i, atomic_t *v); /* 原子变量增加i */
void atomic_sub(int i, atomic_t *v); /* 原子变量减少i *///4.原子变量自增/自减
void atomic_inc(atomic_t *v); /* 原子变量增加1 */
void atomic_dec(atomic_t *v); /* 原子变量减少1 *///5.操作并测试
int atomic_inc_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i, atomic_t *v);//上述操作对原子变量执行自增、自减和减操作后(注意没有加),测试其是否为0,为0返回true,否则返回false。//6.操作并返回
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);
//上述操作对原子变量进行加/减和自增/自减操作,并返回新的值。
2.2位原子操作
//1.设置位
void set_bit(nr, void *addr);
//上述操作设置addr地址的第nr位,所谓设置位即是将位写为1。//2.清除位
void clear_bit(nr, void *addr);
//上述操作清除addr地址的第nr位,所谓清除位即是将位写为0。//3.改变位
void change_bit(nr, void *addr);
//上述操作对addr地址的第nr位进行反置。//4.测试位
test_bit(nr, void *addr);
//上述操作返回addr地址的第nr位。//5.测试并操作位
int test_and_set_bit(nr, void *addr);
int test_and_clear_bit(nr, void *addr);
int test_and_change_bit(nr, void *addr);
//上述test_and_xxx_bit(nr,void*addr)操作等同于执行test_bit(nr,void*addr)后再执行xxx_bit(nr,void*addr)。
原子变量使用例子

使用原子变量使设备只能被一个进程打开

static atomic_t xxx_available = ATOMIC_INIT(1); /* 定义原子变量*/static int xxx_open(struct inode *inode, struct file*filp)
{...if (!atomic_dec_and_test(&xxx_available)) {atomic_inc(&xxx_available);return - EBUSY; /* 已经打开*/
}
...return 0; /* 成功*/
}static int xxx_release(struct inode *inode, struct file*filp)
{atomic_inc(&xxx_available); /* 释放设备*/return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/470418.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VSCode python插件:找不到自定义包导致语法解析失败

众所周知,在python源码中,我们可以通过 sys.path.append("path-to-lib") 来为python解释器添加自定义包的寻找路径。 但是vscode的默认python插件可没法聪明到根据这句话去找这个包,这就会导致后续代码中使用了这个库的部分无法享…

【前端实战小项目】学成在线网页制作

文章目录 1.项目准备1.1 项目目录 2.头部区域2.1 头部区域布局2.2 logo制作2.2 导航制作技巧(nav)2.3搜索区域(search)2.3用户区域(user区域) 3.banner区域3.1 总体布局3.2 左侧侧导航(left)3.3 右侧课程表(left) 4.精品推荐区域(recommend)5.精品课程( course)6.前端开发工程师…

『 C++ - STL 』位图(BitMap)与布隆过滤器(Bloom Filter)

文章目录 🧸 位图(BitMap)概念🧸 位图的实现🪅 总体框架🪅 位图的数据插入🧩 左移操作与右移操作的区别 🪅 位图的数据删除🪅 位图的数据查找🪅 位图整体代码(供参考) 🧸…

蓝桥杯嵌入式学习记录——PWM输出

目录 一、PWM原理介绍 二、学习目的 三、cubeMX的配置 四、PWM输出代码 一、PWM原理介绍 PWM(Pulse Width Modulation,脉宽调制)是一种通过改变信号的脉冲宽度来控制电平的技术。它通过调整脉冲信号的占空比(高电平时间与周期…

Spring 用法学习总结(四)之 JdbcTemplate 连接数据库

🐉目录 9 JdbcTemplate 9 JdbcTemplate Spring 框架对 JDBC 进行了封装,使用 JdbcTemplate 方便实现对数据库操作 相关包: 百度网盘链接https://pan.baidu.com/s/1Gw1l6VKc-p4gdqDyD626cg?pwd6666 创建properties配置文件 💥注意…

Mybatis Day02

增删改查 环境准备 创建一个emp表创建一个新的springboot工程,选择mysql、lombok、mybatis依赖application.properties中引入数据库连接信息创建对应的实体类Emp准备Mapper接口EmpMapper,mapper代表程序运行时自动创建接口的代理对象,并放入…

[嵌入式AI从0开始到入土]14_orangepi_aipro小修补含yolov7多线程案例

[嵌入式AI从0开始到入土]嵌入式AI系列教程 注:等我摸完鱼再把链接补上 可以关注我的B站号工具人呵呵的个人空间,后期会考虑出视频教程,务必催更,以防我变身鸽王。 第1期 昇腾Altas 200 DK上手 第2期 下载昇腾案例并运行 第3期 官…

大学的英语搜题软件有哪些?大学生必知技巧:如何更好地利用搜题工具? #笔记#学习方法

在这个信息爆炸的时代,合理利用学习工具可以帮助我们过滤和获取有用的知识。 1.智能翻译官 这是一款多语言在线翻译神器,除了最基础的英语以外,还支持日语、德语、俄语、法语等几十种语言文本翻译和拍照翻译,并且还支持语音翻译…

c语言操作符(上)

目录 ​编辑 原码、反码、补码 1、正数 2、负数 3、二进制计算1-1 移位操作符 1、<<左移操作符 2、>>右移操作符 位操作符&、|、^、~ 1、&按位与 2、|按位或 3、^按位异或 特点 4、~按位取反 原码、反码、补码 1、正数 原码 反码 补码相同…

基于BitVM的乐观 BTC bridge

1. 引言 前序博客&#xff1a; 区块链互操作协议Bitcoin Bridge&#xff1a;治愈还是诅咒&#xff1f;BitVM&#xff1a;Bitcoin的链下合约 基于BitVM的乐观 BTC bridge&#xff1a; Trust-minimized two-way peg 机制 BitVM BTC bridge背后的主要思想是&#xff1a; 为比…

FT2232调试记录(3)

FT2232调试记录&#xff08;1&#xff09;: FT2232调试记录&#xff08;2&#xff09;: FT2232调试记录&#xff08;3&#xff09;: FT2232 SPI读写函数: 参照SPI提供的文档&#xff1a; 工程&#xff1a; SPI 写函数&#xff1a; FT_STATUS write_byte(FT_HANDLE handle…

java8使用流

这种处理数据的方式很有用&#xff0c;因为你让Stream API管理如何处理数据。这样StreamAPI就可以在背后进行多种优化。此外&#xff0c;使用内部迭代的话&#xff0c;SteamAPI可以决定并行运行你的代码。这要是用外部迭代的话就办不到了&#xff0c;因为你只能用单一线程挨个迭…