1@page aos_kernel 内核编程
2
3[更正文档](https://gitee.com/alios-things/osal_aos/edit/master/README.md)      [贡献说明](https://help.aliyun.com/document_detail/302301.html)
4
5# 概述
6
7AliOS Things是一款支持单处理器上运行多个任务的实时操作系统。操作系统内核只包含用来控制系统资源和处理器对资源的使用的基础功能,来支持系统服务和上层应用的构建和开发。
8AliOS Things操作系统内核特性如下:
9
10   - 可抢占式/协作式任务调度
11   - 多任务管理
12   - 软件定时器
13   - 任务间通信机制包括信号量、互斥量、队列、事件
14   - 内存管理
15   - 时间功能
16   - 随机数
17   - 系统信息
18   - 系统初始化
19   - 工作队列
20## 版权信息
21> Apache license v2.0
22
23## 目录结构
24```tree
25├── include
26│   ├── aos
27│       └── compiler.h	  # 编译相关的宏定义文件
28│       └── errno.h	    	# 兼容posix的错误号定义及相关接口的声明文件
29│       └── kernel.h	    # 操作系统内核接口声明文件
30│       └── list.h	    	# 链表接口声明文件
31│       └── osal_debug.h	# 错误检测与处理相关的声明文件
32│       └── rhino.h	      # 扩展AOS API,目前仅用于POSIX对接
33├── common.c 系统通用接口实现
34├── rhino.c 操作系统内核接口实现
35├── package.yaml    # 编译配置文件
36└── example
37    ├── event_example.c   # 事件使用示例代码
38    ├── mem_example.c   # 内存管理功能使用示例代码
39    ├── mutex_example.c   # 互斥量使用示例代码
40    ├── queue_example.c   # 消息队列使用示例代码
41    ├── sem_example.c   # 信号量使用示例代码
42    ├── task_example.c   # 任务功能使用示例代码
43    ├── timer_example.c   # 定时器功能使用示例代码
44    └── workqueue_example.c   # 工作队列功能使用示例代码
45```
46
47
48## 依赖组件
49
50- rhino
51
52# 任务管理
53## 概述
54任务可以认为是一段独享CPU的运行程序,而应用是完成特定功能的多个任务的集合。任务管理就是为多任务环境中的每个任务分配一个上下文(context)(上下文(context)是指当任务被调度执行的所必不可少的一组数据,包括前任务的CPU指令地址(PC指针),当前任务的栈空间,当前任务的CPU寄存器状态等),在任务相继执行过程中,将切出任务的信息保存在任务上下文中,将切入任务的上下文信息恢复,使其得以执行。为维护任务上下文、状态、栈等相关信息,操作系统内核为每个任务定义了一组数据结构,即任务控制块(Task Control Block),来存放这些信息。
55任务调度负责将处理器资源分配给关键任务,让其优先运行。所以系统中的每个任务需要根据关键程度分配不同的优先级,那些执行关键操作的任务被赋予高优先级,而一些非关键性任务赋予低优先级。当系统发生调度时,高优先级任务可以抢占低优先级任务的处理器资源得到调度执行。系统在无任务可调度时,就运行空闲任务,其优先级最低。
56任务被创建时,需要为任务指定执行体入口地址、栈大小、优先级等信息,创建过程中内核为任务分配任务控制块(TCB)。任务栈空间可以在任务创建时由用户程序指定,也可以由内核根据用户指定大小来动态分配。操作系统内核提供基于软件的栈溢出检测方法,用户可根据需要配置或关闭该功能。
57### 任务状态
58任务状态是反映当前系统中任务所处的状况,操作系统内核需要维护所有任务的当前状态。AliOS Things为了充分描述任务在系统中所处的状态以及引发状态迁移的条件差异,将任务状态分为就绪状态、挂起状态、休眠状态、阻塞状态、运行状态和删除状态。当任务通过aos_task_create()创建时,任务处于挂起状态,当任务通过aos_task_del()删除时,任务处于删除状态,具体的转化过程如下图:
59
60<div align=left display=flex>
61    <img src="https://img.alicdn.com/imgextra/i3/O1CN01LHAt211Dqmoi53SlY_!!6000000000268-2-tps-684-451.png" style="max-width:800px;"/>
62</div>
63
64(1)阻塞状态是指因等待资源而处于等待状态,如调用aos_mutex_lock()获取互斥量时互斥量已经被锁定、调用aos_queue_recv()获取队列数据时队列为空、调用aos_sem_wait()等待信号量时信号量计数为0、调用aos_evnet_get()获取事件时,事件还未发生;
65(2)挂起状态是因任务被其他或自身调用挂起函数aos_task_suspend()后,将无条件地停止运行,被挂起的任务只能通过其他任务调用恢复函数aos_task_resume()使其恢复到就绪状态;
66(3)休眠状态是因任务在调用任务休眠函数aos_msleep()后,进入一种延迟执行的状态,直到休眠时间到达任务被重新调度恢复运行。
67(4)删除状态是因任务运行完成调用任务退出函数aos_task_exit()或被调用任务删除函数aos_task_del()时被设置的一种状态。
68(5)就绪状态是在任务被创建或任务解除阻塞或延迟到期时,任务被置为就绪状态。只有当任务处于就绪状态时,才能被系统调度进入运行状态。
69(6)运行状态是获取处理器执行权的就绪任务所处的状态,单处理器系统,任意时刻只有一个任务可以运行。
70AliOS Things允许任务处于组合状态,如阻塞挂起状态:任务在阻塞状态下,被其他任务挂起,则进入阻塞挂起状态。该状态下,若任务被恢复则保持阻塞状态;若任务解除阻塞则保持挂起状态。
71用户可通过tasklist命令查看任务状态,任务状态描述符号和含义如下表:
72
73| **状态符号** | **描述** |
74| --- | --- |
75| RDY | 任务已在就绪队列或已被调度运行,处于就绪状态或运行状态 |
76| PEND | 任务因等待资源或事件的发生而处于阻塞状态 |
77| SUS | 任务因被其他或自身调用挂起函数aos_task_suspend()后所处的挂起状态 |
78| SLP | 任务处于休眠状态 |
79| PEND_SUS | 任务在阻塞状态下,被其他任务挂起,处于阻塞挂起状态 |
80| SLP_SUS | 任务在休眠状态下,被其他任务挂起,处于休眠挂起状态 |
81| DELETED | 任务处于删除状态 |
82
83### 任务调度
84任务调度是为多任务环境中的就绪任务分配处理器资源。AliOS Things操作系统内核支持两种调度策略:
85
86- 基于优先级的抢占式调度
87
88该调度策略下,每个任务优先级都维护了一个FIFO模式的就绪队列(ready queue),里面包含了当前所有可运行的任务列表,此列表中的任务都处于就绪状态,当处理器可用时,最高优先级的就绪队列的第一个任务得到处理器被执行。当有一个高优先级任务进入就绪队列,正在运行的低优先级任务会立即被唤出,将处理器执行权交给高优先级任务。此种调度机制存在一个潜在问题,如果存在多个优先级相同的任务,其中一个任务强占处理器,则其他同等优先级的任务将无法被执行。时间片轮转调度可以避免这个问题。
89
90- 基于时间片的轮转调度
91
92时间片轮转调度使用时间片控制每个任务的执行时间,同等优先级的任务依次获得处理器被调度执行,每个任务可以运行的时间片是固定的,当任务的时间片用完后,该任务被放在对应优先级就绪队列的队尾,然后调度就绪队列第一个位置上的任务来执行。
93### 优先级分配
94AliOS Things操作系统内核允许的任务优先级分配范围默认为0~62,值越大表示优先级越低,其中空闲任务的优先级是61,即系统最低优先级。
95### 任务栈保护
96任务栈保护采用栈统计的方法在任务切换时,对当前任务栈的使用情况进行统计,当检测到任务栈使用量超过了预先分配的空间,则抛出异常。
97### 空闲任务
98空闲任务是一个无限循环函数,在进入循环前或循环体内允许用户配置钩子函数。
99### 任务管理函数
100AliOS Things操作系统内核提供了任务创建、任务删除、任务延迟、获取任务名称等任务相关的服务函数,提供给应用程序调用,具体描述如下:
101
102| **函数名** | **描述** |
103| --- | --- |
104| aos_task_create() | 任务创建函数(推荐) |
105| aos_task_new() | 任务创建函数(兼容3.1) |
106| aos_task_new_ext() | 任务创建函数(兼容3.1) |
107| aos_task_exit() | 任务退出函数 |
108| aos_task_delete() | 任务删除函数 |
109| aos_task_resume() | 任务恢复函数 |
110| aos_task_suspend() | 任务挂起函数 |
111| aos_task_yield() | 任务让出CPU函数 |
112| aos_task_self() | 获取当前任务的具柄 |
113| aos_task_name_get() | 获取任务名称 |
114
115## 常用配置
116> 任务优先级最大值: 默认62, 最大不能超过256,如需修改,在yaml中修改RHINO_CONFIG_PRI_MAX配置
117
118```yaml
119def_config:
120  RHINO_CONFIG_PRI_MAX: 127
121```
122
123
124> 任务栈溢出检测: 默认关闭, 如需修改,在yaml中修改RHINO_CONFIG_TASK_STACK_OVF_CHECK配置
125
126```yaml
127def_config:
128  RHINO_CONFIG_TASK_STACK_OVF_CHECK: 1
129```
130
131
132> 空闲任务栈大小: 默认100Bytes, 如需修改,在yaml中修改RHINO_CONFIG_IDLE_TASK_STACK_SIZE配置
133
134```yaml
135def_config:
136  RHINO_CONFIG_IDLE_TASK_STACK_SIZE: 1024
137```
138
139
140> 时间片轮转调度策略: 默认使能, 如需修改,在yaml中修改RHINO_CONFIG_SCHED_RR配置
141
142```yaml
143def_config:
144  RHINO_CONFIG_SCHED_RR: 0
145```
146
147
148> 完全公平调度策略: 默认关闭, 如需修改,在yaml中修改RHINO_CONFIG_SCHED_CFS配置
149
150```yaml
151def_config:
152  RHINO_CONFIG_SCHED_CFS: 1
153```
154## API说明
155
156- 参考 [aos_kernel_task](https://g.alicdn.com/alios-things-3.3/doc/group__aos__kernel__task.html)
157
158## 使用示例
159
160示例代码参考[example/task_example.c](https://gitee.com/alios-things/osal_aos/blob/master/example/task_example.c),该示例使用任务管理函数来控制任务的执行状态,具体场景为任务2因等待某个信号量进入阻塞状态,而此时被任务1将其挂起,则任务2仍然是处于阻塞状态,如果在此过程中等到信号量,则任务2会解除阻塞进入挂起状态;如果未等到信号量,则任务2恢复状态后仍然处于阻塞状态。
161示例说明如下:
162
1631. 在t0时刻,任务task1、task2是通过aos_task_create()函数调用被创建,之后task1进入就绪状态,而task2处于挂起状态。
1641. Task1得到运行后,在t1时刻调用aos_task_resume()将task2恢复,task2进入就绪状态,之后task1通过调用aos_msleep()进入休眠状态,task2因为task1休眠而获得CPU执行权,task2运行后因等待信号量进入阻塞状态。
1651. Task1在t2时刻因延迟到期得到运行,并调用aos_task_suspend()将task2挂起,task2此时的状态为阻塞挂起。之后task1通过调用aos_msleep()进入休眠状态。
1661. Task2在t3时刻因延迟到期得到运行,并调用aos_task_resume()将task2恢复,此时task2的状态为阻塞状态。之后task1通过调用aos_msleep()进入休眠状态。
1671. Task1在t4时刻因延迟到期得到运行,并调用aos_sem_signal()释放信号量,这时task2因等到信号量而进入就绪状态。待到task1再次进入休眠转改后task2得到运行,进入运行状态。
168
169<div align=left display=flex>
170    <img src="https://img.alicdn.com/imgextra/i4/O1CN01WaKfUM1fXNGrOho4q_!!6000000004016-2-tps-859-291.png" style="max-width:800px;"/>
171</div>
172
173该示例可配置到helloworld_demo案例中运行,相关代码的下载、编译和固件烧录均依赖AliOS Things配套的开发工具  ,所以首先需要参考[《AliOS Things集成开发环境使用说明之搭建开发环境》](https://help.aliyun.com/document_detail/302378.html),下载安装  。
174待开发环境搭建完成后,可以按照以下步骤进行示例的测试。
175
176### 步骤1 创建或打开工程
177
178**打开已有工程**
179
180如果用于测试的案例工程已存在,可参考[《AliOS Things集成开发环境使用说明之打开工程》](https://help.aliyun.com/document_detail/302381.html)打开已有工程。
181
182**创建新的工程**
183
184组件的示例代码可以通过编译链接到AliOS Things的任意案例(solution)来运行,这里选择helloworld_demo案例。helloworld_demo案例相关的源代码下载可参考[《AliOS Things集成开发环境使用说明之创建工程》](https://help.aliyun.com/document_detail/302379.html)185
186### 步骤2 添加组件
187
188> 案例下载完成后,需要在helloworld_demo的package.yaml中添加
189
190```yaml
191depends:
192  - osal_aos: master # helloworld_demo中引入osal_aos组件
193```
194
195### 步骤3 下载组件
196
197在已安装了  的开发环境工具栏中,选择Terminal -> New Terminal启动终端,并且默认工作路径为当前工程的workspace,此时在终端命令行中输入:
198
199```shell
200
201aos install osal_aos
202
203```
204
205上述命令执行成功后,组件源码则被下载到了./components/osal_aos路径中。
206
207### 步骤4 添加示例
208
209> 在osal_aos组件的package.yaml中添加[example示例代码](https://gitee.com/alios-things/osal_aos/tree/master/example)210
211```yaml
212depends:
213  - rhino: master
214  - cli: master # 添加cli依赖
215source_file:
216  - "*.c"
217  - "example/task_example.c" # 添加 task_example.c
218```
219
220### 步骤5 编译固件
221
222在示例代码已经添加至组件的配置文件,并且helloworld_demo已添加了对该组件的依赖后,就可以编译helloworld_demo案例来生成固件了,具体编译方法可参考[《AliOS Things集成开发环境使用说明之编译固件》](https://help.aliyun.com/document_detail/302384.html)223
224### 步骤6 烧录固件
225
226helloworld_demo案例的固件生成后,可参考[《AliOS Things集成开发环境使用说明之烧录固件》](https://help.aliyun.com/document_detail/302383.html)来烧录固件。
227
228### 步骤7 打开串口
229
230固件烧录完成后,可以通过串口查看示例的运行结果,打开串口的具体方法可参考[《AliOS Things集成开发环境使用说明之查看日志》](https://help.aliyun.com/document_detail/302382.html)231
232当串口终端打开成功后,可在串口中输入help来查看已添加的测试命令。
233
234### 步骤8 测试示例
235
236> CLI命令行输入:
237
238```shell
239task_example
240```
241
242> 关键日志:
243
244```shell
245[aos_task_example]task1 is running!
246[aos_task_example]task1 resume task2!
247[aos_task_example]task1 start to sleep and release CPU!
248[aos_task_example]task2 is running!
249[aos_task_example]task1 suspend task2!
250[aos_task_example]task1 start to sleep and release CPU!
251[aos_task_example]task1 resume task2 again!
252[aos_task_example]task1 start to sleep and release CPU!
253[aos_task_example]task1 signal a semphone!
254[aos_task_example]task1 start to sleep and release CPU!
255[aos_task_example]task2 get semphone and is running!
256```
257
258
259## 注意事项
260
261- 时间片轮转调度也是基于优先级策略的,如果有一个高优先级任务就绪了,无论当前任务的时间片是否用完,处理器都会立即去执行高优先级任务,当被打断的任务恢复执行时,它将继续执行剩下的时间片。
262- tasklist查看运行任务的状态标识符为RDY,内核并没有为运行状态定义特定的标识符。
263
264## FAQ
265
266- Q1: 如何避免栈溢出?
267
268答:任务栈必须满足应用程序对函数嵌套调用所需的空间,用户可以通过tasklist查看栈的使用情况,根据需              要调整栈大小,这样可以在防止栈溢出的同时,也可避免栈空间分配过大造成浪费。
269
270- Q2: aos_task_yield()和aos_task_suspend()都能够使任务退出运行状态,使用上有什么差异吗?
271
272答:aos_task_yield()仅仅是让出CPU,它仍然处于就绪状态,因为该操作只是将任务放置到就绪队列的队              尾,若无更高优先级任务在就绪队列中,它就可以被调度执行。而aos_task_suspend()是将任务挂起停              止运行,任务会从就绪队列中去除,只有当任务被其他任务调用aos_task_resume()才能恢复。
273
274# 软件定时器
275## 概述
276AliOS Things操作系统内核使用tick作为时间片轮转调度以及延迟操作的时间度量单位,tick是实现定时触发功能的基础。tick计数发生在每次时钟中断处理的过程,时钟中断是定时产生的,系统在默认情况下为1ms触发一次,即一个tick代表1ms,用户可根据应用需要调整该时间。
277软件定时器是用来在指定时间或者触发一次或多次某个功能函数的调用。这种由定时器来执行的函数叫做定时器回调函数,定时器回调函数以及触发时间由应用程序来设定。当定时器的触发时间到来,则定时器的回调函数会被执行。定时器支持两种工作模式:单次模式和周期性模式。定时器在创建时,用户可通过option参数来制定该定时器的工作模式是单次还是周期性的,若option设置了AOS_TIMER_REPEAT则为周期性的,否则为单次。
278### 单次模式
279定时器会从应用程序设置的初始时间开始,以tick为计时单位进行倒计时,当计数值减为0时调用回调函数执行。回调函数执行完毕,则定时器停止。
280### 周期性模式
281周期定时器在时间到期执行完回调函数后,重新开始计时,直到下次时间到期,再次执行回调函数,然后一直循环下去。
282### 定时器任务
283为了在定时器创建、删除、启动、停止以及参数并更的过程中,避免锁操作,系统采用一个定时器任务来处理这些操作。当应用程序调用定时器管理接口时,将接口调用命令发送到一个消息队列,定时器任务从消息队列中读取消息并处理该命令。
284定时器任务除了处理命令外,还对当前已运行的定时器进行实时调度。所有正在运行的定时器会被挂接到g_timer_head链表,定时器任务循环的从g_timer_head链表中取出时间最近一次的定时器,通过当前tick计数和该定时器的超时tick数来判断定时时间是否到,如果该定时器触发时间已到则立即执行其处理函数,否则代表最近一次的定时器时间尚未到来,则在此时差,继续从命令buffer中收取消息,直到时间差到来后,再立即执行定时器处理函数。
285上述流程在g_timer_head中有待处理定时器时才会进入,如果没有待处理定时器,在定时器任务将只会进入定时器管理循环中。
286### 定时器管理函数
287| **函数名** | **描述** |
288| --- | --- |
289| aos_timer_create() | 定时器创建函数(推荐) |
290| aos_timer_new() | 定时器创建函数(兼容3.1) |
291| aos_timer_new_ext() | 定时器创建函数 |
292| aos_timer_free() | 定时器删除函数 |
293| aos_timer_start() | 定时器启动函数 |
294| aos_timer_stop() | 定时器停止函数 |
295| aos_timer_change() | 定时器初始时长和周期间隔参数变更函数 |
296| aos_timer_change_once() | 定时器初始时长参数变更函数 |
297| aos_timer_gettime() | 获取定时器时间参数函数 |
298
299## 常用配置
300> 软件定时器功能: 默认使能,如需修改,在yaml中修改RHINO_CONFIG_TIMER配置
301
302```yaml
303def_config:
304  RHINO_CONFIG_TIMER: 0
305```
306
307
308软件定时器任务栈: 默认200Bytes,如需修改,在yaml中修改RHINO_CONFIG_TIMER_TASK_STACK_SIZE配置
309```yaml
310def_config:
311  RHINO_CONFIG_TIMER_TASK_STACK_SIZE: 512
312```
313
314
315软件定时器任务优先级: 默认5,如需修改,在yaml中修改RHINO_CONFIG_TIMER_TASK_PRI配置
316```yaml
317def_config:
318  RHINO_CONFIG_TIMER_TASK_PRI: 5
319```
320
321
322定时器消息队列消息数: 默认20,如需修改,在yaml中修改RHINO_CONFIG_TIMER_MSG_NUM配置
323```yaml
324def_config:
325  RHINO_CONFIG_TIMER_MSG_NUM: 24
326```
327
328
329每秒tick数: 默认100,如需修改,在yaml中修改RHINO_CONFIG_TICKS_PER_SECOND配置
330```yaml
331def_config:
332  RHINO_CONFIG_TICKS_PER_SECOND: 1000
333```
334
335
336时间片: 默认50个tick,如需修改,在yaml中修改RHINO_CONFIG_TIME_SLICE_DEFAULT配置
337```yaml
338def_config:
339  RHINO_CONFIG_TIME_SLICE_DEFAULT: 100
340```
341## API说明
342- 参考 [aos_kernel_timer](https://g.alicdn.com/alios-things-3.3/doc/group__aos__kernel__timer.html)
343## 使用示例
344
345- 示例代码参考[example/timer_example.c](https://gitee.com/alios-things/osal_aos/blob/master/example/timer_example.c),该示例使用定时器管理函数来控制定时器的执行,具体场景为创建一个周期性定时器,定时调用回调函数执行,停止定时器该变定时器的时间参数,则定时器按照修改后的时间间隔定时调用回调函数执行。
346- 示例说明如下:
3471. t0时刻,测试任务调用aos_timer_create()创建一个周期性的定时器,周期间隔为1秒,回调函数为timer1_func。然后测试任务调用aos_sleep()进入休眠状态。
3481. t1时刻,相对t0过去1秒,定时器到期,回调函数timer1_func被执行。该过程重复10次。
3491. tn时刻,测试任务休眠到期,调用aos_timer_stop()停止定时器。然后调用aos_timer_change()接口修改周期间隔为2秒。
3501. tn+1时刻,相对tn过去2秒,定时器到期,回调函数timer1_func被执行。该过程重复5次。
351
352<div align=left display=flex>
353    <img src="https://img.alicdn.com/imgextra/i3/O1CN01eCw5Hz1JIKiZdxYMI_!!6000000001005-2-tps-689-192.png"  style="max-width:800px;" />
354</div>
355
356- 该示例可配置到helloworld_demo中运行,相关代码的下载、编译和固件烧录均依赖AliOS Things配套的开发工具  ,所以首先需要参考[《AliOS Things集成开发环境使用说明之搭建开发环境》](https://help.aliyun.com/document_detail/302378.html),下载安装  。
357待开发环境搭建完成后,可以按照以下步骤进行示例的测试。
358
359### 步骤1 创建或打开工程
360
361**打开已有工程**
362
363如果用于测试的案例工程已存在,可参考[《AliOS Things集成开发环境使用说明之打开工程》](https://help.aliyun.com/document_detail/302381.html)打开已有工程。
364
365**创建新的工程**
366
367组件的示例代码可以通过编译链接到AliOS Things的任意案例(solution)来运行,这里选择helloworld_demo案例。helloworld_demo案例相关的源代码下载可参考[《AliOS Things集成开发环境使用说明之创建工程》](https://help.aliyun.com/document_detail/302379.html)368
369### 步骤2 添加组件
370> helloworld_demo组件的package.yaml中添加
371
372```yaml
373depends:
374  - osal_aos: master # helloworld_demo中引入osal_aos组件
375```
376
377### 步骤3 下载组件
378
379在已安装了  的开发环境工具栏中,选择Terminal -> New Terminal启动终端,并且默认工作路径为当前工程的workspace,此时在终端命令行中输入:
380
381```shell
382
383aos install osal_aos
384
385```
386
387上述命令执行成功后,组件源码则被下载到了./components/osal_aos路径中。
388
389### 步骤4 添加示例
390> osal_aos组件的package.yaml中添加[example示例代码](https://gitee.com/alios-things/osal_aos/tree/master/example)391
392```yaml
393depends:
394  - rhino: master
395  - cli: master # 添加cli依赖
396source_file:
397  - "*.c"
398  - "example/timer_example.c" # 添加 timer_example.c
399```
400
401### 步骤5 编译固件
402
403在示例代码已经添加至组件的配置文件,并且helloworld_demo已添加了对该组件的依赖后,就可以编译helloworld_demo案例来生成固件了,具体编译方法可参考[《AliOS Things集成开发环境使用说明之编译固件》](https://help.aliyun.com/document_detail/302384.html)404
405### 步骤6 烧录固件
406
407helloworld_demo案例的固件生成后,可参考[《AliOS Things集成开发环境使用说明之烧录固件》](https://help.aliyun.com/document_detail/302383.html)来烧录固件。
408
409### 步骤7 打开串口
410
411固件烧录完成后,可以通过串口查看示例的运行结果,打开串口的具体方法可参考[《AliOS Things集成开发环境使用说明之查看日志》](https://help.aliyun.com/document_detail/302382.html)412
413当串口终端打开成功后,可在串口中输入help来查看已添加的测试命令。
414
415### 步骤8 测试示例
416> CLI命令行输入:
417
418```shell
419timer_example
420```
421
422> 关键日志:
423
424```shell
425[aos_timer_example]timer expires 10 times
426[aos_timer_example]timer expires 5 times
427```
428## 注意事项
429
430- tick是内核调度和延迟操作的基础,所以RHINO_CONFIG_TICKS_PER_SECOND参数必须配置,而定时器是基于tick实现的软件定时器功能,可以通过配置RHINO_CONFIG_TIMER参数来使能或关闭该功能。
431## FAQ
432
433- Q1: 定时器回调函数的执行上下文是在哪里?
434
435答:定时器回调函数是在定时器任务中执行的,所以定时器回调函数的执行上下文为定时器任务,当回调函              数所需栈空间超过定时器任务栈大小时,用户需要通过配置                                                                       RHINO_CONFIG_TIMER_TASK_STACK_SIZE参数来增加栈大小。
436# 内存管理
437## 概述
438AliOS Things操作系统内核通过内存管理实现应用程序可以调用标准C库的malloc和free接口来动态的分配和释放内存。
439### 内存管理函数
440| **函数名** | **描述** |
441| --- | --- |
442| aos_malloc() | 从系统heap分配内存给用户 |
443| aos_zalloc() | 从系统heap分配内存给用户,并且将分配的内存初始化为0 |
444| aos_calloc() | 从系统heap分配内存给用户,并且将分配的内存初始化为0 |
445| aos_realloc() | 重新调整之前调用 `aos_malloc(aos_calloc、aos_zalloc)`所分配的内存块的大小 |
446| aos_free() | 内存释放函数 |
447
448### 内存分配算法
449AliOS Things内存管理采用类buddy伙伴算法,以及blk快速小内存申请算法相结合的策略。
450
451- buddy算法:
452
453<div align=left display=flex>
454    <img src="https://img.alicdn.com/imgextra/i1/O1CN01N1qqmG1md3UkY5gRB_!!6000000004976-2-tps-841-393.png"  style="max-width:800px;" />
455</div>
456
457Buddy算法申请的内存最小为8字节对齐
458可申请的最大长度除了受堆空间总大小限制外还可通过编译宏配置
459通过32 bit的位图结构来挂载对应的空闲链表bit n代表内存块对应的type,其下面挂载了长度范围 (2^(n- 1 + off) ~ 2^(n + off) - 1)的空闲块,其中off值主要是考虑利用了原本较小长度块的低bit。
460内存申请时,优先从对应type的空闲链表中申请;如果没有空闲,则从 type+1 的位置申请内存,并将多余的空闲内存块做拆分并重新放入对应的空闲链表位置;每个内存块维测信息,包括如魔术字、多级申请调用栈、是否空闲等信息,便于内存踩踏、泄漏、重复释放等问题的定位。
461内存释放时,考虑前后的内存块是否也空闲,如果是,则进行内存块合并,并插入相应type的空闲链表位置。
462**优点:**
463能够满足绝大多数场景的内存申请需求;
464可以申请最大范围内任意长度的内存;
465内存碎片整理已经相当出色;
466有充分的维测信息,便于问题定位。
467**缺点:**
468内存维测信息较多,有一定的空间浪费;
469内存不停地拆分组合,一定程度上影响效率;
470长期运行过多的小内存会出现碎片,会隔断大内存空间。
471
472
473- blk小内存快速申请:
474
475<div align=left display=flex>
476    <img src="https://img.alicdn.com/imgextra/i4/O1CN01mHjJZ11viOeeBd76S_!!6000000006206-2-tps-835-477.png"  style="max-width:800px;" />
477</div>
478
479blk小内存算法申请的内存最小为4字节对齐
480小于RHINO_CONFIG_MM_BLK_SIZE大小的内存块会优先从blk算法中申请不够的再从通过buddy申请
481内存申请时,优先从对应type的空闲链表中申请;如果没有空闲,则先考虑从空闲资源池拉取一段空闲资源补充;如果空闲资源不足,则从 type+1 的位置申请内存(以此类推)申请过程不涉及拆分;
482内存释放时,直接挂在到对应type的空闲链表内,释放不涉及合并。
483**优点:**
484申请效率高;
485避免过多小内存碎片。
486**缺点:**
487内存维测信息少,出现问题较难定位;
488申请的内存大小有限制,一般为1K以下。
489## 常用配置
490内存的常用配置都在各个单板的k_config.h中,如果不需修改,则会使用k_default_config.h里面的默认配置。当然由于其本身是宏定义,也可在board对应的yaml中定义。
491> 打开关闭内存模块功能
492
493```yaml
494def_config:
495  RHINO_CONFIG_MM_TLF: 1
496```
497
498
499打开关闭buddy内存模块的维测功能
500```yaml
501def_config:
502  RHINO_CONFIG_MM_DEBUG: 1
503```
504
505
506内存维测打开时,记录内存申请时的多少级调用栈
507```yaml
508def_config:
509  RHINO_CONFIG_MM_TRACE_LVL: 8
510```
511
512
513Buddy算法最大支持申请的内存大小bit位
514实际大小换算为 1ul << RHINO_CONFIG_MM_MAXMSIZEBIT
515```yaml
516def_config:
517  RHINO_CONFIG_MM_MAXMSIZEBIT: 28
518```
519
520
521打开关闭固定长度小内存块快速申请
522```yaml
523def_config:
524  RHINO_CONFIG_MM_BLK: 1
525```
526
527
528固定长度小内存块总空间大小(byte)
529```yaml
530def_config:
531  RHINO_CONFIG_MM_TLF_BLK_SIZE: 8192
532```
533
534
535设定从blk小内存申请的阈值(byte)即
536```yaml
537def_config:
538  RHINO_CONFIG_MM_BLK_SIZE: 256
539```
540
541
542注: blk目前最大支持1K以下的小内存块如果需要调整可修改内部配置 MM_BLK_SLICE_BIT一般无需修改
543## API说明
544
545- 参考 [aos_kernel_memory](https://g.alicdn.com/alios-things-3.3/doc/group__aos__kernel__memory.html)
546
547## 使用示例
548该示例可配置到helloworld_demo中运行,相关代码的下载、编译和固件烧录均依赖AliOS Things配套的开发工具  ,所以首先需要参考[《AliOS Things集成开发环境使用说明之搭建开发环境》](https://help.aliyun.com/document_detail/302378.html),下载安装  。
549待开发环境搭建完成后,可以按照以下步骤进行示例的测试。
550
551### 步骤1 创建或打开工程
552
553**打开已有工程**
554
555如果用于测试的案例工程已存在,可参考[《AliOS Things集成开发环境使用说明之打开工程》](https://help.aliyun.com/document_detail/302381.html)打开已有工程。
556
557**创建新的工程**
558
559组件的示例代码可以通过编译链接到AliOS Things的任意案例(solution)来运行,这里选择helloworld_demo案例。helloworld_demo案例相关的源代码下载可参考[《AliOS Things集成开发环境使用说明之创建工程》](https://help.aliyun.com/document_detail/302379.html)560
561### 步骤2 添加组件
562
563> helloworld_demo组件的package.yaml中添加
564
565```c
566depends:
567  - osal_aos: master # helloworld_demo中引入osal_aos组件
568```
569
570### 步骤3 下载组件
571
572在已安装了  的开发环境工具栏中,选择Terminal -> New Terminal启动终端,并且默认工作路径为当前工程的workspace,此时在终端命令行中输入:
573
574```shell
575
576aos install osal_aos
577
578```
579
580上述命令执行成功后,组件源码则被下载到了./components/osal_aos路径中。
581
582### 步骤4 添加示例
583
584> osal_aos组件的package.yaml中添加[example示例代码](https://gitee.com/alios-things/osal_aos/tree/master/example)585
586```yaml
587depends:
588  - rhino: master
589  - cli: master # 添加cli依赖
590source_file:
591  - "*.c"
592  - "example/mem_example.c" # 添加 mem_example.c
593```
594
595### 步骤5 编译固件
596
597在示例代码已经添加至组件的配置文件,并且helloworld_demo已添加了对该组件的依赖后,就可以编译helloworld_demo案例来生成固件了,具体编译方法可参考[《AliOS Things集成开发环境使用说明之编译固件》](https://help.aliyun.com/document_detail/302384.html)598
599### 步骤6 烧录固件
600
601helloworld_demo案例的固件生成后,可参考[《AliOS Things集成开发环境使用说明之烧录固件》](https://help.aliyun.com/document_detail/302383.html)来烧录固件。
602
603### 步骤7 打开串口
604
605固件烧录完成后,可以通过串口查看示例的运行结果,打开串口的具体方法可参考[《AliOS Things集成开发环境使用说明之查看日志》](https://help.aliyun.com/document_detail/302382.html)606
607当串口终端打开成功后,可在串口中输入help来查看已添加的测试命令。
608
609### 步骤8 测试示例
610> CLI命令行输入:
611
612```shell
613mem_example
614```
615
616> 关键日志:
617
618```shell
619[aos_mem_example]aos_malloc success!
620[aos_mem_example]aos_zalloc success!
621[aos_mem_example]aos_calloc success!
622[aos_mem_example]aos_realloc success!
623```
624## 注意事项
625
626- 实际内存申请释放应统一通过malloc/free接口,其内部已经对接了aos_malloc/aos_free接口
627- 内存申请使用的堆统一由g_mm_region数据结构初始化(传入堆起点和长度),并在aos_init内核初始化接口中默认完成了内存模块的初始化
628- AliOS Things针对内存模块提供了完善的内存解析工具,用来分析内存踩踏、内存泄漏、重复释放等问题。具体可参考:[https://blog.csdn.net/HaaSTech/article/details/112246611](https://blog.csdn.net/HaaSTech/article/details/112246611)
629## FAQ
630
631- Q1: 内存模块的维测功能如何打开?
632
633答:通过打开宏RHINO_CONFIG_MM_DEBUG即可打开内存管理模块的维测信息;其包含内存头魔术字、内存申请的调用栈以及当前内存空闲情况等等。
634其中blk算法不包含维测信息。
635
636- Q2:如何查看当前内存的使用情况?
637
638答:通过在cli中输入 dumpsys_mm_info ,将所有内存块的使用情况,以及整体的占用空闲情况输出。
639# 信号量
640## 概述
641信号量是多任务系统中实现任务间同步,并且协调多任务对共享资源访问的一种互斥机制。信号量允许有多个使用者,所以采用计数值来表示可用的资源数,当请求一个信号量时,该计数值减1,若此时计数值大于等于0,表示当前有可用的信号量,则任务获得信号量,可以访问资源,若此时计数值为负数,则任务进入阻塞状态,释放处理器资源。当获取信号量的任务执行完操作,释放信号量时,则将当前计数值加1,如果当前存在等待该资源的任务,则任务被唤醒而获得该信号量。
642### 超时时间
643信号量获取可设置超时时间,如果任务在超时时间到期后仍未等待信号量,则任务解除阻塞进入就绪状态。
644### 多任务同步
645多个任务可以等待同一个信号量,若信号量可用或信号量被释放,通常情况下,系统会将阻塞在该信号量上优先级最高的任务置于就绪状态,提供了面向所有阻塞任务的信号量释放机制,可以将阻塞在该信号量上的所有任务都置为就绪状态。
646### 信号量功能函数
647| **函数名** | **描述** |
648| --- | --- |
649| aos_sem_create() | 信号量创建函数(推荐),需指定计数值 |
650| aos_sem_new() | 信号量创建函数(兼容3.1),需指定计数值 |
651| aos_sem_free() | 信号量删除函数 |
652| aos_sem_wait() | 信号量获取函数,可以指定超时时间 |
653| aos_sem_signal() | 信号量释放函数,只唤醒阻塞在此信号量上的最高优先级任务 |
654| aos_sem_signal_all() | 信号量释放函数,唤醒阻塞在此信号量上的所有任务 |
655| aos_sem_is_valid() | 判断信号量具柄是否合法函数 |
656
657## 常用配置
658> 软件定时器功能: 默认使能,如需修改,在yaml中修改RHINO_CONFIG_SEM配置
659
660```yaml
661def_config:
662  RHINO_CONFIG_SEM: 0
663```
664
665
666## API说明
667- 参考 [aos_kernel_sem](https://g.alicdn.com/alios-things-3.3/doc/group__aos__kernel__sem.html)
668## 使用示例
669
670- 示例代码参考[example/sem_example.c](https://gitee.com/alios-things/osal_aos/blob/master/example/sem_example.c),该示例使用信号量实现多任务同步,具体场景为创建一个高优先级任务A,一个低优先级任务B,任务A和任务B同时等待同一信号量,此时测试任务T调用aos_sem_signal()释放信号量,任务A首先获得信号量,任务A操作完成后释放一次信号量,此时任务B获取信号量得到运行。
671- 示例说明如下:
6721. t0时刻,任务T调用aos_sem_create()创建一信号量,初始计数值为0。任务T然后调用aos_task_create()创建任务A和任务B,任务A优先级为30,任务B优先级为31。任务A和任务B运行后因等待信号量而阻塞。
6731. t1时刻,任务T调用aos_sem_signal()释放信号量,任务A获得信号量。
6741. t2时刻,任务A调用aos_sem_signal()释放信号量,任务B获得信号量。
675
676<div align=left display=flex>
677    <img src="hhttps://img.alicdn.com/imgextra/i3/O1CN01NW351a1hXUbVEKizH_!!6000000004287-2-tps-516-261.png"  style="max-width:800px;" />
678</div>
679
680- 该示例可配置到helloworld_demo中运行,相关代码的下载、编译和固件烧录均依赖AliOS Things配套的开发工具  ,所以首先需要参考[《AliOS Things集成开发环境使用说明之搭建开发环境》](https://help.aliyun.com/document_detail/302378.html),下载安装  。
681待开发环境搭建完成后,可以按照以下步骤进行示例的测试。
682
683### 步骤1 创建或打开工程
684
685**打开已有工程**
686
687如果用于测试的案例工程已存在,可参考[《AliOS Things集成开发环境使用说明之打开工程》](https://help.aliyun.com/document_detail/302381.html)打开已有工程。
688
689**创建新的工程**
690
691组件的示例代码可以通过编译链接到AliOS Things的任意案例(solution)来运行,这里选择helloworld_demo案例。helloworld_demo案例相关的源代码下载可参考[《AliOS Things集成开发环境使用说明之创建工程》](https://help.aliyun.com/document_detail/302379.html)692
693### 步骤2 添加组件
694
695案例下载完成后,需要在helloworld_demo组件的package.yaml中添加对组件的依赖:
696
697```yaml
698depends:
699  - osal_aos: master # helloworld_demo中引入osal_aos组件
700```
701### 步骤3 下载组件
702
703在已安装了  的开发环境工具栏中,选择Terminal -> New Terminal启动终端,并且默认工作路径为当前工程的workspace,此时在终端命令行中输入:
704
705```shell
706
707aos install osal_aos
708
709```
710
711上述命令执行成功后,组件源码则被下载到了./components/osal_aos路径中。
712
713### 步骤4 添加示例
714
715> 在osal_aos组件的package.yaml中添加[example示例代码](https://gitee.com/alios-things/osal_aos/tree/master/example)716
717```yaml
718depends:
719  - rhino: master
720  - cli: master # 添加cli依赖
721source_file:
722  - "*.c"
723  - "example/sem_example.c" # 添加 sem_example.c
724```
725### 步骤5 编译固件
726
727在示例代码已经添加至组件的配置文件,并且helloworld_demo已添加了对该组件的依赖后,就可以编译helloworld_demo案例来生成固件了,具体编译方法可参考[《AliOS Things集成开发环境使用说明之编译固件》](https://help.aliyun.com/document_detail/302384.html)728
729### 步骤6 烧录固件
730
731helloworld_demo案例的固件生成后,可参考[《AliOS Things集成开发环境使用说明之烧录固件》](https://help.aliyun.com/document_detail/302383.html)来烧录固件。
732
733### 步骤7 打开串口
734
735固件烧录完成后,可以通过串口查看示例的运行结果,打开串口的具体方法可参考[《AliOS Things集成开发环境使用说明之查看日志》](https://help.aliyun.com/document_detail/302382.html)736
737当串口终端打开成功后,可在串口中输入help来查看已添加的测试命令。
738
739### 步骤8 测试示例
740> CLI命令行输入:
741
742```shell
743sem_example
744```
745> 关键日志:
746
747```sh
748[aos_sem_example][0x34035840]field1=1449208480, field2=1449208480, field3=1449208480, field4=1449208480
749[aos_sem_example][0x34035d10]field1=670588782, field2=670588782, field3=670588782, field4=670588782
750[aos_sem_example][0x34035840]field1=1648917010, field2=1648917010, field3=1648917010, field4=1648917010
751[aos_sem_example][0x34035d10]field1=1949034929, field2=1949034929, field3=1949034929, field4=1949034929
752[aos_sem_example][0x34035840]field1=672861528, field2=672861528, field3=672861528, field4=672861528
753[aos_sem_example][0x34035d10]field1=820049120, field2=820049120, field3=820049120, field4=820049120
754```
755## 注意事项
756
757- 中断禁止信号量获取检测
758
759信号量的获取接口在中断上下文调用很容易发生死锁问题。当被打断的上下文和打断的中断上下文要获取同一个信号量时,会发生互相等待的情况。有些内核将这种判断处理交由上层软件进行判断和使用,本内核会在take信号量检测,如果是中断上下文,则直接返回失败。
760
761- 占用信号量非等待、永远等待、延时使用区别
762
763应用程序在获取信号量时,需要按照实际的需求,来安排信号量获取策略。aos_sem_take传入延时timeout为0,获取不到信号量会立即报失败;timeout为AOS_WAIT_FOREVER时,会永远在此等待,直到获取到信号量,可能会造成该任务无法继续运行;其他值标识最大延迟的时间上限,达到上限时,即使未获取到信号量,任务也会被唤醒,并返回错误。
764## FAQ
765
766- Q1: 调用aos_sem_task()接口无限期的等待信号量,timeout参数怎么设置?
767
768     答:将timeout赋值为AOS_WAIT_FOREVER。
769# 互斥量
770## 概述
771互斥量的获取是完全互斥的,即同一时刻,互斥量只能被一个任务获取。而信号量按照起始的计数值的配置,可以存在多个任务获取同一信号量的情况,直到计数值减为0,则后续任务无法再获取信号量,当信号量的计数初值设置为1,同样有互斥的效果。但信号量无法避免优先级反转问题。
772### 优先级反转
773优先级反转是一种不希望发生的任务调度状态,该状态下,一个高优先级任务间接的被一个低优先级任务所抢占,使得两个任务的相对优先级反转了。当高、中、低三个优先级任务同时访问使用信号量互斥资源时,可能会引起优先级反转。当高优先级的任务需要的信号量被低优先级任务占用时,处理器资源会调度给低优先级任务。此时如果低优先级需要获取的另一个信号量被中优先级的pend任务所占用,那么低优先级的任务则需要等待中优先级的任务事件到来,并释放信号量,则就出现了 高、中优先级的任务并不是等待一个信号量,但是中优先级任务先运行的现象。如下图:
774
775<div align=left display=flex>
776    <img src="https://img.alicdn.com/imgextra/i3/O1CN01zD7nbO1kzNZGRDjRR_!!6000000004754-2-tps-410-240.png"  style="max-width:800px;" />
777</div>
778
779任务H具有高优先级,任务M具有中等优先级,任务L具有低优先级,在t0时刻任务H还未运行,任务M因等待信号量2而阻塞,低优先级任务L得到调度占用信号量1;t1时刻,任务H运行并抢占任务L,任务L占用信号量还未释放;t2时刻,任务H阻塞于信号量1,任务L得以继续运行;t3时刻,任务L等待信号量2而阻塞,t4时刻,信号量2被释放,任务M得到运行,此时任务H虽然具有高优先级,但需先等待任务M释放信号量2让任务L运行,并且任务L释放信号量1后才能运行,这种情况即出现了优先级反转。
780### 优先级继承
781互斥量可以解决优先级反转问题,高优先级的任务获取互斥量时,如果该互斥量被某低优先级的任务占用, 会动态提升该低优先级任务的优先级等于高优先级,并且将该优先级值依次传递给该低优先级任务依赖的互斥量关联的任务,以此递归下去。当某任务释放互斥量时,会查找该任务的基础优先级,以及获取到的互斥量所阻塞的最高优先级的任务的优先级,取两者中高的优先级来重新设定此任务的优先级。总的原则就是,高优先级任务被互斥量阻塞时,会将占用该互斥量的低优先级任务临时提高;互斥量释放时,相应任务的优先级需要恢复。如下图:
782
783<div align=left display=flex>
784    <img src="https://img.alicdn.com/imgextra/i1/O1CN0131uJpc1veH5gWvL3g_!!6000000006197-2-tps-481-250.png"  style="max-width:800px;" />
785</div>
786
787任务H具有高优先级,任务M具有中等优先级,任务L具有低优先级,在t0时刻任务H还未运行,任务M获取互斥量2而阻塞,低优先级任务L得到调度获得互斥量1;t1时刻taskH抢占taskL,但因无法获得互斥量1而阻塞,此时taskL的优先级被提升至与taskH一样高,并继续运行;t2时刻taskL因无法获得互斥量2而阻塞,t3时刻互斥量2被释放,taskL因比taskM优先级高获得互斥量2得到运行;在t4时刻,taskL释放互斥量1,并将优先级恢复到之前状态,taskH因获得互斥量1得到运行,该机制消除了优先级反转的发生。
788### 超时时间
789互斥量获取可设置超时时间,如果任务在超时时间到期后仍未获得互斥量,则任务解除阻塞进入就绪状态。
790### 互斥量功能函数
791| **函数名** | **描述** |
792| --- | --- |
793| aos_mutex_create() | 互斥量创建函数(推荐) |
794| aos_mutex_new() | 互斥量创建函数(兼容3.1) |
795| aos_mutex_free() | 互斥量删除函数 |
796| aos_mutex_lock() | 互斥量获取函数 |
797| aos_mutex_unlock() | 互斥量释放函数 |
798| aos_mutex_is_valid() | 判断互斥量具柄是否合法函数 |
799
800## 常用配置
801> 互斥量优先级继承: 默认关闭,如需修改,在yaml中修改RHINO_CONFIG_MUTEX_INHERIT配置
802
803```yaml
804def_config:
805  RHINO_CONFIG_MUTEX_INHERIT: 1
806```
807
808
809## API说明
810- 参考 [aos_kernel_mutex](https://g.alicdn.com/alios-things-3.3/doc/group__aos__kernel__mutex.html)
811## 使用示例
812
813- 示例代码参考[example/mutex_example.c](https://gitee.com/alios-things/osal_aos/blob/master/example/mutex_example.c),该示例使用互斥量实现共享资源的互斥访问,具体场景为创建任务A和认为B,以及一互斥量。任务A和任务B使用互斥量同时访问共享数据区,访问共享数据区时使用互斥量做保护。
814- 示例说明如下:
8151. t0时刻,任务T调用aos_mutex_create()创建一互斥量。任务T然后调用aos_task_create()创建任务A和任务B。任务A得到运行,并获取互斥量对数据区record_status进行读写操作。
8161. t1时刻,任务A因时间片耗尽,让出CPU,任务B得到运行。
8171. t2时刻,任务B因无法获得互斥量,进入阻塞状态,任务A得到运行。
8181. t3时刻,任务A对数据区record_status的操作完成,释放互斥量,任务B获得互斥量开始对数据区record_status进行读写操作。
819
820<div align=left display=flex>
821    <img src="https://img.alicdn.com/imgextra/i2/O1CN01UKTCDa1kGx9OVO9Yi_!!6000000004657-2-tps-409-251.png"  style="max-width:800px;"/>
822</div>
823
824- 该示例可配置到helloworld_demo中运行,相关代码的下载、编译和固件烧录均依赖AliOS Things配套的开发工具  ,所以首先需要参考[《AliOS Things集成开发环境使用说明之搭建开发环境》](https://help.aliyun.com/document_detail/302378.html),下载安装  。
825待开发环境搭建完成后,可以按照以下步骤进行示例的测试。
826
827### 步骤1 创建或打开工程
828
829**打开已有工程**
830
831如果用于测试的案例工程已存在,可参考[《AliOS Things集成开发环境使用说明之打开工程》](https://help.aliyun.com/document_detail/302381.html)打开已有工程。
832
833**创建新的工程**
834
835组件的示例代码可以通过编译链接到AliOS Things的任意案例(solution)来运行,这里选择helloworld_demo案例。helloworld_demo案例相关的源代码下载可参考[《AliOS Things集成开发环境使用说明之创建工程》](https://help.aliyun.com/document_detail/302379.html)836
837### 步骤2 添加组件
838
839案例下载完成后,需要在helloworld_demo组件的package.yaml中添加对组件的依赖:
840
841```yaml
842depends:
843  - osal_aos: master # helloworld_demo中引入osal_aos组件
844```
845
846### 步骤3 下载组件
847
848在已安装了  的开发环境工具栏中,选择Terminal -> New Terminal启动终端,并且默认工作路径为当前工程的workspace,此时在终端命令行中输入:
849
850```shell
851
852aos install osal_aos
853
854```
855
856上述命令执行成功后,组件源码则被下载到了./components/osal_aos路径中。
857
858### 步骤4 添加示例
859
860> osal_aos组件的package.yaml中添加[example示例代码](https://gitee.com/alios-things/osal_aos/tree/master/example)861
862```yaml
863depends:
864  - rhino: master
865  - cli: master # 添加cli依赖
866source_file:
867  - "*.c"
868  - "example/mutex_example.c" # 添加 mutex_example.c
869```
870
871### 步骤5 编译固件
872
873在示例代码已经添加至组件的配置文件,并且helloworld_demo已添加了对该组件的依赖后,就可以编译helloworld_demo案例来生成固件了,具体编译方法可参考[《AliOS Things集成开发环境使用说明之编译固件》](https://help.aliyun.com/document_detail/302384.html)874
875### 步骤6 烧录固件
876
877helloworld_demo案例的固件生成后,可参考[《AliOS Things集成开发环境使用说明之烧录固件》](https://help.aliyun.com/document_detail/302383.html)来烧录固件。
878
879### 步骤7 打开串口
880
881固件烧录完成后,可以通过串口查看示例的运行结果,打开串口的具体方法可参考[《AliOS Things集成开发环境使用说明之查看日志》](https://help.aliyun.com/document_detail/302382.html)882
883当串口终端打开成功后,可在串口中输入help来查看已添加的测试命令。
884
885### 步骤8 测试示例
886> CLI命令行输入:
887
888```c
889mutex_example
890```
891
892> 关键日志:
893
894```sh
895[aos_mutex_example][0x34035d80]field1=1709388289, field2=1709388289, field3=1709388289, field4=1709388289
896[aos_mutex_example][0x340358b0]field1=1386345753, field2=1386345753, field3=1386345753, field4=1386345753
897[aos_mutex_example][0x34035d80]field1=2042805823, field2=2042805823, field3=2042805823, field4=2042805823
898[aos_mutex_example][0x340358b0]field1=1026149465, field2=1026149465, field3=1026149465, field4=1026149465
899[aos_mutex_example][0x34035d80]field1=839374236, field2=839374236, field3=839374236, field4=839374236
900[aos_mutex_example][0x340358b0]field1=532559370, field2=532559370, field3=532559370, field4=532559370
901[aos_mutex_example][0x34035d80]field1=1406670318, field2=1406670318, field3=1406670318, field4=1406670318
902[aos_mutex_example][0x340358b0]field1=24443955, field2=24443955, field3=24443955, field4=24443955
903[aos_mutex_example][0x34035d80]field1=693331083, field2=693331083, field3=693331083, field4=693331083
904```
905## 注意事项
906
907- 互斥量只能由获取该互斥量的任务的释放,不能由其他任务释放。
908- 互斥量已被当前任务获取,若当前任务再次获取互斥量则返回错误。
909## FAQ
910Q1: 调用aos_mutex_lock()接口无限期的获取互斥量,timeout参数怎么设置?
911答:将timeout赋值为AOS_WAIT_FOREVER。
912# 事件
913## 概述
914事件是AliOS Things内核提供的一种任务间通信方式,它不同于信号量和互斥量,可以使用事件组实现一个任务同时等待多个事件的发生,或者等待同一个事件的任务在事件发生时解除阻塞状态。事件组是一个32位的数,每一位都对应一个事件标志,事件标志只有两种状态:
915
916   - 1 代表被设置,即当有事件发生时,该事件对应的事件标志位将被设置为1
917   - 0 代表被清除,即任务获得事件后,将事件对应的事件标志位被清除为0。
918
919应用程序可以使用事件的如下特性来实现任务间同步:
920
921   - 任务可以等待事件组中设置的所有事件都发生后,即“与”的方式,任务才解除阻塞进入就绪状态;
922   - 任务可以等待事件组中设置的任意一个事件发生,即“或”的方式,任务就解除阻塞进入就绪状态;
923   - 如果有多个任务等待同一事件,当事件发生时,若任务等待条件满足均会解除阻塞。
924
925### 超时时间
926事件等待可设置超时时间,如果任务在超时时间到期后仍未等到事件发生,则任务解除阻塞进入就绪状态。
927### 事件功能函数
928| **函数名** | **描述** |
929| --- | --- |
930| aos_event_create() | 事件创建函数(推荐) |
931| aos_event_new() | 事件创建函数(兼容3.1) |
932| aos_event_free() | 事件删除函数 |
933| aos_event_get() | 获取事件函数 |
934| aos_event_set() | 设置事件函数 |
935| aos_event_is_valid() | 判断事件具柄是否合法函数 |
936
937## 常用配置
938> 事件功能: 默认使能,如需修改,在yaml中修改RHINO_CONFIG_EVENT_FLAG配置
939
940```yaml
941def_config:
942  RHINO_CONFIG_EVENT_FLAG: 0
943```
944
945
946## API说明
947
948- 参考 [aos_kernel_event](https://g.alicdn.com/alios-things-3.3/doc/group__aos__kernel__event.html)
949## 使用示例
950
951- 示例代码参考[example/event_example.c](https://gitee.com/alios-things/osal_aos/blob/master/example/event_example.c),该示例使用事件机制实现任务间同步,具体场景为创建任务A和认为B,以及一事件。任务A以“与”的方式等待事件1和事件2;任务B以“或”的方式等待事件1和事件2。测试任务T设置事件1,则任务B因获取事件得到运行,之后测试任务T设置事件2,则任务A因等到全部事件而得到运行。
952- 示例说明如下:
9531. t0时刻,任务T调用aos_event_create()创建一事件。任务T然后调用aos_task_create()创建任务A和任务B。任务A调用aos_event_get()以RHINO_AND为选项参数等待事件1和事件2的发生;任务B调用aos_event_get()以RHINO_OR为选项参数等待事件1或事件2的发生。
9541. t1时刻,任务T调用aos_event_get()设置事件1,任务B因等到事件1得到运行。
9551. t2时刻,任务T调用aos_event_get()设置事件2,任务A因等到了所有事件1和2而得到运行。
956
957<div align=left display=flex>
958    <img src="https://img.alicdn.com/imgextra/i2/O1CN01F9Ij8h1vWwkllyh25_!!6000000006181-2-tps-430-250.png"  style="max-width:800px;" />
959</div>
960
961- 该示例可配置到helloworld_demo中运行,相关代码的下载、编译和固件烧录均依赖AliOS Things配套的开发工具  ,所以首先需要参考[《AliOS Things集成开发环境使用说明之搭建开发环境》](https://help.aliyun.com/document_detail/302378.html),下载安装  。
962待开发环境搭建完成后,可以按照以下步骤进行示例的测试。
963
964### 步骤1 创建或打开工程
965
966**打开已有工程**
967
968如果用于测试的案例工程已存在,可参考[《AliOS Things集成开发环境使用说明之打开工程》](https://help.aliyun.com/document_detail/302381.html)打开已有工程。
969
970**创建新的工程**
971
972组件的示例代码可以通过编译链接到AliOS Things的任意案例(solution)来运行,这里选择helloworld_demo案例。helloworld_demo案例相关的源代码下载可参考[《AliOS Things集成开发环境使用说明之创建工程》](https://help.aliyun.com/document_detail/302379.html)973
974### 步骤2 添加组件
975
976案例下载完成后,需要在helloworld_demo组件的package.yaml中添加对组件的依赖:
977
978```c
979depends:
980  - osal_aos: master # helloworld_demo中引入osal_aos组件
981```
982
983### 步骤3 下载组件
984
985在已安装了  的开发环境工具栏中,选择Terminal -> New Terminal启动终端,并且默认工作路径为当前工程的workspace,此时在终端命令行中输入:
986
987```shell
988
989aos install osal_aos
990
991```
992
993上述命令执行成功后,组件源码则被下载到了./components/osal_aos路径中。
994
995### 步骤4 添加示例
996> 在osal_aos组件的package.yaml中添加[example示例代码](https://gitee.com/alios-things/osal_aos/tree/master/example)997
998```yaml
999depends:
1000  - rhino: master
1001  - cli: master # 添加cli依赖
1002source_file:
1003  - "*.c"
1004  - "example/evnet_example.c" # 添加 evnet_example.c
1005```
1006
1007### 步骤5 编译固件
1008
1009在示例代码已经添加至组件的配置文件,并且helloworld_demo已添加了对该组件的依赖后,就可以编译helloworld_demo案例来生成固件了,具体编译方法可参考[《AliOS Things集成开发环境使用说明之编译固件》](https://help.aliyun.com/document_detail/302384.html)1010
1011### 步骤6 烧录固件
1012
1013helloworld_demo案例的固件生成后,可参考[《AliOS Things集成开发环境使用说明之烧录固件》](https://help.aliyun.com/document_detail/302383.html)来烧录固件。
1014
1015### 步骤7 打开串口
1016
1017固件烧录完成后,可以通过串口查看示例的运行结果,打开串口的具体方法可参考[《AliOS Things集成开发环境使用说明之查看日志》](https://help.aliyun.com/document_detail/302382.html)1018
1019当串口终端打开成功后,可在串口中输入help来查看已添加的测试命令。
1020
1021### 步骤8 测试示例
1022> CLI命令行输入:
1023
1024```sh
1025event_example
1026```
1027> 关键日志:
1028
1029```sh
1030[aos_event_example]set event 1!
1031[aos_event_example][taskA]field1=0, field2=0, field3=0, field4=0
1032[aos_event_example]set event 2!
1033[aos_event_example][taskB]field1=1123961442, field2=1123961442, field3=1123961442, field4=1123961442
1034```
1035## 注意事项
1036
1037- 在删除事件时,如果有任务阻塞在该事件上,则将这些任务从此事件上脱离,这种任务行为像是事件发生导致的,但实际上并非如此。所以删除事件时需要谨慎。
1038## FAQ
1039Q1: 调用aos_event_get()接口无限期的等待事件,timeout参数怎么设置?
1040答:将timeout赋值为AOS_WAIT_FOREVER。
1041# 消息队列
1042## 概述
1043消息队列是一种任务间传递数据的有效方式。消息队列使用环形缓冲池(ring buffer)来管理消息的队列缓冲区,并使用类似信号量的机制进行任务间的同步。任务通过消息队列可以发送消息,也可以通过它接收消息,从而实现数据的同步及通信。任务发送的消息会暂存在消息队列中,当接收任务来读时,将暂存的数据传递给接收任务;若接收任务在接收数据时,消息队列中无可读数据,则任务会阻塞,直到有消息到来解除阻塞而进入就绪状态。
1044### 超时时间
1045消息接收可设置超时时间,如果任务在超时时间到期后仍未收到消息,则任务解除阻塞进入就绪状态。
1046### 消息队列功能函数
1047| **函数名** | **描述** |
1048| --- | --- |
1049| aos_queue_create() | 消息队列创建函数(推荐) |
1050| aos_queue_new() | 消息队列创建函数(兼容3.1) |
1051| aos_queue_free() | 消息队列删除函数 |
1052| aos_queue_send() | 向消息队列发送消息函数 |
1053| aos_queue_recv() | 从消息队列读取消息函数 |
1054| aos_queue_is_valid() | 判断消息队列具柄是否合法 |
1055| aos_queue_buf_ptr() | 获取消息队列消息数据区地址 |
1056| aos_queue_get_count() | 获取消息队列当前消息数 |
1057
1058## 常用配置
1059> 消息队列功能: 默认使能,如需修改,在yaml中修改RHINO_CONFIG_BUF_QUEUE配置
1060
1061```yaml
1062def_config:
1063  RHINO_CONFIG_BUF_QUEUE: 0
1064```
1065
1066
1067## API说明
1068
1069- 参考 [aos_kernel_queue](https://g.alicdn.com/alios-things-3.3/doc/group__aos__kernel__queue.html)
1070
1071## 使用示例
1072
1073- 示例代码参考[example/queue_example.c](https://gitee.com/alios-things/osal_aos/blob/master/example/queue_example.c),该示例使用消息队列实现任务间数据同步,具体场景为创建任务A和认为B,以及一消息队列。任务A作为生产者循环向消息队列发送消息,任务B作为消费者循环从消息队列接收消息,一般情况下,消费者处理数据是要花费很长时间,所以会导致消息产生的速度大于消息处理的速度,使得消息队列溢出。所以可以通过调整任务B优先级大于任务A来避免这种情况,或者使用信号量来控制数据收发同步。
1074- 示例说明如下:
10751. t0时刻,任务T调用aos_queue_new()创建一互斥量。任务T然后调用aos_task_create()创建任务A和任务B,任务A优先级设置为31,任务B优先级设置为30。任务B因消息队列无消息而阻塞,任务A得到运行向消息队列发送消息。
10761. t1时刻,任务B因从消息队列读取到消息而解除阻塞,任务B对消息进行处理后继续等待新的消息到来。
10771. t2时刻,任务A向消息队列发送消息。
10781. t3时刻,重复t1时刻的操作。
1079
1080<div align=left display=flex>
1081    <img src="https://img.alicdn.com/imgextra/i1/O1CN01VjMeUN1XLuQimsUEW_!!6000000002908-2-tps-438-190.png"  style="max-width:800px;" />
1082</div>
1083
1084- 该示例可配置到helloworld_demo中运行,相关代码的下载、编译和固件烧录均依赖AliOS Things配套的开发工具  ,所以首先需要参考[《AliOS Things集成开发环境使用说明之搭建开发环境》](https://help.aliyun.com/document_detail/302378.html),下载安装  。
1085待开发环境搭建完成后,可以按照以下步骤进行示例的测试。
1086
1087### 步骤1 创建或打开工程
1088
1089**打开已有工程**
1090
1091如果用于测试的案例工程已存在,可参考[《AliOS Things集成开发环境使用说明之打开工程》](https://help.aliyun.com/document_detail/302381.html)打开已有工程。
1092
1093**创建新的工程**
1094
1095组件的示例代码可以通过编译链接到AliOS Things的任意案例(solution)来运行,这里选择helloworld_demo案例。helloworld_demo案例相关的源代码下载可参考[《AliOS Things集成开发环境使用说明之创建工程》](https://help.aliyun.com/document_detail/302379.html)1096
1097### 步骤2 添加组件
1098
1099案例下载完成后,需要在helloworld_demo组件的package.yaml中添加对组件的依赖:
1100
1101```yaml
1102depends:
1103  - osal_aos: master # helloworld_demo中引入osal_aos组件
1104```
1105
1106### 步骤3 下载组件
1107
1108在已安装了  的开发环境工具栏中,选择Terminal -> New Terminal启动终端,并且默认工作路径为当前工程的workspace,此时在终端命令行中输入:
1109
1110```shell
1111
1112aos install osal_aos
1113
1114```
1115
1116上述命令执行成功后,组件源码则被下载到了./components/osal_aos路径中。
1117
1118### 步骤4 添加示例
1119
1120> osal_aos组件的package.yaml中添加[example示例代码](https://gitee.com/alios-things/osal_aos/tree/master/example)1121
1122```yaml
1123depends:
1124  - rhino: master
1125  - cli: master # 添加cli依赖
1126source_file:
1127  - "*.c"
1128  - "example/queue_example.c" # 添加 queue_example.c
1129```
1130
1131### 步骤5 编译固件
1132
1133在示例代码已经添加至组件的配置文件,并且helloworld_demo已添加了对该组件的依赖后,就可以编译helloworld_demo案例来生成固件了,具体编译方法可参考[《AliOS Things集成开发环境使用说明之编译固件》](https://help.aliyun.com/document_detail/302384.html)1134
1135### 步骤6 烧录固件
1136
1137helloworld_demo案例的固件生成后,可参考[《AliOS Things集成开发环境使用说明之烧录固件》](https://help.aliyun.com/document_detail/302383.html)来烧录固件。
1138
1139### 步骤7 打开串口
1140
1141固件烧录完成后,可以通过串口查看示例的运行结果,打开串口的具体方法可参考[《AliOS Things集成开发环境使用说明之查看日志》](https://help.aliyun.com/document_detail/302382.html)1142
1143当串口终端打开成功后,可在串口中输入help来查看已添加的测试命令。
1144
1145### 步骤8 测试示例
1146> CLI命令行输入:
1147
1148```shell
1149queue_example
1150```
1151
1152> 关键日志:
1153
1154```shell
1155[aos_queue_example]10 recv message : 3456789012
1156[aos_queue_example]10 recv message : 4567890123
1157[aos_queue_example]10 recv message : 5678901234
1158[aos_queue_example]10 recv message : 6789012345
1159[aos_queue_example]10 recv message : 7890123456
1160[aos_queue_example]10 recv message : 8901234567
1161[aos_queue_example]10 recv message : 9012345678
1162```
1163## 注意事项
1164
1165- 发送消息时,如果没有任务在等待接收消息,则此消息会暂时保存在环形缓冲区中,如果有任务在等待数据接收,则将消息直接传递给任务。
1166## FAQ
1167Q1: 调用aos_queue_recv()接口无限期的等待消息,ms参数怎么设置?
1168答:将ms赋值为AOS_WAIT_FOREVER。
1169
1170
1171# 工作队列
1172## 概述
1173在操作系统中,如果我们需要进行一项工作处理,往往需要创建一个任务来加入内核的调度队列。一个任务对应一个处理函数,如果要进行不同的事务处理,则需要创建多个不同的任务。任务作为CPU调度的基本单元,任务数量越大,则调度成本越高。工作队列(workqueue)机制简化了基础的任务创建和处理机制,一个workqueue对应一个实体task任务处理,workqueue下面可以挂接多个work实体,每一个work实体都能对应不同的处理接口。即用户只需要创建一个workqueue,则可以完成多个挂接不同处理函数的工作队列。其次,当某些实时性要求较高的任务中,需要进行较繁重钩子处理时,可以将其处理函数挂在workqueue中,其执行过程将位于workqueue的上下文,而不会占用原有任务的处理资源。另外,workqueue还提供了work的延时处理机制,用户可以选择立即执行或是延时处理。当应用需要创建大量实时性要求不高的任务时,可以使用workqueue来统一调度;或者将任务中实时性要求不高的部分处理延后到workqueue中处理。如果需要设置延后处理,则需要使用work机制。注意:该机制不支持周期work的处理。
1174### 工作机制
1175workqueue的处理依赖于task任务。一个workqueue队列会创建关联其对应的task任务,一个workqueue会挂载多个work处理,每个work处理对应一个处理函数。当workqueue得到调度,即其关联的task得到运行,在每次task的调度期间,都会从工作队列中按照先后顺序取出一个work来进行处理。workqueue模块在初始化时,会创建一个系统默认的工作队列,用户可根据需要将work添加到该队列中去执行。
1176### 工作队列功能函数
1177| **函数名** | **描述** |
1178| --- | --- |
1179| aos_workqueue_create() | 工作队列创建函数 |
1180| aos_workqueue_del() | 工作队列删除函数 |
1181| aos_work_init() | work创建函数 |
1182| aos_work_destroy() | work销毁函数 |
1183| aos_work_run() | work触发函数 |
1184| aos_work_sched() | work触发函数(由系统默认工作队列来执行) |
1185| aos_work_cancel() | 工作队列删除函数 |
1186
1187## 常用配置
1188> 消息队列功能: 默认使能,如需修改,在yaml中修改RHINO_CONFIG_BUF_QUEUE配置
1189
1190```yaml
1191def_config:
1192  RHINO_CONFIG_WORKQUEUE: 0
1193```
1194
1195
1196## API说明
1197
1198- 参考 [aos_kernel_workqueue](https://g.alicdn.com/alios-things-3.3/doc/group__aos__kernel__queue.html)
1199
1200## 使用示例
1201
1202- 示例代码参考[example/workqueue_example.c](https://gitee.com/alios-things/osal_aos/blob/master/example/workqueue_example.c), 该示例用工作队列实现不同场景的任务执行。示例说明如下:
12031. t0时刻,任务T调用aos_sem_create()创建一个信号量,调用aos_workqueue_create()创建一工作队列wq1。
1204     任务T然后调用aos_work_init()创建7个任务:work0、work1、work2、work3、work4、work5、work6,延迟参数分别为0、0、1、20、18、40、50。
12051. t1时刻,任务T调用aos_work_run()依次:
1206      a、将work0加入工作队列wq1
1207      b、将work1加入工作队列wq1
1208      c、将work1再次工作队列wq1
1209      d、将work2加入工作队列wq1
1210      e、将work2加入工作队列wq1
1211      f、将work3加入工作队列wq1
1212      g、将work4加入系统默认工作队列
1213      h、将work5加入系统默认工作队列
1214      i、将work6加入系统默认工作队列
12152. t2时刻,任务T调用aos_sem_wait()等待信号量进入阻塞状态,
12163. t3时刻,work4被执行释放信号量,任务T解除阻塞,将work5再加入系统默认工作队列后因等待信号量进入阻塞状态
12174. t4时刻,work5被执行释放信号量,任务T解除阻塞,释放资源。
1218
1219- 该示例可配置到helloworld_demo中运行,相关代码的下载、编译和固件烧录均依赖AliOS Things配套的开发工具  ,所以首先需要参考[《AliOS Things集成开发环境使用说明之搭建开发环境》](https://help.aliyun.com/document_detail/302378.html),下载安装  。
1220待开发环境搭建完成后,可以按照以下步骤进行示例的测试。
1221
1222### 步骤1 创建或打开工程
1223
1224**打开已有工程**
1225
1226如果用于测试的案例工程已存在,可参考[《AliOS Things集成开发环境使用说明之打开工程》](https://help.aliyun.com/document_detail/302381.html)打开已有工程。
1227
1228**创建新的工程**
1229
1230组件的示例代码可以通过编译链接到AliOS Things的任意案例(solution)来运行,这里选择helloworld_demo案例。helloworld_demo案例相关的源代码下载可参考[《AliOS Things集成开发环境使用说明之创建工程》](https://help.aliyun.com/document_detail/302379.html)1231
1232### 步骤2 添加组件
1233
1234案例下载完成后,需要在helloworld_demo组件的package.yaml中添加对组件的依赖:
1235
1236```yaml
1237depends:
1238  - osal_aos: master # helloworld_demo中引入osal_aos组件
1239```
1240
1241### 步骤3 下载组件
1242
1243在已安装了  的开发环境工具栏中,选择Terminal -> New Terminal启动终端,并且默认工作路径为当前工程的workspace,此时在终端命令行中输入:
1244
1245```shell
1246
1247aos install osal_aos
1248
1249```
1250
1251上述命令执行成功后,组件源码则被下载到了./components/osal_aos路径中。
1252
1253### 步骤4 添加示例
1254
1255> osal_aos组件的package.yaml中添加[example示例代码](https://gitee.com/alios-things/osal_aos/tree/master/example)1256
1257```yaml
1258depends:
1259  - rhino: master
1260  - cli: master # 添加cli依赖
1261source_file:
1262  - "*.c"
1263  - "example/workqueue_example.c" # 添加 workqueue_example.c
1264```
1265
1266### 步骤5 编译固件
1267
1268在示例代码已经添加至组件的配置文件,并且helloworld_demo已添加了对该组件的依赖后,就可以编译helloworld_demo案例来生成固件了,具体编译方法可参考[《AliOS Things集成开发环境使用说明之编译固件》](https://help.aliyun.com/document_detail/302384.html)1269
1270### 步骤6 烧录固件
1271
1272helloworld_demo案例的固件生成后,可参考[《AliOS Things集成开发环境使用说明之烧录固件》](https://help.aliyun.com/document_detail/302383.html)来烧录固件。
1273
1274### 步骤7 打开串口
1275
1276固件烧录完成后,可以通过串口查看示例的运行结果,打开串口的具体方法可参考[《AliOS Things集成开发环境使用说明之查看日志》](https://help.aliyun.com/document_detail/302382.html)1277
1278当串口终端打开成功后,可在串口中输入help来查看已添加的测试命令。
1279
1280### 步骤8 测试示例
1281> CLI命令行输入:
1282
1283```shell
1284workqueue_example
1285```
1286
1287> 关键日志:
1288
1289```shell
1290--WORK 0--
1291--WORK 1--
1292--WORK 1--
1293--WORK 2--
1294--WORK 4--
1295--WORK 3--
1296--WORK 6--
1297--WORK 5--
1298```
1299## 注意事项
1300
1301- 当work延时参数为0时,调用aos_work_run()触发多次,则会在对应工作队列中会被执行多次;若work延迟参数不为0,调用aos_work_run()触发多次,则只会在对应工作队列中被执行一次。
1302## FAQ
1303Q1: 系统默认工作队列对应的任务优先级和栈大小是多少?
1304答:优先级为:20,栈大小为:3072字节。
1305