【Linux驱动开发】015 原子操作

一、原子操作的定义

🐠 定义

原子操作就是指一个操作或者一组操作,这些操作是不可中断的,且在执行期间不会被其他线程或进程中断。

🐠 示例

举个例子: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 设置为了 20,而并不是要求的 10!线程 B 没有问题。这就是一个最简单的设置变量值的并发与竞争的例子。

要解决这个问题就要保证上述三行汇编代码作为一个整体运行,也就是作为一个原子存在。

Linux 内核提供了一组原子操作 API 函数来完成此功能,Linux 内核提供了两组原子操作 API 函数,一组是对整形变量进行操作的,一组是对位进行操作的。

二、原子整形操作 API 函数

🐠 原子变量

Linux 内核定义了叫做 atomic_t 的结构体来完成整形数据的原子操作:

typedef struct { 
    int counter; 
} atomic_t; 

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

atomic_t    a;  //定义原子变量 a,可以理解为上面的a,通过a可以调用原子函数

也可以在定义原子变量的时候给原子变量赋初值:

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

🐠 原子整形操作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,如果结果为负就返回真,否则返回假 

🐠 示例

原子变量和相应的 API 函数使用示例:

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

三、原子位操作 API 函数

原子位操作不像原子整形变量那样有个 atomic_t 结构体,原子位操作是直接对内存进行操作:

函数 描述
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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Kashine

你的鼓励将是我创作的动力

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

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

打赏作者

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

抵扣说明:

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

余额充值