• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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```