原子操作简介
原子操作(Atomic operation)是指一种不可分割的操作,要么完全执行成功,要么完全不执行。
原子操作的执行过程中不允许有任何中断,如果出现了中断,那么操作的结果就无法保证。
原子操作通常用于多线程编程中,保证多个线程之间的并发执行不会出现数据竞争等问题。
在实现原子操作时,通常使用硬件指令或者操作系统提供的原子操作函数来保证操作的原子性。
在应用层面,原子操作可以用于实现一些高级的同步和并非控制。例如,在多线程编程中,如果多个线程都需要访问同一个共享变量,为了避免数据竞争问题,可以使用原子操作来保证对该变量的操作是原子的。
movl i, %eax //内存访问,读取i变量到CPU的eax寄存器
add $1, %exa //修改寄存器的值
movl %eax, i //把寄存器中的值写回内存
i++操作在编译后会被编译成三条指令,所以这三条指令之间是可能被系统调度、中断等事件打断的,因而我们在一些场景就需要一气呵成完成上述操作,原子操作就具备这样的能力。
原子操作的优点
在RT-Thread中我们可以采取开关全局中断,调度器上锁等方式对临界区资源进行保护,其它OS也会提供类似操作。
若采用原子操作后,我们可以提高临界区代码的执行效率,大幅提升系统的运行效率,同时也会在一定程度上降低编程的复杂度,下文是一个简单变量自增的示例:
采用开关全局中断的方式实现
int a = 5;
level = rt_hw_interrupt_disable();
a++;
rt_hw_interrupt_enable(level);
采用原子操作API
int a = 5;
rt_atomatic_add($a,1);
显然采用了原子操作的方式更加简单一些,且避免了开关全局中断带来的性能损失。
RT-Thread原子操作的实现与使用方法
RT-Thread对32-bit的ARM、32-bit的RISC-V与64-bit的RISC-V中支持原子操作的内核提供了原子操作支持,使用对应平台的原子操作指令与相关指令实现,默认支持,无需用户关心实现,用户使用时仅需在工程包含rtatomatic.h即可使用该文件提供的原子操作API。
RT-Thread 原子操作 API
- rt_atomic_t rt_hw_atomic_load(volatile rt_atomic_t *ptr),原子的从 ptr 地址加载一个字
- void rt_atomic_store(volatile rt_atomic_t *ptr, rt_atomic_t val),原子的将 val 写入 ptr 地址
- rt_atomic_t rt_atomic_exchange(volatile rt_atomic_t *ptr, rt_atomic_t val),原子的将 ptr 地址处的值替换为 val
- rt_atomic_t rt_atomic_add(volatile rt_atomic_t *ptr, rt_atomic_t val),原子的将 ptr 地址处的值与 val 相加
- rt_atomic_t rt_atomic_sub(volatile rt_atomic_t *ptr, rt_atomic_t val),原子的将 ptr 地址处的值与 val 相减
- rt_atomic_t rt_atomic_xor(volatile rt_atomic_t *ptr, rt_atomic_t val),原子的将 ptr 地址处的值与 val 按位异或
- rt_atomic_t rt_atomic_and(volatile rt_atomic_t *ptr, rt_atomic_t val),原子的将 ptr 地址处的值与 val 按位与
- rt_atomic_t rt_atomic_or(volatile rt_atomic_t *ptr, rt_atomic_t val),原子的将 ptr 地址处的值与 val 按位或
- rt_atomic_t rt_atomic_flag_test_and_set(volatile rt_atomic_t *ptr),原子的将 ptr 地址处的值置 1
- void rt_atomic_flag_clear(volatile rt_atomic_t *ptr),原子的将 ptr 地址处的值清 0
- rt_atomic_t rt_atomic_compare_exchange_strong(volatile rt_atomic_t *ptr, rt_atomic_t *old, rt_atomic_t new),原子的将 ptr 地址处的值与 val 进行比较与交换,并返回比较结果