Linux--原子操作(介绍及其操作函数集)

1、原子操作

1.1、概念

原子操作提供了指令原子执行,中间没有中断。就像原子被认为是不可分割颗粒一样,原子操作(atomic operation)是不可分割的操作。

1.2、事例

假如现在要对无符号整形变量 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 变量中 */

从上述代码可以看出, C 语言里面简简单单的一句“a=3”,编译成汇编文件以后变成了 3 句,那么程序在执行的时候肯定是按照示例代码 47.2.1.1 中的汇编语句一条一条的执行。假设现在线程 A要向 a 变量写入 3 这个值,而线程 B 也要向 a 变量写入 30 这个值,我们理想中的执行顺序如下图 所示:
在这里插入图片描述

理想情况如上图所示,A线程先给a赋值给30,B线程再给a赋值为30.

但是它一共是六条语句,实际执行时可能是如下情况:

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

1.3、原子操作结构体介绍

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

/*32位系统下*/
typedef struct {
	int counter;
} atomic_t;


/*64位系统下*/
#ifdef CONFIG_64BIT
typedef struct {
	long counter;
} atomic64_t;
#endif

1.4、原子操作的使用

1.4.1、定义

atomic_t a; //定义 a

1.4.2、初始化

可以通过宏 ATOMIC_INIT 向原子变量赋初值。

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

1.5、原子整形操作 API 函数

对原子变量进行操作,比如读、写、增加、减少等等, Linux 内核提供了大量的原子操作 API 函数,如下表 所示:

API含义
ATOMIC_INIT(int i)定义原子变量的时候对其初始化。
int atomic_read(atomic_t *v)读取 v 的值,并且返回。
void atomic_set(atomic_t *v, int i)向 v 写入 i 值。
void atomic_add(int i, atomic_t *v)给 v 加上 i 值。
void atomic_sub(int i, atomic_t *v)从 v 减去 i 值。
void atomic_inc(atomic_t *v)给 v 加 1,也就是自增。
void atomic_dec(atomic_t *v)从 v 减 1,也就是自减
int atomic_dec_return(atomic_t *v)从 v 减 1,并且返回 v 的值。
int atomic_inc_return(atomic_t *v)给 v 加 1,并且返回 v 的值。
int atomic_sub_and_test(int i, atomic_t *v)从 v 减 i,如果结果为 0 就返回真,否则返回假
int atomic_dec_and_test(atomic_t *v)从 v 减 1,如果结果为 0 就返回真,否则返回假
int atomic_inc_and_test(atomic_t *v)给 v 加 1,如果结果为 0 就返回真,否则返回假
int atomic_add_negative(int i, atomic_t *v)给 v 加 i,如果结果为负就返回真,否则返回假

相应的也提供了 64 位原子变量的操作 API 函数,这里我们就不详细讲解了,和表 中的 API 函数有用法一样,只是将“atomic_”前缀换为“atomic64_”,将 int 换为 long long。如果使用的是 64 位的 SOC,那么就要使用 64 位的原子操作函数。

1.5.1、事例

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 */

1.6、原子位操作 API 函数

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

API描述
void set_bit(int nr, void *p)将 p 地址的第 nr 位置 1。
void clear_bit(int nr,void *p)将 p 地址的第 nr 位清零。
void change_bit(int nr, void *p)将 p 地址的第 nr 位进行翻转。
int test_bit(int nr, void *p)获取 p 地址的第 nr 位的值。
int test_and_set_bit(int nr, void *p)将 p 地址的第 nr 位置 1,并且返回 nr 位原来的值。
int test_and_clear_bit(int nr, void *p)将 p 地址的第 nr 位清零,并且返回 nr 位原来的值。
int test_and_change_bit(int nr, void *p)将 p 地址的第 nr 位翻转,并且返回 nr 位原来的值。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值