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

亦凉 2022-10-17 00:54 555阅读 0赞

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 语言来讲很简单,直接就是:

  1. a=3

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

  1. ldr r0, =0X30000000 /* 变量 a 地址 */
  2. ldr r1, = 3 /* 要写入的值 */
  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 文件中,定义如下:

  1. /*32位系统下*/
  2. typedef struct {
  3. int counter;
  4. } atomic_t;
  5. /*64位系统下*/
  6. #ifdef CONFIG_64BIT
  7. typedef struct {
  8. long counter;
  9. } atomic64_t;
  10. #endif

1.4、原子操作的使用

1.4.1、定义

  1. atomic_t a; //定义 a

1.4.2、初始化

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

  1. 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、事例

  1. atomic_t v = ATOMIC_INIT(0); /* 定义并初始化原子变零 v=0 */
  2. atomic_set(10); /* 设置 v=10 */
  3. atomic_read(&v); /* 读取 v 的值,肯定是 10 */
  4. 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 位原来的值。

发表评论

表情:
评论列表 (有 0 条评论,555人围观)

还没有评论,来说两句吧...

相关阅读

    相关 原子操作

     原子操作 在多线程编程中,访问同一个变量时需要利用锁来限制同一个时刻只有一个线程访问,否则会遇到未知的错误,最常用的锁有信号量、线程锁。系统也提供了一些原子操作来实现

    相关 原子操作

    原子操作指的是在执行过程中不会被别的代码中断的操作 位和整型变量原子操作依赖底层CPU的原子操作来实现,因此所有这些函数都与CPU架构密切相关 整型原子操作 定义

    相关 原子操作介绍

    原子操作类介绍 因为java的多线程的缘故,当多个线程同时修改同一个变量,导致最后变量得不到预期的结果。原因是多线程操作,导致变量操作缺少原子性,所以导致变量在多线程的操