解决竞态问题之原子操作

原子操作简介:

原子操作就是:指不能再进一步分割的操作
一般原子操作用于:<整形操作>或者<位操作>。
假如现在要对无符号整形变量 a 赋值,值为3,对于 C 语言来讲很简单,直接就是:a = 3

但是 C 语言要先编译为成汇编指令,ARM 架构不支持直接对寄存器进行读写操作,比如 要借助寄存器 R0、R1 等来完成赋值操作。假设变量 a 的地址为 0X3000000,“a=3”这一行 C 语言可能会被编译为如下所示的汇编代码:
ldr r0, =0X30000000 /* 变量 a 地址 /
ldr r1, = 3 /
要写入的值 /
str r1, [r0] /
将 3 写入到 a 变量中 */

假设现在线程 A要向 a 变量写入 10 这个值,而线程 B 也要向 a 变量写入 20 这个值,我们理想中的执行顺序:
在这里插入图片描述
确实可以实现线程 A 将 a 变量设置为 10,线程 B 将 a 变量设置为 20。但是实际上的执行流程可能如图 下 所示:
在这里插入图片描述

线程 A 最终将变量 a 设置为了 20,而并不是要求的 10!线程B没有问题。这就是一个最简单的设置变量值的并发与竞争的例子,要解决这个问题就要保证代码a=10中的三行汇编指令作为一个整体运行,也就是作为一个原子存在。Linux内核提供了一组原子操作 API 函数来完成此功能,Linux 内核提供了两组原子操作 API 函数,一组是对整形变量进行操作的,一组是对位进行操作的,我们接下来看一下这些 API 函数。

1、原子整形操作 API 函数:

Linux 内核定义了叫做 atomic_t 的结构体来完成整形数据的原子操作,在使用中用原子变量来代替整形变量,此结构体定义在 include/linux/types.h 文件中,定义如下:

 typedef struct {
    int counter;  
   }atomic_t;

如果要使用原子操作 API 函数,首先要先定义一个 atomic_t 类型的变量,如下所示:

atomic_t a; //定义 a

也可以在定义原子变量的时候通过宏 ATOMIC_INIT() 向原子变量赋初值给原子变量赋初值,如下所示:

atomic_t b = ATOMIC_INIT(0); //定义原子变量 b 并赋初值为 0

原子变量有了,接下来就是对原子变量进行操作,比如读、写、增加、减少等等,Linux 内
核提供了大量的原子操作 API 函数,如下所示:
在这里插入图片描述

注意:
如果使用 64 位的 SOC 的话,就要用到 64 位的原子变量,Linux 内核也定义了 64 位原子 结构体,如下所示:

typedef struct {      
   long long counter; 
} atomic64_t;

相应的也提供了 64 位原子变量的操作 API 函数,这里就不讲了,和表中的 API
函数有用法一样,只是将“atomic_”前缀换为“atomic64_”,将 int 换为 long long

32 位的架构原子变量和相应的 API 函数使用起来很简单,参考如下示例:

atomic_t v = ATOMIC_INIT(0); /* 定义并初始化原子变零 v=0 */
atomic_set(10); /* 设置 v=10 */
atomic_read(&v); /* 读取 v 的值,肯定是 10 */
atomic_inc(&v); /* v 的值加 1,v=11 */

2、原子位操作 API 函数:

位操作也是很常用的操作,Linux 内核也提供了一系列的原子位操作 API 函数,只不过原 子位操作不像原子整形变量那样有个 atomic_t 的数据结构,原子位操作是直接对内存进行操作, API 函数如表 下 所示:在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

那肯定是很多年以后!

你的鼓励就我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值