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

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

Linux--原子操作

1、原子操作1.1、概念1.2、事例1.3、原子操作结构体介绍1.4、原子操作的使用1.4.1、定义1.4.2、初始化

1.5、原子整形操作 API 函数1.5.1、事例

1.6、原子位操作 API 函数

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 位原来的值。

相关推荐

同日迎来淘汰赛 日本韩国能否延续奇迹?
从零开始,探索Golang学习之路:掌握这门语言需要多久?揭秘高效学习策略
怎么在手机上把照片缩小,内存不够看这里!