1# 原子操作 2 3 4## 基本概念 5 6在支持多任务的操作系统中,修改一块内存区域的数据需要“读取-修改-写入”三个步骤。然而同一内存区域的数据可能同时被多个任务访问,如果在修改数据的过程中被其他任务打断,就会造成该操作的执行结果无法预知。 7 8使用开关中断的方法固然可以保证多任务执行结果符合预期,但是显然这种方法会影响系统性能。 9 10ARMv6架构引入了LDREX和STREX指令,以支持对共享存储器更缜密的非阻塞同步。由此实现的原子操作能确保对同一数据的“读取-修改-写入”操作在它的执行期间不会被打断,即操作的原子性。 11 12 13## 运行机制 14 15OpenHarmony系统通过对ARMv6架构中的LDREX和STREX进行封装,向用户提供了一套原子性的操作接口。 16 17- LDREX Rx, [Ry] 18 读取内存中的值,并标记对该段内存的独占访问: 19 - 读取寄存器Ry指向的4字节内存数据,保存到Rx寄存器中。 20 - 对Ry指向的内存区域添加独占访问标记。 21 22- STREX Rf, Rx, [Ry] 23 检查内存是否有独占访问标记,如果有则更新内存值并清空标记,否则不更新内存: 24 - 有独占访问标记 25 - 将寄存器Rx中的值更新到寄存器Ry指向的内存。 26 - 标志寄存器Rf置为0。 27 - 没有独占访问标记 28 - 不更新内存。 29 - 标志寄存器Rf置为1。 30 31- 判断标志寄存器 32 - 标志寄存器为0时,退出循环,原子操作结束。 33 - 标志寄存器为1时,继续循环,重新进行原子操作。 34 35 36## 开发指导 37 38 39### 接口说明 40 41OpenHarmony LiteOS-A内核的原子操作模块提供以下几种功能。 42 43 **表1** 原子操作接口说明 44 45| 功能分类 | 接口**名称** | 描述 | 46| ------------ | ----------------------- | --------------------------- | 47| 读 | LOS_AtomicRead | 读取32bit原子数据 | 48| 读 | LOS_Atomic64Read | 读取64bit原子数据 | 49| 写 | LOS_AtomicSet | 设置32bit原子数据 | 50| 写 | LOS_Atomic64Set | 设置64bit原子数据 | 51| 加 | LOS_AtomicAdd | 对32bit原子数据做加法 | 52| 加 | LOS_Atomic64Add | 对64bit原子数据做加法 | 53| 加 | LOS_AtomicInc | 对32bit原子数据做加1 | 54| 加 | LOS_Atomic64Inc | 对64bit原子数据做加1 | 55| 加 | LOS_AtomicIncRet | 对32bit原子数据做加1并返回 | 56| 加 | LOS_Atomic64IncRet | 对64bit原子数据做加1并返回 | 57| 减 | LOS_AtomicSub | 对32bit原子数据做减法 | 58| 减 | LOS_Atomic64Sub | 对64bit原子数据做减法 | 59| 减 | LOS_AtomicDec | 对32bit原子数据做减1 | 60| 减 | LOS_Atomic64Dec | 对64bit原子数据做减1 | 61| 减 | LOS_AtomicDecRet | 对32bit原子数据做减1并返回 | 62| 减 | LOS_Atomic64DecRet | 对64bit原子数据做减1并返回 | 63| 交换 | LOS_AtomicXchgByte | 交换8bit内存数据 | 64| 交换 | LOS_AtomicXchg16bits | 交换16bit内存数据 | 65| 交换 | LOS_AtomicXchg32bits | 交换32bit内存数据 | 66| 交换 | LOS_AtomicXchg64bits | 交换64bit内存数据 | 67| 先比较后交换 | LOS_AtomicCmpXchgByte | 比较相同后交换8bit内存数据 | 68| 先比较后交换 | LOS_AtomicCmpXchg16bits | 比较相同后交换16bit内存数据 | 69| 先比较后交换 | LOS_AtomicCmpXchg32bits | 比较相同后交换32bit内存数据 | 70| 先比较后交换 | LOS_AtomicCmpXchg64bits | 比较相同后交换64bit内存数据 | 71 72 73### 开发流程 74 75有多个任务对同一个内存数据进行加减或交换等操作时,使用原子操作保证结果的可预知性。 76 77> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** 78> 原子操作接口仅支持整型数据。 79 80 81### 编程实例 82 83**实例描述** 84 85调用原子操作相关接口,观察结果: 86 871. 创建两个任务 88 - 任务一用LOS_AtomicInc对全局变量加100次。 89 - 任务二用LOS_AtomicDec对全局变量减100次。 90 912. 子任务结束后在主任务中打印全局变量的值。 92 93**示例代码** 94 95示例代码如下: 96 97 98``` 99#include "los_hwi.h" 100#include "los_atomic.h" 101#include "los_task.h" 102 103UINT32 g_testTaskId01; 104UINT32 g_testTaskId02; 105Atomic g_sum; 106Atomic g_count; 107 108UINT32 Example_Atomic01(VOID) 109{ 110 int i = 0; 111 for(i = 0; i < 100; ++i) { 112 LOS_AtomicInc(&g_sum); 113 } 114 115 LOS_AtomicInc(&g_count); 116 return LOS_OK; 117} 118 119UINT32 Example_Atomic02(VOID) 120{ 121 int i = 0; 122 for(i = 0; i < 100; ++i) { 123 LOS_AtomicDec(&g_sum); 124 } 125 126 LOS_AtomicInc(&g_count); 127 return LOS_OK; 128} 129 130UINT32 Example_AtomicTaskEntry(VOID) 131{ 132 TSK_INIT_PARAM_S stTask1={0}; 133 stTask1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Atomic01; 134 stTask1.pcName = "TestAtomicTsk1"; 135 stTask1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; 136 stTask1.usTaskPrio = 4; 137 stTask1.uwResved = LOS_TASK_STATUS_DETACHED; 138 139 TSK_INIT_PARAM_S stTask2={0}; 140 stTask2.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Atomic02; 141 stTask2.pcName = "TestAtomicTsk2"; 142 stTask2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; 143 stTask2.usTaskPrio = 4; 144 stTask2.uwResved = LOS_TASK_STATUS_DETACHED; 145 146 LOS_TaskLock(); 147 LOS_TaskCreate(&g_testTaskId01, &stTask1); 148 LOS_TaskCreate(&g_testTaskId02, &stTask2); 149 LOS_TaskUnlock(); 150 151 while(LOS_AtomicRead(&g_count) != 2); 152 PRINTK("g_sum = %d\n", g_sum); 153 154 return LOS_OK; 155} 156``` 157 158**结果验证** 159 160 161``` 162g_sum = 0 163```