Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenHarmony
Docs
提交
ad9b265f
D
Docs
项目概览
OpenHarmony
/
Docs
1 年多 前同步成功
通知
159
Star
292
Fork
28
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
Docs
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
未验证
提交
ad9b265f
编写于
10月 21, 2022
作者:
O
openharmony_ci
提交者:
Gitee
10月 21, 2022
浏览文件
操作
浏览文件
下载
差异文件
!10454 内核开发文档优化(M核基础部分)
Merge pull request !10454 from wangchen/1010_d
上级
8f7788f2
d5ea037a
变更
13
隐藏空白更改
内联
并排
Showing
13 changed file
with
464 addition
and
389 deletion
+464
-389
zh-cn/device-dev/kernel/Readme-CN.md
zh-cn/device-dev/kernel/Readme-CN.md
+2
-2
zh-cn/device-dev/kernel/kernel-mini-basic-interrupt.md
zh-cn/device-dev/kernel/kernel-mini-basic-interrupt.md
+71
-36
zh-cn/device-dev/kernel/kernel-mini-basic-ipc-event.md
zh-cn/device-dev/kernel/kernel-mini-basic-ipc-event.md
+33
-34
zh-cn/device-dev/kernel/kernel-mini-basic-ipc-mutex.md
zh-cn/device-dev/kernel/kernel-mini-basic-ipc-mutex.md
+43
-44
zh-cn/device-dev/kernel/kernel-mini-basic-ipc-queue.md
zh-cn/device-dev/kernel/kernel-mini-basic-ipc-queue.md
+52
-50
zh-cn/device-dev/kernel/kernel-mini-basic-ipc-sem.md
zh-cn/device-dev/kernel/kernel-mini-basic-ipc-sem.md
+21
-31
zh-cn/device-dev/kernel/kernel-mini-basic-list.md
zh-cn/device-dev/kernel/kernel-mini-basic-list.md
+18
-16
zh-cn/device-dev/kernel/kernel-mini-basic-memory.md
zh-cn/device-dev/kernel/kernel-mini-basic-memory.md
+53
-41
zh-cn/device-dev/kernel/kernel-mini-basic-soft.md
zh-cn/device-dev/kernel/kernel-mini-basic-soft.md
+86
-68
zh-cn/device-dev/kernel/kernel-mini-basic-task.md
zh-cn/device-dev/kernel/kernel-mini-basic-task.md
+42
-36
zh-cn/device-dev/kernel/kernel-mini-basic-time.md
zh-cn/device-dev/kernel/kernel-mini-basic-time.md
+40
-28
zh-cn/device-dev/porting/porting-stm32f407-on-minisystem-eth.md
...device-dev/porting/porting-stm32f407-on-minisystem-eth.md
+1
-1
zh-cn/device-dev/website.md
zh-cn/device-dev/website.md
+2
-2
未找到文件。
zh-cn/device-dev/kernel/Readme-CN.md
浏览文件 @
ad9b265f
...
...
@@ -13,8 +13,9 @@
-
[
互斥锁
](
kernel-mini-basic-ipc-mutex.md
)
-
[
消息队列
](
kernel-mini-basic-ipc-queue.md
)
-
[
信号量
](
kernel-mini-basic-ipc-sem.md
)
-
[
时间管理
](
kernel-
basic-mini
-time.md
)
-
[
时间管理
](
kernel-
mini-basic
-time.md
)
-
[
软件定时器
](
kernel-mini-basic-soft.md
)
-
[
双向链表
](
kernel-mini-basic-list.md
)
-
扩展组件
-
[
C++支持
](
kernel-mini-extend-support.md
)
-
[
CPU占用率
](
kernel-mini-extend-cpup.md
)
...
...
@@ -27,7 +28,6 @@
-
[
LMS调测
](
kernel-mini-memory-lms.md
)
-
附录
-
[
内核编码规范
](
kernel-mini-appx-code.md
)
-
[
双向链表
](
kernel-mini-appx-data-list.md
)
-
[
标准库支持
](
kernel-mini-appx-lib.md
)
-
小型系统内核(LiteOS-A)
-
[
小型系统内核概述
](
kernel-small-overview.md
)
...
...
zh-cn/device-dev/kernel/kernel-mini-basic-interrupt.md
浏览文件 @
ad9b265f
...
...
@@ -9,25 +9,25 @@
下面介绍下中断的相关概念:
-
中断号
-
中断号
:
中断请求信号特定的标志,计算机能够根据中断号判断是哪个设备提出的中断请求。
-
中断请求
-
中断请求
:
“紧急事件”向CPU提出申请(发一个电脉冲信号),请求中断,需要CPU暂停当前执行的任务处理该“紧急事件”,这一过程称为中断请求。
-
中断优先级
-
中断优先级
:
为使系统能够及时响应并处理所有中断,系统根据中断事件的重要性和紧迫程度,将中断源分为若干个级别,称作中断优先级。
-
中断处理程序
-
中断处理程序
:
当外设发出中断请求后,CPU暂停当前的任务,转而响应中断请求,即执行中断处理程序。产生中断的每个设备都有相应的中断处理程序。
-
中断触发
-
中断触发
:
中断源向中断控制器发送中断信号,中断控制器对中断进行仲裁,确定优先级,将中断信号发送给CPU。中断源产生中断信号的时候,会将中断触发器置“1”,表明该中断源产生了中断,要求CPU去响应该中断。
-
中断向量
-
中断向量
:
中断服务程序的入口地址。
-
中断向量表
-
中断向量表
:
存储中断向量的存储区,中断向量与中断号对应,中断向量在中断向量表中按照中断号顺序存储。
...
...
@@ -37,37 +37,46 @@ OpenHarmony LiteOS-M内核的中断模块提供下面几种功能,接口详细
**表1**
创建、删除中断
| 接口名 | 描述 |
| 接口名 | 描述 |
| -------- | -------- |
|
HalHwiCreate | 中断创建,注册中断号、中断触发模式、中断优先级、中断处理程序。中断被触发时,会调用该中断处理程序。 |
|
HalHwiDelete | 根据指定的中断号,删除中断。 |
|
LOS_HwiCreate | 中断创建,注册中断号、中断触发模式、中断优先级、中断处理程序。中断被触发时,会调用该中断处理程序。 |
|
LOS_HwiDelete | 根据指定的中断号,删除中断。 |
**表2**
打开、关闭中断
| 接口名 | 描述 |
| 接口名 | 描述 |
| -------- | -------- |
| LOS_IntUnLock | 开中断,使能当前处理器所有中断响应。 |
| LOS_IntLock | 关中断,关闭当前处理器所有中断响应。 |
| LOS_IntRestore | 恢复到使用LOS_IntLock、LOS_IntUnLock操作之前的中断状态。 |
| LOS_IntUnLock | 开中断,使能当前处理器所有中断响应。 |
| LOS_IntLock | 关中断,关闭当前处理器所有中断响应。 |
| LOS_IntRestore | 恢复到使用LOS_IntLock、LOS_IntUnLock操作之前的中断状态。 |
**表3**
其他中断操作
| 接口名 | 描述 |
| :----------------- | ---------------- |
| LOS_HwiTrigger | 中断触发。 |
| LOS_HwiEnable | 中断使能。 |
| LOS_HwiDisable | 中断禁用。 |
| LOS_HwiClear | 中断手动清除。 |
| LOS_HwiSetPriority | 设置中断优先级。 |
| LOS_HwiCurIrqNum | 获取当前中断号。 |
## 开发流程
1.
调用中断创建接口
Hal
HwiCreate创建中断。
1.
调用中断创建接口
LOS_
HwiCreate创建中断。
2.
调用
TestHwiTrigger接口触发指定中断(该接口在测试套中定义,通过写中断控制器的相关寄存器模拟外部中断,一般的外设设备,不需要执行这一步)
。
2.
调用
LOS_HwiTrigger接口触发指定中断(写中断控制器的相关寄存器模拟外部中断),或通过外设触发中断
。
3.
调用
HalHwiDelete接口删除指定中断,此接口根据实际情况使用,
判断是否需要删除中断。
3.
调用
LOS_HwiDelete接口删除指定中断,此接口根据实际情况使用,开发者
判断是否需要删除中断。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> - 根据具体硬件,配置支持的最大中断数及可设置的中断优先级个数。
>
> - 中断处理程序耗时不能过长,否则会影响CPU对中断的及时响应。
>
> - 关中断时间或中断处理程序耗时不能过长,否则会影响CPU对中断的及时响应。
> - 中断响应过程中不能直接、间接执行引起调度的LOS_Schedule等函数。
>
> -
中断恢复LOS_IntRestore()的入参必须是与之对应的LOS_IntLock()的返回值(即关中断之前的CPSR值)。
Cortex-M系列处理器中0-15中断为内部使用,因此不建议用户去申请和创建。
>
- 中断恢复LOS_IntRestore()的入参必须是与之对应的LOS_IntLock()的返回值(即关中断之前的CPSR值)。
> - Cortex-M系列处理器中0-15中断为内部使用,因此不建议用户去申请和创建。
## 编程实例
...
...
@@ -80,29 +89,39 @@ OpenHarmony LiteOS-M内核的中断模块提供下面几种功能,接口详细
3.
删除中断。
代码实现如下,演示如何创建中断和删除中断,当指定的中断号HWI_NUM_TEST产生中断时,会调用中断处理函数:
代码实现如下,演示如何创建中断、触发指定的中断号进而调用中断处理函数、删除中断。
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleInterrupt。
```
#include "los_interrupt.h"
#include "los_compiler.h"
/*
创建中断
*/
/*
验证的中断号
*/
#define HWI_NUM_TEST 7
STATIC VOID HwiUsrIrq(VOID)
/* 中断处理程序 */
STATIC VOID UsrIrqEntry(VOID)
{
printf("in the func
HwiUsrIrq \n");
printf("in the func
UsrIrqEntry\n");
}
static UINT32 Example_Interrupt(VOID)
/* 注册的线程回调函数,用于触发中断 */
STATIC VOID InterruptTest(VOID)
{
LOS_HwiTrigger(HWI_NUM_TEST);
}
UINT32 ExampleInterrupt(VOID)
{
UINT32 ret;
HWI_PRIOR_T hwiPrio = 3;
HWI_PRIOR_T hwiPrio = 3;
// 3,中断优先级
HWI_MODE_T mode = 0;
HWI_ARG_T arg = 0;
/*
创建中断
*/
ret =
HalHwiCreate(HWI_NUM_TEST, hwiPrio, mode, (HWI_PROC_FUNC)HwiUsrIrq
, arg);
/*
创建中断
*/
ret =
LOS_HwiCreate(HWI_NUM_TEST, hwiPrio, mode, (HWI_PROC_FUNC)UsrIrqEntry
, arg);
if(ret == LOS_OK){
printf("Hwi create success!\n");
} else {
...
...
@@ -110,17 +129,32 @@ static UINT32 Example_Interrupt(VOID)
return LOS_NOK;
}
/* 延时50个Ticks, 当有硬件中断发生时,会调用函数HwiUsrIrq*/
TSK_INIT_PARAM_S taskParam = { 0 };
UINT32 testTaskID;
/* 创建一个优先级低优先级的线程,用于验证触发中断 */
taskParam.pfnTaskEntry = (TSK_ENTRY_FUNC)InterruptTest;
taskParam.uwStackSize = OS_TSK_TEST_STACK_SIZE;
taskParam.pcName = "InterruptTest";
taskParam.usTaskPrio = TASK_PRIO_TEST - 1;
taskParam.uwResved = LOS_TASK_ATTR_JOINABLE;
ret = LOS_TaskCreate(&testTaskID, &taskParam);
if (LOS_OK != ret) {
PRINTF("InterruptTest task error\n");
}
/* 延时50 tick,让出当前线程的调度 */
LOS_TaskDelay(50);
/*
删除中断
*/
ret =
HalHwiDelete(HWI_NUM_TEST);
/*
删除注册的中断
*/
ret =
LOS_HwiDelete(HWI_NUM_TEST, NULL);
if(ret == LOS_OK){
printf("Hwi delete success!\n");
} else {
printf("Hwi delete failed!\n");
return LOS_NOK;
}
return LOS_OK;
}
```
...
...
@@ -131,8 +165,9 @@ static UINT32 Example_Interrupt(VOID)
编译运行得到的结果为:
```
Hwi create success!
in the func UsrIrqEntry
Hwi delete success!
```
zh-cn/device-dev/kernel/kernel-mini-basic-ipc-event.md
浏览文件 @
ad9b265f
...
...
@@ -16,14 +16,12 @@
## 运行机制
### 事件控制块
由事件初始化函数配置的一个结构体,在事件读写等操作时作为参数传入,用于标识不同的事件,控制块数据结构如下:
```
/**
* 事件控制块数据结构
*/
typedef struct tagEvent {
UINT32 uwEventID; /* 事件集合,表示已经处理(写入和清零)的事件集合 */
LOS_DL_LIST stEventList; /* 等待特定事件的任务链表 */
...
...
@@ -33,7 +31,7 @@ typedef struct tagEvent {
### 事件运作原理
**事件初始化**
:
会
创建一个事件控制块,该控制块维护一个已处理的事件集合,以及等待特定事件的任务链表。
**事件初始化**
:创建一个事件控制块,该控制块维护一个已处理的事件集合,以及等待特定事件的任务链表。
**写事件**
:会向事件控制块写入指定的事件,事件控制块更新事件集合,并遍历任务链表,根据任务等待具体条件满足情况决定是否唤醒相关任务。
...
...
@@ -96,31 +94,29 @@ typedef struct tagEvent {
### 实例描述
示例中,任务Example
_TaskEntry创建一个任务Example_Event,Example_Event读事件阻塞,Example_TaskEntry
向该任务写事件。可以通过示例日志中打印的先后顺序理解事件操作时伴随的任务切换。
示例中,任务Example
Event创建一个任务EventReadTask,EventReadTask读事件阻塞,ExampleEvent
向该任务写事件。可以通过示例日志中打印的先后顺序理解事件操作时伴随的任务切换。
1.
在任务Example
_TaskEntry创建任务Example_Event,其中任务Example_Event优先级高于Example_TaskEntry
。
1.
在任务Example
Event创建任务EventReadTask,其中任务EventReadTask优先级高于ExampleEvent
。
2.
在任务E
xample_Event中读事件0x00000001,阻塞,发生任务切换,执行任务Example_TaskEntry
。
2.
在任务E
ventReadTask中读事件0x00000001,阻塞,发生任务切换,执行任务ExampleEvent
。
3.
在任务Example
_TaskEntry向任务Example_Event写事件0x00000001,发生任务切换,执行任务Example_Event
。
3.
在任务Example
Event写事件0x00000001,发生任务切换,执行任务EventReadTask
。
4.
E
xample_Event
得以执行,直到任务结束。
4.
E
ventReadTask
得以执行,直到任务结束。
5.
Example
_TaskEntry
得以执行,直到任务结束。
5.
Example
Event
得以执行,直到任务结束。
### 示例代码
示例代码如下:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleEvent。
```
#include "los_event.h"
#include "los_task.h"
#include "securec.h"
/* 任务ID */
UINT32 g_testTaskId;
/* 事件控制结构体 */
EVENT_CB_S g_exampleEvent;
...
...
@@ -128,8 +124,11 @@ EVENT_CB_S g_exampleEvent;
/* 等待的事件类型 */
#define EVENT_WAIT 0x00000001
/* 等待超时时间 */
#define EVENT_TIMEOUT 100
/* 用例任务入口函数 */
VOID E
xample_Event
(VOID)
VOID E
ventReadTask
(VOID)
{
UINT32 ret;
UINT32 event;
...
...
@@ -137,39 +136,39 @@ VOID Example_Event(VOID)
/* 超时等待方式读事件,超时时间为100 ticks, 若100 ticks后未读取到指定事件,读事件超时,任务直接唤醒 */
printf("Example_Event wait event 0x%x \n", EVENT_WAIT);
event = LOS_EventRead(&g_exampleEvent, EVENT_WAIT, LOS_WAITMODE_AND,
100
);
event = LOS_EventRead(&g_exampleEvent, EVENT_WAIT, LOS_WAITMODE_AND,
EVENT_TIMEOUT
);
if (event == EVENT_WAIT) {
printf("Example_Event,read event :0x%x\n", event);
printf("Example_Event,
read event :0x%x\n", event);
} else {
printf("Example_Event,read event timeout\n");
printf("Example_Event,
read event timeout\n");
}
}
UINT32 Example
_TaskEntry
(VOID)
UINT32 Example
Event
(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S task1;
UINT32 taskId;
TSK_INIT_PARAM_S taskParam = { 0 };
/* 事件初始化 */
ret = LOS_EventInit(&g_exampleEvent);
if (ret != LOS_OK) {
printf("init event failed .\n");
return
-1
;
return
LOS_NOK
;
}
/* 创建任务 */
(VOID)memset_s(&task1, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
task1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Event;
task1.pcName = "EventTsk1";
task1.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE;
task1.usTaskPrio = 5;
ret = LOS_TaskCreate(&g_testTaskId, &task1);
taskParam.pfnTaskEntry = (TSK_ENTRY_FUNC)EventReadTask;
taskParam.pcName = "EventReadTask";
taskParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
taskParam.usTaskPrio = 3;
ret = LOS_TaskCreate(&taskId, &taskParam);
if (ret != LOS_OK) {
printf("task create failed.\n");
return LOS_NOK;
}
/* 写
g_testTaskId 等待
事件 */
/* 写事件 */
printf("Example_TaskEntry write event.\n");
ret = LOS_EventWrite(&g_exampleEvent, EVENT_WAIT);
...
...
@@ -183,10 +182,10 @@ UINT32 Example_TaskEntry(VOID)
LOS_EventClear(&g_exampleEvent, ~g_exampleEvent.uwEventID);
printf("EventMask:%d\n", g_exampleEvent.uwEventID);
/* 删除
任务
*/
ret = LOS_
TaskDelete(g_testTaskId
);
/* 删除
事件
*/
ret = LOS_
EventDestroy(&g_exampleEvent
);
if (ret != LOS_OK) {
printf("
task delete failed
.\n");
printf("
destory event failed
.\n");
return LOS_NOK;
}
...
...
@@ -202,9 +201,9 @@ UINT32 Example_TaskEntry(VOID)
```
Example_Event wait event 0x1
Example_Event wait event 0x1
Example_TaskEntry write event.
Example_Event,read event :0x1
Example_Event,
read event :0x1
EventMask:1
EventMask:0
```
zh-cn/device-dev/kernel/kernel-mini-basic-ipc-mutex.md
浏览文件 @
ad9b265f
...
...
@@ -5,7 +5,7 @@
互斥锁又称互斥型信号量,是一种特殊的二值性信号量,用于实现对共享资源的独占式处理。
任意时刻互斥锁的状态只有两种,开锁或闭锁。当
有任务持有时,互斥锁处于闭锁状态,这个任务获得该互斥锁的所有权。当该任务释放它
时,该互斥锁被开锁,任务失去该互斥锁的所有权。当一个任务持有互斥锁时,其他任务将不能再对该互斥锁进行开锁或持有。
任意时刻互斥锁的状态只有两种,开锁或闭锁。当
任务持有互斥锁时,该互斥锁处于闭锁状态,这个任务获得该互斥锁的所有权。当该任务释放互斥锁
时,该互斥锁被开锁,任务失去该互斥锁的所有权。当一个任务持有互斥锁时,其他任务将不能再对该互斥锁进行开锁或持有。
多任务环境下往往存在多个任务竞争同一共享资源的应用场景,互斥锁可被用于对共享资源的保护从而实现独占式访问。另外互斥锁可以解决信号量存在的优先级翻转问题。
...
...
@@ -24,10 +24,10 @@
**表1**
互斥锁模块接口
| 功能分类 | 接口描述 |
| 功能分类 | 接口描述 |
| -------- | -------- |
| 互斥锁的创建和删除 | LOS_MuxCreate:创建互斥锁
<br/>
LOS_MuxDelete:删除指定的互斥锁 |
| 互斥锁的申请和释放 | LOS_MuxPend:申请指定的互斥锁
<br/>
LOS_MuxPost:释放指定的互斥锁 |
| 互斥锁的创建和删除 | LOS_MuxCreate:创建互斥锁
。
<br/>
LOS_MuxDelete:删除指定的互斥锁。 |
| 互斥锁的申请和释放 | LOS_MuxPend:申请指定的互斥锁
。
<br/>
LOS_MuxPost:释放指定的互斥锁。 |
## 开发流程
...
...
@@ -39,7 +39,7 @@
2.
申请互斥锁LOS_MuxPend。
申请模式有三种:无阻塞模式、永久阻塞模式、定时阻塞模式。
-
无阻塞模式:任务需要申请互斥锁,若该互斥锁当前没有任务持有,或者持有该互斥锁的任务和申请该互斥锁的任务为同一个任务,则申请成功。
-
无阻塞模式:任务需要申请互斥锁,若该互斥锁当前没有任务持有,或者持有该互斥锁的任务和申请该互斥锁的任务为同一个任务,则申请成功。
否则直接返回并继续运行当前任务,不会产生阻塞。
-
永久阻塞模式:任务需要申请互斥锁,若该互斥锁当前没有被占用,则申请成功。否则,该任务进入阻塞态,系统切换到就绪任务中优先级高者继续执行。任务进入阻塞态后,直到有其他任务释放该互斥锁,阻塞任务才会重新得以执行。
-
定时阻塞模式:任务需要申请互斥锁,若该互斥锁当前没有被占用,则申请成功。否则该任务进入阻塞态,系统切换到就绪任务中优先级高者继续执行。任务进入阻塞态后,指定时间超时前有其他任务释放该互斥锁,或者用户指定时间超时后,阻塞任务才会重新得以执行。
...
...
@@ -50,7 +50,7 @@
4.
删除互斥锁LOS_MuxDelete。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> -
两个任务不能对同一把互斥锁加锁。如果某任务对已被持有的互斥锁加锁,则该任务会被挂起,直到持有该锁的任务对互斥锁解锁,才能执行对这把互斥锁的加锁操作
。
> -
互斥锁支持嵌套,即申请该互斥锁的任务与已经持有该互斥锁的任务为同一个任务时会认为申请成功,按申请次数对应的去释放该锁即可
。
>
> - 互斥锁不能在中断服务程序中使用。
>
...
...
@@ -66,67 +66,66 @@
本实例实现如下流程。
1.
任务Example
_TaskEntry创建一个互斥锁,锁任务调度,创建两个任务Example_MutexTask1、Example_MutexTask2。Example_MutexTask2优先级高于Example_
MutexTask1,解锁任务调度。
1.
任务Example
Mutex创建一个互斥锁,锁任务调度,创建两个任务ExampleMutexTask1、ExampleMutexTask2。ExampleMutexTask2优先级高于Example
MutexTask1,解锁任务调度。
2.
Example
_MutexTask2被调度,以永久阻塞模式申请互斥锁,并成功获取到该互斥锁,然后任务休眠100Tick,Example_MutexTask2挂起,Example_
MutexTask1被唤醒。
2.
Example
MutexTask2被调度,以永久阻塞模式申请互斥锁,并成功获取到该互斥锁,然后任务休眠100Tick,ExampleMutexTask2挂起,Example
MutexTask1被唤醒。
3.
Example
_MutexTask1以定时阻塞模式申请互斥锁,等待时间为10Tick,因互斥锁仍被Example_MutexTask2持有,Example_MutexTask1挂起。10Tick超时时间到达后,Example_MutexTask1被唤醒,以永久阻塞模式申请互斥锁,因互斥锁仍被Example_MutexTask2持有,Example_
MutexTask1挂起。
3.
Example
MutexTask1以定时阻塞模式申请互斥锁,等待时间为10Tick,因互斥锁仍被ExampleMutexTask2持有,ExampleMutexTask1挂起。10Tick超时时间到达后,ExampleMutexTask1被唤醒,以永久阻塞模式申请互斥锁,因互斥锁仍被ExampleMutexTask2持有,Example
MutexTask1挂起。
4.
100Tick休眠时间到达后,Example
_MutexTask2被唤醒, 释放互斥锁,唤醒Example_MutexTask1。Example_MutexTask1成功获取到互斥锁后,释放,
删除互斥锁。
4.
100Tick休眠时间到达后,Example
MutexTask2被唤醒, 释放互斥锁,唤醒ExampleMutexTask1。ExampleMutexTask1成功获取到互斥锁后,释放并
删除互斥锁。
### 示例代码
示例代码如下:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleMutex。
```
#include <string.h>
#include "los_mux.h"
/* 互斥锁句柄
id
*/
/* 互斥锁句柄 */
UINT32 g_testMux;
/* 任务ID */
UINT32 g_testTaskId01;
UINT32 g_testTaskId02;
VOID Example
_
MutexTask1(VOID)
VOID ExampleMutexTask1(VOID)
{
UINT32 ret;
printf("task1 try to get mutex, wait 10 ticks.\n");
/* 申请互斥锁 */
ret = LOS_MuxPend(g_testMux, 10);
if (ret == LOS_OK) {
printf("task1 get mutex g_testMux.\n");
/* 释放互斥锁 */
/* 释放互斥锁
,这个分支正常不应该进来
*/
LOS_MuxPost(g_testMux);
LOS_MuxDelete(g_testMux);
return;
}
}
if (ret == LOS_ERRNO_MUX_TIMEOUT ) {
printf("task1 timeout and try to get mutex, wait forever.\n");
/* 申请互斥锁 */
ret = LOS_MuxPend(g_testMux, LOS_WAIT_FOREVER);
if (ret == LOS_OK) {
printf("task1 wait forever, get mutex g_testMux.\n");
/* 释放互斥锁 */
LOS_MuxPost(g_testMux);
/* 删除互斥锁 */
LOS_MuxDelete(g_testMux);
printf("task1 post and delete mutex g_testMux.\n");
return;
}
printf("task1 timeout and try to get mutex, wait forever.\n");
/* 申请互斥锁 */
ret = LOS_MuxPend(g_testMux, LOS_WAIT_FOREVER);
if (ret == LOS_OK) {
printf("task1 wait forever, get mutex g_testMux.\n");
/* 释放互斥锁 */
LOS_MuxPost(g_testMux);
/* 删除互斥锁 */
LOS_MuxDelete(g_testMux);
printf("task1 post and delete mutex g_testMux.\n");
return;
}
}
return;
}
VOID Example
_
MutexTask2(VOID)
VOID ExampleMutexTask2(VOID)
{
printf("task2 try to get mutex, wait forever.\n");
/* 申请互斥锁 */
(VOID)LOS_MuxPend(g_testMux, LOS_WAIT_FOREVER);
printf("task2 get mutex g_testMux and suspend 100 ticks.\n");
/* 任务休眠100Ticks */
...
...
@@ -138,11 +137,13 @@ VOID Example_MutexTask2(VOID)
return;
}
UINT32 Example
_TaskEntry
(VOID)
UINT32 Example
Mutex
(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S task1;
TSK_INIT_PARAM_S task2;
TSK_INIT_PARAM_S task1 = { 0 };
TSK_INIT_PARAM_S task2 = { 0 };
UINT32 taskId01;
UINT32 taskId02;
/* 创建互斥锁 */
LOS_MuxCreate(&g_testMux);
...
...
@@ -151,24 +152,22 @@ UINT32 Example_TaskEntry(VOID)
LOS_TaskLock();
/* 创建任务1 */
memset(&task1, 0, sizeof(TSK_INIT_PARAM_S));
task1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_MutexTask1;
task1.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleMutexTask1;
task1.pcName = "MutexTsk1";
task1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task1.usTaskPrio = 5;
ret = LOS_TaskCreate(&
g_testT
askId01, &task1);
ret = LOS_TaskCreate(&
t
askId01, &task1);
if (ret != LOS_OK) {
printf("task1 create failed.\n");
return LOS_NOK;
}
/* 创建任务2 */
memset(&task2, 0, sizeof(TSK_INIT_PARAM_S));
task2.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_MutexTask2;
task2.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleMutexTask2;
task2.pcName = "MutexTsk2";
task2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task2.usTaskPrio = 4;
ret = LOS_TaskCreate(&
g_testT
askId02, &task2);
ret = LOS_TaskCreate(&
t
askId02, &task2);
if (ret != LOS_OK) {
printf("task2 create failed.\n");
return LOS_NOK;
...
...
@@ -185,7 +184,7 @@ UINT32 Example_TaskEntry(VOID)
### 结果验证
编译运行得到的结果为:
```
task2 try to get mutex, wait forever.
task2 get mutex g_testMux and suspend 100 ticks.
...
...
zh-cn/device-dev/kernel/kernel-mini-basic-ipc-queue.md
浏览文件 @
ad9b265f
...
...
@@ -3,7 +3,7 @@
## 基本概念
队列又称消息队列,是一种常用于任务间通信的数据结构。
队列接收来自任务或中断的不固定长度消息,并根据不同的接口确定传递的消息是否存放在队列空间中。
消息队列又称队列,是一种任务间通信的机制。消息
队列接收来自任务或中断的不固定长度消息,并根据不同的接口确定传递的消息是否存放在队列空间中。
任务能够从队列里面读取消息,当队列中的消息为空时,挂起读取任务;当队列中有新消息时,挂起的读取任务被唤醒并处理新消息。任务也能够往队列里写入消息,当队列已经写满消息时,挂起写入任务;当队列中有空闲消息节点时,挂起的写入任务被唤醒并写入消息。
...
...
@@ -12,44 +12,40 @@
消息队列提供了异步处理机制,允许将一个消息放入队列,但不立即处理。同时队列还有缓冲消息的作用,可以使用队列实现任务异步通信,队列具有如下特性:
-
消息以先进先出的方式排队,支持异步读写。
-
读队列和写队列都支持超时机制。
-
每读取一条消息,就会将该消息节点设置为空闲。
-
发送消息类型由通信双方约定,可以允许不同长度(不超过队列的消息节点大小)的消息。
-
一个任务能够从任意一个消息队列接收和发送消息。
-
多个任务能够从同一个消息队列接收和发送消息。
-
创建
队列时所需的队列空间,接口内系统自行动态申请内存
。
-
创建普通队列时所需的队列空间,由系统自行动态申请内存。
-
创建
静态队列时所需的队列空间,由用户传入。这块空间在队列删除之后也由用户去释放
。
## 运行机制
### 队列控制块
队列会在初始化时给分配一个属于自己的控制块,控制块包含了队列的名称、状态等信息。删除队列时会释放该控制块。
队列控制块数据结构如下:
```
/**
* 队列控制块数据结构
*/
typedef struct
{
UINT8 *queue; /* 队列消息内存空间的指针 */
UINT16 queueState; /* 队列状态 */
UINT16 queueLen; /* 队列中消息节点个数,即队列长度 */
UINT16 queueSize; /* 消息节点大小 */
UINT16 queueID; /* 队列ID */
UINT16 queueHead; /* 消息头节点位置(数组下标)*/
UINT16 queueTail; /* 消息尾节点位置(数组下标)*/
UINT16 readWriteableCnt[OS_READWRITE_LEN]; /* 数组下标0的元素表示队列中可读消息数,
数组下标1的元素表示队列中可写消息数 */
LOS_DL_LIST readWriteList[OS_READWRITE_LEN]; /* 读取或写入消息的任务等待链表,
下标0:读取链表,下标1:写入链表 */
LOS_DL_LIST memList; /* 内存块链表 */
UINT8 *queue; /* 队列消息内存空间的指针 */
UINT8 *queueName /* 队列名称 */
UINT16 queueState; /* 队列状态 */
UINT16 queueLen; /* 队列中消息节点个数,即队列长度 */
UINT16 queueSize; /* 消息节点大小 */
UINT16 queueID; /* 队列ID */
UINT16 queueHead; /* 消息头节点位置(数组下标)*/
UINT16 queueTail; /* 消息尾节点位置(数组下标)*/
UINT16 readWriteableCnt[OS_READWRITE_LEN]; /* 数组下标0的元素表示队列中可读消息数,
数组下标1的元素表示队列中可写消息数 */
LOS_DL_LIST readWriteList[OS_READWRITE_LEN]; /* 读取或写入消息的任务等待链表,
下标0:读取链表,下标1:写入链表 */
LOS_DL_LIST memList; /* 内存块链表 */
} LosQueueCB;
```
...
...
@@ -81,12 +77,12 @@ typedef struct
## 接口说明
| 功能分类 | 接口描述 |
| 功能分类 | 接口描述 |
| -------- | -------- |
| 创建/删除消息队列 |
-
LOS_QueueCreate:创建一个消息队列,由系统动态申请队列空间。
<br/>
-
LOS_QueueDelete:根据队列ID删除一个指定队列。 |
| 读/写队列(不带拷贝) |
-
LOS_QueueRead:读取指定队列头节点中的数据(队列节点中的数据实际上是一个地址)。
<br/>
-
LOS_QueueWrite:向指定队列尾节点中写入入参bufferAddr的值(即buffer的地址)。
<br/>
-
LOS_QueueWriteHead:向指定队列头节点中写入入参bufferAddr的值(即buffer的地址)。 |
| 读/写队列(带拷贝) |
-
LOS_QueueReadCopy:读取指定队列头节点中的数据。
<br/>
-
LOS_QueueWriteCopy:向指定队列尾节点中写入入参bufferAddr中保存的数据。
<br/>
-
LOS_QueueWriteHeadCopy:向指定队列头节点中写入入参bufferAddr中保存的数据。 |
| 获取队列信息 | LOS_QueueInfoGet:获取指定队列的信息,包括队列ID、队列长度、消息节点大小、头节点、尾节点、可读节点数量、可写节点数量、等待读操作的任务、等待写操作的任务。 |
| 创建/删除消息队列 |
LOS_QueueCreate:创建一个消息队列,由系统动态申请队列空间。
<br/>
LOS_QueueCreateStatic:创建一个消息队列,由用户传入队列空间。
<br/>
LOS_QueueDelete:根据队列ID删除一个指定队列,静态消息队列删除后,队列空间需要用例自行处理。 |
| 读/写队列(不带拷贝) |
LOS_QueueRead:读取指定队列头节点中的数据(队列节点中的数据实际上是一个地址)。
<br/>
LOS_QueueWrite:向指定队列尾节点中写入入参bufferAddr的值(即buffer的地址)。
<br/>
LOS_QueueWriteHead:向指定队列头节点中写入入参bufferAddr的值(即buffer的地址)。 |
| 读/写队列(带拷贝) |
LOS_QueueReadCopy:读取指定队列头节点中的数据。
<br/>
LOS_QueueWriteCopy:向指定队列尾节点中写入入参bufferAddr中保存的数据。
<br/>
LOS_QueueWriteHeadCopy:向指定队列头节点中写入入参bufferAddr中保存的数据。 |
| 获取队列信息 | LOS_QueueInfoGet:获取指定队列的信息,包括队列ID、队列长度、消息节点大小、头节点、尾节点、可读节点数量、可写节点数量、等待读操作的任务、等待写操作的任务。 |
## 开发流程
...
...
@@ -140,11 +136,14 @@ typedef struct
示例代码如下:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleQueue。
```
#include "los_task.h"
#include "los_queue.h"
static UINT32 g_queue;
STATIC UINT32 g_queue;
#define BUFFER_LEN 50
VOID SendEntry(VOID)
...
...
@@ -154,7 +153,7 @@ VOID SendEntry(VOID)
UINT32 len = sizeof(abuf);
ret = LOS_QueueWriteCopy(g_queue, abuf, len, 0);
if(ret != LOS_OK) {
if
(ret != LOS_OK) {
printf("send message failure, error: %x\n", ret);
}
}
...
...
@@ -165,17 +164,17 @@ VOID RecvEntry(VOID)
CHAR readBuf[BUFFER_LEN] = {0};
UINT32 readLen = BUFFER_LEN;
/
/休眠1s
/
* 休眠1s */
usleep(1000000);
ret = LOS_QueueReadCopy(g_queue, readBuf, &readLen, 0);
if(ret != LOS_OK) {
if
(ret != LOS_OK) {
printf("recv message failure, error: %x\n", ret);
}
printf("recv message: %s\n", readBuf);
printf("recv message: %s
.
\n", readBuf);
ret = LOS_QueueDelete(g_queue);
if(ret != LOS_OK) {
if
(ret != LOS_OK) {
printf("delete the queue failure, error: %x\n", ret);
}
...
...
@@ -186,25 +185,28 @@ UINT32 ExampleQueue(VOID)
{
printf("start queue example.\n");
UINT32 ret = 0;
UINT32 task1, task2;
TSK_INIT_PARAM_S initParam = {0};
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)SendEntry;
initParam.usTaskPrio = 9;
initParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
initParam.pcName = "SendQueue";
UINT32 task1;
UINT32 task2;
TSK_INIT_PARAM_S taskParam1 = { 0 };
TSK_INIT_PARAM_S taskParam2 = { 0 };
LOS_TaskLock();
ret = LOS_TaskCreate(&task1, &initParam);
taskParam1.pfnTaskEntry = (TSK_ENTRY_FUNC)SendEntry;
taskParam1.usTaskPrio = 9;
taskParam1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
taskParam1.pcName = "SendQueue";
ret = LOS_TaskCreate(&task1, &taskParam1);
if(ret != LOS_OK) {
printf("create task1 failed, error: %x\n", ret);
return ret;
}
initParam.pcName = "RecvQueue";
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)RecvEntry;
initParam.usTaskPrio = 10;
ret = LOS_TaskCreate(&task2, &initParam);
taskParam2.pfnTaskEntry = (TSK_ENTRY_FUNC)RecvEntry;
taskParam2.usTaskPrio = 10;
taskParam2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
taskParam2.pcName = "RecvQueue";
ret = LOS_TaskCreate(&task2, &taskParam2);
if(ret != LOS_OK) {
printf("create task2 failed, error: %x\n", ret);
return ret;
...
...
@@ -227,7 +229,7 @@ UINT32 ExampleQueue(VOID)
编译运行得到的结果为:
```
start queue example.
create the queue success.
...
...
zh-cn/device-dev/kernel/kernel-mini-basic-ipc-sem.md
浏览文件 @
ad9b265f
...
...
@@ -11,7 +11,7 @@
-
正值,表示该信号量当前可被获取。
以同步为目的的信号量和以互斥为目的的信号量在使用上有如下不同:
信号量可用于同步或者互斥。
以同步为目的的信号量和以互斥为目的的信号量在使用上有如下不同:
-
用作互斥时,初始信号量计数值不为0,表示可用的共享资源个数。在需要使用共享资源前,先获取信号量,然后使用一个共享资源,使用完毕后释放信号量。这样在共享资源被取完,即信号量计数减至0时,其他需要获取信号量的任务将被阻塞,从而保证了共享资源的互斥访问。另外,当共享资源数为1时,建议使用二值信号量,一种类似于互斥锁的机制。
...
...
@@ -23,7 +23,7 @@
### 信号量控制块
```
/**
* 信号量控制块数据结构
...
...
@@ -40,7 +40,7 @@ typedef struct {
### 信号量运作原理
信号量初始化,为配置的N个信号量申请内存(N值可以由用户自行配置,通过LOSCFG_BASE_IPC_SEM_LIMIT宏实现),并把所有信号量初始化成未使用,加入到未使用链表中供系统使用。
信号量初始化,为配置的N个信号量申请内存(N值可以由用户自行配置,通过LOSCFG_BASE_IPC_SEM_LIMIT宏实现
,按产品实际需要设定
),并把所有信号量初始化成未使用,加入到未使用链表中供系统使用。
信号量创建,从未使用的信号量链表中获取一个信号量,并设定初值。
...
...
@@ -58,10 +58,10 @@ typedef struct {
## 接口说明
| 功能分类 | 接口描述 |
| 功能分类 | 接口描述 |
| -------- | -------- |
| 创建/删除信号量 |
-
LOS_SemCreate:创建信号量,返回信号量ID
<br/>
-
LOS_BinarySemCreate:创建二值信号量,其计数值最大为1
<br/>
-
LOS_SemDelete:删除指定的信号量 |
| 申请/释放信号量 |
-
LOS_SemPend:申请指定的信号量,并设置超时时间
<br/>
-
LOS_SemPost:释放指定的信号量 |
| 创建/删除信号量 |
LOS_SemCreate:创建信号量,返回信号量ID。
<br/>
LOS_BinarySemCreate:创建二值信号量,其计数值最大为1。
<br/>
LOS_SemDelete:删除指定的信号量。 |
| 申请/释放信号量 |
LOS_SemPend:申请指定的信号量,并设置超时时间。
<br/>
LOS_SemPost:释放指定的信号量。 |
## 开发流程
...
...
@@ -101,17 +101,11 @@ typedef struct {
示例代码如下:
```
#include "los_sem.h"
#include "securec.h"
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleSem。
/* 任务ID */
static UINT32 g_testTaskId01;
static UINT32 g_testTaskId02;
/* 测试任务优先级 */
#
define TASK_PRIO_TEST 5
```
#
include "los_sem.h"
/* 信号量结构体id */
static UINT32 g_semId;
...
...
@@ -121,19 +115,17 @@ VOID ExampleSemTask1(VOID)
UINT32 ret;
printf("ExampleSemTask1 try get sem g_semId, timeout 10 ticks.\n");
/* 定时阻塞模式申请信号量,定时时间为10ticks */
ret = LOS_SemPend(g_semId, 10);
/* 申请到信号量 */
if (ret == LOS_OK) {
LOS_SemPost(g_semId);
return;
}
/* 定时时间到,未申请到信号量 */
if (ret == LOS_ERRNO_SEM_TIMEOUT) {
printf("ExampleSemTask1 timeout and try get sem g_semId wait forever.\n");
/*永久阻塞模式申请信号量*/
ret = LOS_SemPend(g_semId, LOS_WAIT_FOREVER);
printf("ExampleSemTask1 wait_forever and get sem g_semId.\n");
...
...
@@ -151,15 +143,14 @@ VOID ExampleSemTask2(VOID)
/* 永久阻塞模式申请信号量 */
ret = LOS_SemPend(g_semId, LOS_WAIT_FOREVER);
if (ret == LOS_OK) {
printf("ExampleSemTask2 get sem g_semId and then delay 20 ticks.\n");
}
/* 任务休眠20 ticks */
LOS_TaskDelay(20);
printf("ExampleSemTask2 post sem g_semId.\n");
/* 释放信号量 */
LOS_SemPost(g_semId);
return;
...
...
@@ -168,8 +159,10 @@ VOID ExampleSemTask2(VOID)
UINT32 ExampleSem(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S task1;
TSK_INIT_PARAM_S task2;
TSK_INIT_PARAM_S task1 = { 0 };
TSK_INIT_PARAM_S task2 = { 0 };
UINT32 taskId1;
UINT32 taskId2;
/* 创建信号量 */
LOS_SemCreate(0, &g_semId);
...
...
@@ -178,24 +171,22 @@ UINT32 ExampleSem(VOID)
LOS_TaskLock();
/* 创建任务1 */
(VOID)memset_s(&task1, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
task1.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleSemTask1;
task1.pcName = "TestTask1";
task1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task1.usTaskPrio =
TASK_PRIO_TEST
;
ret = LOS_TaskCreate(&
g_testTaskId0
1, &task1);
task1.usTaskPrio =
5
;
ret = LOS_TaskCreate(&
taskId
1, &task1);
if (ret != LOS_OK) {
printf("task1 create failed.\n");
return LOS_NOK;
}
/* 创建任务2 */
(VOID)memset_s(&task2, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
task2.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleSemTask2;
task2.pcName = "TestTask2";
task2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task2.usTaskPrio =
(TASK_PRIO_TEST - 1)
;
ret = LOS_TaskCreate(&
g_testTaskId0
2, &task2);
task2.usTaskPrio =
4
;
ret = LOS_TaskCreate(&
taskId
2, &task2);
if (ret != LOS_OK) {
printf("task2 create failed.\n");
return LOS_NOK;
...
...
@@ -221,12 +212,11 @@ UINT32 ExampleSem(VOID)
编译运行得到的结果为:
```
ExampleSemTask2 try get sem g_semId wait forever.
ExampleSemTask2 get sem g_semId and then delay 20 ticks.
ExampleSemTask1 try get sem g_semId, timeout 10 ticks.
ExampleSemTask2 get sem g_semId and then delay 20 ticks.
ExampleSemTask1 timeout and try get sem g_semId wait forever.
ExampleSemTask2 post sem g_semId.
ExampleSemTask1 wait_forever and get sem g_semId.
...
...
zh-cn/device-dev/kernel/kernel-mini-
appx-data
-list.md
→
zh-cn/device-dev/kernel/kernel-mini-
basic
-list.md
浏览文件 @
ad9b265f
...
...
@@ -12,16 +12,16 @@
双向链表模块为用户提供下面几种功能,接口详细信息可以查看API参考。
| | |
| | |
| -------- | -------- |
|
**功能分类**
|
**接口描述**
|
| 初始化
链表 | -
LOS_ListInit:将指定双向链表节点初始化为双向链表
<br/>
-
LOS_DL_LIST_HEAD:定义一个双向链表节点并以该节点初始化为双向链表 |
| 增加节点 |
-
LOS_ListAdd:将指定节点插入到双向链表头端
<br/>
-
LOS_ListTailInsert:将指定节点插入到双向链表尾端 |
| 删除节点 |
-
LOS_ListDelete:将指定节点从链表中删除
<br/>
-
LOS_ListDelInit:将指定节点从链表中删除,并使用该节点初始化链表 |
| 判断双向链表是否为空 | LOS_ListEmpty:判断链表是否为空
|
| 获取结构体信息 |
-
LOS_DL_LIST_ENTRY:获取包含链表的结构体地址,接口的第一个入参表示的是链表中的某个节点,第二个入参是要获取的结构体名称,第三个入参是链表在该结构体中的名称
<br/>
-
LOS_OFF_SET_OF:获取指定结构体内的成员相对于结构体起始地址的偏移量 |
| 遍历双向链表 |
-
LOS_DL_LIST_FOR_EACH:遍历双向链表
<br/>
-
LOS_DL_LIST_FOR_EACH_SAFE:遍历双向链表,并存储当前节点的后继节点用于安全校验 |
| 遍历包含双向链表的结构体 |
-
LOS_DL_LIST_FOR_EACH_ENTRY:遍历指定双向链表,获取包含该链表节点的结构体地址
<br/>
-
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE:遍历指定双向链表,获取包含该链表节点的结构体地址,并存储包含当前节点的后继节点的结构体地址 |
|
**功能分类**
|
**接口描述**
|
| 初始化
和删除链表 |
LOS_ListInit:将指定双向链表节点初始化为双向链表。
<br/>
LOS_DL_LIST_HEAD:定义一个双向链表节点并以该节点初始化为双向链表。
<br/>
LOS_ListDelInit:删除指定的双向链表。 |
| 增加节点 |
LOS_ListAdd:将指定节点插入到双向链表头端。
<br/>
LOS_ListTailInsert:将指定节点插入到双向链表尾端。 |
| 删除节点 |
LOS_ListDelete:将指定节点从链表中删除。
<br/>
LOS_ListDelInit:将指定节点从链表中删除,并使用该节点初始化链表。 |
| 判断双向链表是否为空 | LOS_ListEmpty:判断链表是否为空
。 |
| 获取结构体信息 |
LOS_DL_LIST_ENTRY:获取包含链表的结构体地址,接口的第一个入参表示的是链表中的某个节点,第二个入参是要获取的结构体名称,第三个入参是链表在该结构体中的名称。
<br/>
LOS_OFF_SET_OF:获取指定结构体内的成员相对于结构体起始地址的偏移量。 |
| 遍历双向链表 |
LOS_DL_LIST_FOR_EACH:遍历双向链表。
<br/>
LOS_DL_LIST_FOR_EACH_SAFE:遍历双向链表,并存储当前节点的后继节点用于安全校验。 |
| 遍历包含双向链表的结构体 |
LOS_DL_LIST_FOR_EACH_ENTRY:遍历指定双向链表,获取包含该链表节点的结构体地址。
<br/>
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE:遍历指定双向链表,获取包含该链表节点的结构体地址,并存储包含当前节点的后继节点的结构体地址。 |
## 开发流程
...
...
@@ -69,22 +69,24 @@
示例代码如下:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleList。
```
#include "stdio.h"
#include "los_list.h"
static UINT32 ListSample
(VOID)
STATIC UINT32 ExampleList
(VOID)
{
LOS_DL_LIST listHead = {NULL,NULL};
LOS_DL_LIST listNode1 = {NULL,NULL};
LOS_DL_LIST listNode2 = {NULL,NULL};
/
/首先初始化链表
/
* 初始化链表 */
printf("Initial head\n");
LOS_ListInit(&listHead);
/
/添加节点1和节点2,并校验他们的相互关系
/
* 添加节点1和节点2,并校验他们的相互关系 */
LOS_ListAdd(&listHead, &listNode1);
if (listNode1.pstNext == &listHead && listNode1.pstPrev == &listHead) {
printf("Add listNode1 success\n");
...
...
@@ -95,11 +97,11 @@ static UINT32 ListSample(VOID)
printf("Tail insert listNode2 success\n");
}
/
/删除两个节点
/
* 删除两个节点 */
LOS_ListDelete(&listNode1);
LOS_ListDelete(&listNode2);
/
/确认链表为空
/
* 确认链表为空 */
if (LOS_ListEmpty(&listHead)) {
printf("Delete success\n");
}
...
...
@@ -113,7 +115,7 @@ static UINT32 ListSample(VOID)
编译运行得到的结果为:
```
Initial head
Add listNode1 success
...
...
zh-cn/device-dev/kernel/kernel-mini-basic-memory.md
浏览文件 @
ad9b265f
...
...
@@ -45,12 +45,12 @@ OpenHarmony LiteOS-M的静态内存管理主要为用户提供以下功能,接
**表1**
静态内存模块接口
| 功能分类 | 接口名 |
| 功能分类 | 接口名 |
| -------- | -------- |
| 初始化静态内存池 | LOS_MemboxInit:初始化一个静态内存池,根据入参设定其起始地址、总大小及每个内存块大小。 |
| 清除静态内存块内容 | LOS_MemboxClr
:清零从静态内存池中申请的静态内存块的内容。 |
| 申请、释放静态内存 |
-
LOS_MemboxAlloc:从指定的静态内存池中申请一块静态内存块。
<br/>
-
LOS_MemboxFree:释放从静态内存池中申请的一块静态内存块。 |
| 获取、打印静态内存池信息 |
-
LOS_MemboxStatisticsGet:获取指定静态内存池的信息,包括内存池中总内存块数量、已经分配出去的内存块数量、每个内存块的大小。
<br/>
-
LOS_ShowBox:打印指定静态内存池所有节点信息(打印等级是LOS_INFO_LEVEL),包括内存池起始地址、内存块大小、总内存块数量、每个空闲内存块的起始地址、所有内存块的起始地址。 |
| 初始化静态内存池 | LOS_MemboxInit:初始化一个静态内存池,根据入参设定其起始地址、总大小及每个内存块大小。 |
| 清除静态内存块内容 | LOS_MemboxClr
:清零从静态内存池中申请的静态内存块的内容。 |
| 申请、释放静态内存 |
LOS_MemboxAlloc:从指定的静态内存池中申请一块静态内存块。
<br/>
LOS_MemboxFree:释放从静态内存池中申请的一块静态内存块。 |
| 获取、打印静态内存池信息 |
LOS_MemboxStatisticsGet:获取指定静态内存池的信息,包括内存池中总内存块数量、已经分配出去的内存块数量、每个内存块的大小。
<br/>
LOS_ShowBox:打印指定静态内存池所有节点信息,打印等级是LOG_INFO_LEVEL(当前打印等级配置是PRINT_LEVEL),包括内存池起始地址、内存块大小、总内存块数量、每个空闲内存块的起始地址、所有内存块的起始地址。 |
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 初始化后的内存池的内存块数量,不等于总大小除于内存块大小,因为内存池的控制块和每个内存块的控制头,都存在内存开销,设置总大小时,需要将这些因素考虑进去。
...
...
@@ -91,21 +91,26 @@ OpenHarmony LiteOS-M的静态内存管理主要为用户提供以下功能,接
6.
释放该内存块。
示例代码如下:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleStaticMem。
```
#include "los_membox.h"
VOID Example_StaticMem(VOID)
#define MEMBOX_POOL_SIZE 100
#define MEMBOX_BLOCK_SZIE 10
#define MEMBOX_WR_TEST_NUM 828
VOID ExampleStaticMem(VOID)
{
UINT32 *mem = NULL;
UINT32 blkSize =
10
;
UINT32
boxSize = 100
;
UINT32 boxMem[
1000
];
UINT32 blkSize =
MEMBOX_BLOCK_SZIE
;
UINT32
poolSize = MEMBOX_POOL_SIZE
;
UINT32 boxMem[
MEMBOX_POOL_SIZE
];
UINT32 ret;
/*
内存池初始化
*/
ret = LOS_MemboxInit(&boxMem[0],
box
Size, blkSize);
/*
内存池初始化
*/
ret = LOS_MemboxInit(&boxMem[0],
pool
Size, blkSize);
if(ret != LOS_OK) {
printf("Membox init failed!\n");
return;
...
...
@@ -113,23 +118,23 @@ VOID Example_StaticMem(VOID)
printf("Membox init success!\n");
}
/*
申请内存块
*/
/*
申请内存块
*/
mem = (UINT32 *)LOS_MemboxAlloc(boxMem);
if (
NULL == mem
) {
if (
mem == NULL
) {
printf("Mem alloc failed!\n");
return;
}
printf("Mem alloc success!\n");
/*
赋值
*/
*mem =
828
;
/*
内存地址读写验证
*/
*mem =
MEMBOX_WR_TEST_NUM
;
printf("*mem = %d\n", *mem);
/*
清除内存内容
*/
/*
清除内存内容
*/
LOS_MemboxClr(boxMem, mem);
printf("Mem clear success \n
*mem = %d\n", *mem);
printf("Mem clear success \n*mem = %d\n", *mem);
/*
释放内存
*/
/*
释放内存
*/
ret = LOS_MemboxFree(boxMem, mem);
if (LOS_OK == ret) {
printf("Mem free success!\n");
...
...
@@ -139,6 +144,7 @@ VOID Example_StaticMem(VOID)
return;
}
```
...
...
@@ -146,7 +152,7 @@ VOID Example_StaticMem(VOID)
输出结果如下:
```
Membox init success!
Mem alloc success!
...
...
@@ -197,7 +203,7 @@ OpenHarmony LiteOS-M动态内存在TLSF算法的基础上,对区间的划分
2.
获取下一个内存区域的开始地址和长度,计算该内存区域和上一块内存区域的间隔大小gapSize。
3.
把内存区域间隔部分视为虚拟的已使用节点,使用上一个内存区域的尾节点,设置其大小为gapSize
+ OS_MEM_NODE_HEAD_SIZE
。
3.
把内存区域间隔部分视为虚拟的已使用节点,使用上一个内存区域的尾节点,设置其大小为gapSize
+ OS_MEM_NODE_HEAD_SIZE(即sizeof(struct OsMemUsedNodeHead))
。
4.
把当前内存区域划分为一个空闲内存节点和一个尾节点,把空闲内存节点插入到空闲链表,并设置各个节点的前后链接关系。
...
...
@@ -218,14 +224,14 @@ OpenHarmony LiteOS-M的动态内存管理主要为用户提供以下功能,接
**表1**
动态内存模块接口
| 功能分类 | 接口描述 |
| 功能分类 | 接口描述 |
| -------- | -------- |
| 初始化和删除内存池 |
-
LOS_MemInit:初始化一块指定的动态内存池,大小为size。
<br/>
-
LOS_MemDeInit:删除指定内存池,仅打开LOSCFG_MEM_MUL_POOL时有效。 |
| 申请、释放动态内存 |
-
LOS_MemAlloc:从指定动态内存池中申请size长度的内存。
<br/>
-
LOS_MemFree:释放从指定动态内存中申请的内存。
<br/>
-
LOS_MemRealloc:释放从指定动态内存中申请的内存。 |
| 获取内存池信息 |
-
LOS_MemPoolSizeGet:获取指定动态内存池的总大小。
<br/>
-
LOS_MemTotalUsedGet:获取指定动态内存池的总使用量大小。
<br/>
-
LOS_MemInfoGet:获取指定内存池的内存结构信息,包括空闲内存大小、已使用内存大小、空闲内存块数量、已使用的内存块数量、最大的空闲内存块大小。
<br/>
-
LOS_MemPoolList:打印系统中已初始化的所有内存池,包括内存池的起始地址、内存池大小、空闲内存总大小、已使用内存总大小、最大的空闲内存块大小、空闲内存块数量、已使用的内存块数量。仅打开LOSCFG_MEM_MUL_POOL时有效。 |
| 获取内存块信息 |
-
LOS_MemFreeNodeShow:打印指定内存池的空闲内存块的大小及数量。
<br/>
-
LOS_MemUsedNodeShow:打印指定内存池的已使用内存块的大小及数量。 |
| 检查指定内存池的完整性 | LOS_MemIntegrityCheck
:对指定内存池做完整性检查,仅打开LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK时有效。 |
| 增加非连续性内存区域 | LOS_MemRegionsAdd
:支持多段非连续性内存区域,把非连续性内存区域逻辑上整合为一个统一的内存池。仅打开LOSCFG_MEM_MUL_REGIONS时有效。如果内存池指针参数pool为空,则使用多段内存的第一个初始化为内存池,其他内存区域,作为空闲节点插入;如果内存池指针参数pool不为空,则把多段内存作为空闲节点,插入到指定的内存池。 |
| 初始化和删除内存池 |
LOS_MemInit:初始化一块指定的动态内存池,大小为size。
<br/>
LOS_MemDeInit:删除指定内存池,仅打开编译控制开关LOSCFG_MEM_MUL_POOL时有效。 |
| 申请、释放动态内存 |
LOS_MemAlloc:从指定动态内存池中申请size长度的内存。
<br/>
LOS_MemFree:释放从指定动态内存中申请的内存。
<br/>
LOS_MemRealloc:释放从指定动态内存中申请的内存。 |
| 获取内存池信息 |
LOS_MemPoolSizeGet:获取指定动态内存池的总大小。
<br/>
LOS_MemTotalUsedGet:获取指定动态内存池的总使用量大小。
<br/>
LOS_MemInfoGet:获取指定内存池的内存结构信息,包括空闲内存大小、已使用内存大小、空闲内存块数量、已使用的内存块数量、最大的空闲内存块大小。
<br/>
LOS_MemPoolList:打印系统中已初始化的所有内存池,包括内存池的起始地址、内存池大小、空闲内存总大小、已使用内存总大小、最大的空闲内存块大小、空闲内存块数量、已使用的内存块数量。仅打开编译控制开关LOSCFG_MEM_MUL_POOL时有效。 |
| 获取内存块信息 |
LOS_MemFreeNodeShow:打印指定内存池的空闲内存块的大小及数量。
<br/>
LOS_MemUsedNodeShow:打印指定内存池的已使用内存块的大小及数量。 |
| 检查指定内存池的完整性 | LOS_MemIntegrityCheck
:对指定内存池做完整性检查,仅打开编译控制开关LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK时有效。 |
| 增加非连续性内存区域 | LOS_MemRegionsAdd
:支持多段非连续性内存区域,把非连续性内存区域逻辑上整合为一个统一的内存池。仅打开LOSCFG_MEM_MUL_REGIONS时有效。如果内存池指针参数pool为空,则使用多段内存的第一个初始化为内存池,其他内存区域,作为空闲节点插入;如果内存池指针参数pool不为空,则把多段内存作为空闲节点,插入到指定的内存池。 |
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> - 由于动态内存管理需要管理控制块数据结构来管理内存,这些数据结构会额外消耗内存,故实际用户可使用内存总量小于配置项OS_SYS_MEM_SIZE的大小。
...
...
@@ -265,18 +271,24 @@ OpenHarmony LiteOS-M的动态内存管理主要为用户提供以下功能,接
示例代码如下:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleDynMem。
```
#include "los_memory.h"
#define TEST_POOL_SIZE (2*1024)
__attribute__((aligned(4))) UINT8 g_testPool[TEST_POOL_SIZE];
VOID Example_DynMem(VOID)
#define MEMBOX_WR_TEST_NUM 828
__attribute__((aligned(4))) UINT8 g_testDynPool[TEST_POOL_SIZE];
VOID ExampleDynMem(VOID)
{
UINT32 *mem = NULL;
UINT32 ret;
/*
初始化内存池
*/
ret = LOS_MemInit(g_testPool, TEST_POOL_SIZE);
/*
初始化内存池
*/
ret = LOS_MemInit(g_test
Dyn
Pool, TEST_POOL_SIZE);
if (LOS_OK == ret) {
printf("Mem init success!\n");
} else {
...
...
@@ -284,20 +296,20 @@ VOID Example_DynMem(VOID)
return;
}
/*
分配内存
*/
mem = (UINT32 *)LOS_MemAlloc(g_testPool, 4);
if (
NULL == mem
) {
/*
申请内存块
*/
mem = (UINT32 *)LOS_MemAlloc(g_test
Dyn
Pool, 4);
if (
mem == NULL
) {
printf("Mem alloc failed!\n");
return;
}
printf("Mem alloc success!\n");
/*
赋值
*/
*mem =
828
;
/*
内存地址读写验证
*/
*mem =
MEMBOX_WR_TEST_NUM
;
printf("*mem = %d\n", *mem);
/*
释放内存
*/
ret = LOS_MemFree(g_testPool, mem);
/*
释放内存
*/
ret = LOS_MemFree(g_test
Dyn
Pool, mem);
if (LOS_OK == ret) {
printf("Mem free success!\n");
} else {
...
...
@@ -313,7 +325,7 @@ VOID Example_DynMem(VOID)
输出结果如下:
```
Mem init success!
Mem alloc success!
...
...
zh-cn/device-dev/kernel/kernel-mini-basic-soft.md
浏览文件 @
ad9b265f
...
...
@@ -24,7 +24,7 @@
## 运行机制
软件定时器是系统资源,在模块初始化的时候已经分配了一块连续的内存,系统支持的最大定时器个数由los_config.h中的LOSCFG_BASE_CORE_SWTMR_LIMIT宏配置。
软件定时器是系统资源,在模块初始化的时候已经分配了一块连续的内存,系统支持的最大定时器个数由los_config.h中的LOSCFG_BASE_CORE_SWTMR_LIMIT宏配置
,该值按产品实际需要设定
。
软件定时器使用了系统的一个队列和一个任务资源,软件定时器的触发遵循队列规则,先进先出。定时时间短的定时器总是比定时时间长的靠近队列头,满足优先被触发的准则。
...
...
@@ -64,11 +64,11 @@ OpenHarmony LiteOS-M内核的软件定时器模块提供下面几种功能,接
**表1**
软件定时器接口
| 功能分类 | 接口描述 |
| 功能分类 | 接口描述 |
| -------- | -------- |
| 创建、删除定时器 | -
LOS_SwtmrCreate:创建定时器
<br/>
-
LOS_SwtmrDelete:删除定时器 |
| 启动、停止定时器 | -
LOS_SwtmrStart:启动定时器
<br/>
-
LOS_SwtmrStop:停止定时器 |
| 获得软件定时器剩余Tick数 | LOS_SwtmrTimeGet:获得软件定时器剩余Tick数
|
| 创建、删除定时器 | -
LOS_SwtmrCreate:创建定时器
。
<br/>
-
LOS_SwtmrDelete:删除定时器。 |
| 启动、停止定时器 | -
LOS_SwtmrStart:启动定时器
。
<br/>
-
LOS_SwtmrStop:停止定时器。 |
| 获得软件定时器剩余Tick数 | LOS_SwtmrTimeGet:获得软件定时器剩余Tick数
。 |
## 开发流程
...
...
@@ -130,86 +130,107 @@ OpenHarmony LiteOS-M内核的软件定时器模块提供下面几种功能,接
代码实现如下:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleSwtmr。
```
#include "los_swtmr.h"
/*
Timer count
*/
UINT32 g_timerCount1 = 0;
UINT32 g_timerCount2 = 0;
/*
定时器间隔时间
*/
#define SWTMR_INTERVAL_LONG 1000
#define SWTMR_INTERVAL_SHORT 100
/* 任务ID */
UINT32 g_testTaskId01;
/* 定时器触发次数计数 */
UINT32 g_timerCount1 = 0;
UINT32 g_timerCount2 = 0;
void Timer1_Callback(UINT32 arg) // 回调函数1
/* 回调函数1,单次触发定时器的回调函数 */
void Timer1Callback(UINT32 arg)
{
UINT32 tick_last1;
g_timerCount1++;
tick_last1 = (UINT32)LOS_TickCountGet(); // 获取当前Tick数
printf("g_timerCount1=%d, tick_last1=%d\n", g_timerCount1, tick_last1);
}
printf("g_timerCount1=%d\n", g_timerCount1);
}
void Timer2_Callback(UINT32 arg) // 回调函数2
/* 回调函数2,多次触发定时器的回调函数 */
void Timer2Callback(UINT32 arg)
{
UINT32 tick_last2;
tick_last2 = (UINT32)LOS_TickCountGet();
g_timerCount2++;
printf("g_timerCount2=%d
tick_last2=%d\n", g_timerCount2, tick_las
t2);
}
printf("g_timerCount2=%d
\n", g_timerCoun
t2);
}
void
Timer_example(void)
void
SwtmrTest(void)
{
UINT32 ret;
UINT32 id1; //
timer id1
UINT32 id2; //
timer id2
UINT32 id1; //
定时器id1,单次触发定时器
UINT32 id2; //
定时器id2,周期触发定时器
UINT32 tickCount;
/*创建单次软件定时器,Tick数为1000,启动到1000Tick数时执行回调函数1 */
LOS_SwtmrCreate(1000, LOS_SWTMR_MODE_ONCE, Timer1_Callback, &id1, 1);
#if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1)
/* 创建单次软件定时器,Tick数为1000,启动到1000Tick数时执行回调函数1 */
LOS_SwtmrCreate(SWTMR_INTERVAL_LONG, LOS_SWTMR_MODE_ONCE, Timer1Callback, &id1, 0,
OS_SWTMR_ROUSES_IGNORE, OS_SWTMR_ALIGN_SENSITIVE);
/*创建周期性软件定时器,每100Tick数执行回调函数2 */
LOS_SwtmrCreate(100, LOS_SWTMR_MODE_PERIOD, Timer2_Callback, &id2, 1);
printf("create Timer1 success\n");
/* 创建周期性软件定时器,每100Tick数执行回调函数2 */
LOS_SwtmrCreate(SWTMR_INTERVAL_SHORT, LOS_SWTMR_MODE_PERIOD, Timer2Callback, &id2, 0,
OS_SWTMR_ROUSES_IGNORE, OS_SWTMR_ALIGN_SENSITIVE);
#else
/* 创建单次软件定时器,Tick数为1000,启动到1000Tick数时执行回调函数1 */
LOS_SwtmrCreate(SWTMR_INTERVAL_LONG, LOS_SWTMR_MODE_ONCE, Timer1Callback, &id1, 0);
LOS_SwtmrStart(id1); //启动单次软件定时器
printf("start Timer1 success\n");
/* 创建周期性软件定时器,每100Tick数执行回调函数2 */
LOS_SwtmrCreate(SWTMR_INTERVAL_SHORT, LOS_SWTMR_MODE_PERIOD, Timer2Callback, &id2, 0);
#endif
LOS_TaskDelay(200); //延时200Tick数
LOS_SwtmrTimeGet(id1, &tickCount); // 获得单次软件定时器剩余Tick数
printf("
tickCount=%d\n", tickCount
);
/* 启动单次软件定时器 */
ret = LOS_SwtmrStart(id1);
printf("
start Timer1 %s\n", (ret == LOS_OK) ? "success" : "failed"
);
LOS_SwtmrStop(id1); // 停止软件定时器
printf("stop Timer1 success\n");
/* 短时间延时,定时器还未触发 */
LOS_TaskDelay(SWTMR_INTERVAL_SHORT);
/* 单次定时器还未到时间触发,此时停止应该成功 */
ret = LOS_SwtmrStop(id1);
printf("stop timer1 %s\n", (ret == LOS_OK) ? "success" : "failed");
LOS_SwtmrStart(id1);
LOS_TaskDelay(1000);
/* 长时间延时,定时器触发 */
LOS_TaskDelay(SWTMR_INTERVAL_LONG);
/* 单次定时器触发后自删除,此时停止失败才是正常 */
ret = LOS_SwtmrStop(id1);
printf("timer1 self delete test %s\n", (ret != LOS_OK) ? "success" : "failed");
/* 启动周期性软件定时器 */
ret = LOS_SwtmrStart(id2);
printf("start Timer2 %s\n", (ret == LOS_OK) ? "success" : "failed");
LOS_SwtmrStart(id2); // 启动周期性软件定时器
printf("start Timer2\n"
);
/* 长时间延时,定时器周期触发 */
LOS_TaskDelay(SWTMR_INTERVAL_LONG
);
LOS_TaskDelay(1000);
LOS_SwtmrStop(id2);
ret = LOS_SwtmrDelete(id2); // 删除软件定时器
ret = LOS_SwtmrDelete(id2);
if (ret == LOS_OK) {
printf("delete Timer2 success\n");
}
}
UINT32 Example
_TaskEntry
(VOID)
UINT32 Example
Swtmr
(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S task1;
TSK_INIT_PARAM_S taskParam = { 0 };
UINT32 taskId;
/* 锁任务调度 */
LOS_TaskLock();
/* 创建任务1 */
(VOID)memset(&task1, 0, sizeof(TSK_INIT_PARAM_S));
task1.pfnTaskEntry = (TSK_ENTRY_FUNC)Timer_example;
task1.pcName = "TimerTsk";
task1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task1.usTaskPrio = 5;
ret = LOS_TaskCreate(&g_testTaskId01, &task1);
/* 创建任务 */
taskParam.pfnTaskEntry = (TSK_ENTRY_FUNC)SwtmrTest;
taskParam.pcName = "TimerTsk";
taskParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
taskParam.usTaskPrio = 5;
ret = LOS_TaskCreate(&taskId, &taskParam);
if (ret != LOS_OK) {
printf("TimerTsk create failed.\n");
return LOS_NOK;
...
...
@@ -217,7 +238,6 @@ UINT32 Example_TaskEntry(VOID)
/* 解锁任务调度 */
LOS_TaskUnlock();
return LOS_OK;
}
```
...
...
@@ -227,24 +247,22 @@ UINT32 Example_TaskEntry(VOID)
编译烧录运行,输出结果如下:
```
create Timer1 success
start Timer1 success
tickCount=798
stop Timer1 success
g_timerCount1=1, tick_last1=1208
delete Timer1 success
start Timer2
g_timerCount2=1 tick_last2=1313
g_timerCount2=2 tick_last2=1413
g_timerCount2=3 tick_last2=1513
g_timerCount2=4 tick_last2=1613
g_timerCount2=5 tick_last2=1713
g_timerCount2=6 tick_last2=1813
g_timerCount2=7 tick_last2=1913
g_timerCount2=8 tick_last2=2013
g_timerCount2=9 tick_last2=2113
g_timerCount2=10 tick_last2=2213
stop timer1 success
g_timerCount1=1
timer1 self delete test success
start Timer2 success
g_timerCount2=1
g_timerCount2=2
g_timerCount2=3
g_timerCount2=4
g_timerCount2=5
g_timerCount2=6
g_timerCount2=7
g_timerCount2=8
g_timerCount2=9
g_timerCount2=10
delete Timer2 success
```
zh-cn/device-dev/kernel/kernel-mini-basic-task.md
浏览文件 @
ad9b265f
...
...
@@ -3,7 +3,7 @@
## 基本概念
从系统角度看,任务是竞争系统资源的最小运行单元。任务可以使用或等待CPU、使用内存空间等系统资源,
并独立于其它任务运行
。
从系统角度看,任务是竞争系统资源的最小运行单元。任务可以使用或等待CPU、使用内存空间等系统资源,
各任务的运行相互独立
。
OpenHarmony LiteOS-M的任务模块可以给用户提供多个任务,实现任务间的切换,帮助用户管理业务程序流程。任务模块具有如下特性:
...
...
@@ -40,7 +40,9 @@ OpenHarmony LiteOS-M的任务模块可以给用户提供多个任务,实现任
!
[
zh-cn_image_0000001200612002
](
figures/zh-cn_image_0000001200612002.png
)
**任务状态迁移说明:**
系统会同时存在多个任务,因此就绪态和阻塞态的任务分别会加入就绪队列和阻塞队列。队列只是相同状态任务的合集,加入队列的先后与任务状态迁移的顺序无关。运行态任务仅存在一个,不存在运行态队列。
**任务状态迁移说明**
-
就绪态→运行态
任务创建后进入就绪态,发生任务切换时,就绪队列中最高优先级的任务被执行,从而进入运行态,同时该任务从就绪队列中移出。
...
...
@@ -48,11 +50,11 @@ OpenHarmony LiteOS-M的任务模块可以给用户提供多个任务,实现任
-
运行态→阻塞态
正在运行的任务发生阻塞(挂起、延时、读信号量等)时,将该任务插入到对应的阻塞队列中,任务状态由运行态变成阻塞态,然后发生任务切换,运行就绪队列中最高优先级任务。
-
阻塞态→就绪态(阻塞态→运行态)
-
阻塞态→就绪态(阻塞态→运行态
的前置条件
)
阻塞的任务被恢复后(任务恢复、延时时间超时、读信号量超时或读到信号量等),此时被恢复的任务会被加入就绪队列,从而由阻塞态变成就绪态;此时如果被恢复任务的优先级高于正在运行任务的优先级,则会发生任务切换,该任务由就绪态变成运行态。
-
就绪态→阻塞态
任务也有可能在就绪态时被阻塞(挂起),此时任务状态由就绪态变为阻塞态,该任务从就绪队列中
删除
,不会参与任务调度,直到该任务被恢复。
任务也有可能在就绪态时被阻塞(挂起),此时任务状态由就绪态变为阻塞态,该任务从就绪队列中
移出
,不会参与任务调度,直到该任务被恢复。
-
运行态→就绪态
有更高优先级任务创建或者恢复后,会发生任务调度,此刻就绪队列中最高优先级任务变为运行态,那么原先运行的任务由运行态变为就绪态,依然在就绪队列中。
...
...
@@ -65,7 +67,7 @@ OpenHarmony LiteOS-M的任务模块可以给用户提供多个任务,实现任
**任务ID**
任务ID,在任务创建时通过参数返回给用户,是任务的重要标识。系统中的ID号是唯一的
。用户可以通过任务ID对指定任务进行任务挂起、任务恢复、查询任务名等操作。
在任务创建时通过参数返回给用户。系统中任务ID号是唯一的,是任务的重要标识
。用户可以通过任务ID对指定任务进行任务挂起、任务恢复、查询任务名等操作。
**任务优先级**
...
...
@@ -83,7 +85,7 @@ OpenHarmony LiteOS-M的任务模块可以给用户提供多个任务,实现任
任务在运行过程中使用的一些资源,如寄存器等,称为任务上下文。当这个任务挂起时,其他任务继续执行,可能会修改寄存器等资源中的值。如果任务切换时没有保存任务上下文,可能会导致任务恢复后出现未知错误。因此在任务切换时会将切出任务的任务上下文信息,保存在自身的任务栈中,以便任务恢复后,从栈空间中恢复挂起时的上下文信息,从而继续执行挂起时被打断的代码。
**任务控制块
TCB
**
**任务控制块
(TCB)
**
每个任务都含有一个任务控制块(TCB)。TCB包含了任务上下文栈指针(stack pointer)、任务状态、任务优先级、任务ID、任务名、任务栈大小等信息。TCB可以反映出每个任务运行情况。
...
...
@@ -103,15 +105,14 @@ OpenHarmony LiteOS-M内核的任务管理模块提供下面几种功能,接口
**表1**
任务管理模块接口
| 功能分类 | 接口描述 |
| 功能分类 | 接口描述 |
| -------- | -------- |
| 创建和删除任务 | LOS_TaskCreateOnly:创建任务,并使该任务进入ready状态,如果就绪队列中没有更高优先级的任务,则运行该任务。
<br/>
LOS_TaskCreate:创建任务,并使该任务进入ready状态,如果就绪队列中没有更高优先级的任务,则运行该任务。
<br/>
LOS_TaskDelete:删除指定的任务。 |
| 控制任务状态 | LOS_TaskResume:恢复挂起的任务,使该任务进入ready状态。
<br/>
LOS_TaskSuspend:挂起指定的任务,然后切换任务。
<br/>
LOS_TaskJoin:挂起当前任务,等待指定任务运行结束并回收其任务控制块资源
<br/>
LOS_TaskDelay:任务延时等待,释放CPU,等待时间到期后该任务会重新进入ready状态。传入参数为Tick数目。
<br/>
LOS_Msleep:传入参数为毫秒数,转换为Tick数目,调用LOS_TaskDelay。
<br/>
LOS_TaskYield:当前任务时间片设置为0,释放CPU,触发调度运行就绪任务队列中优先级最高的任务。 |
| 控制任务调度 | LOS_TaskLock:锁任务调度,但任务仍可被中断打断。
<br/>
LOS_TaskUnlock:解锁任务调度。
<br/>
LOS_Schedule:触发任务调度。 |
| 控制任务优先级 | LOS_CurTaskPriSet:设置当前任务的优先级。
<br/>
LOS_TaskPriSet:设置指定任务的优先级。
<br/>
LOS_TaskPriGet:获取指定任务的优先级。 |
| 获取任务信息 | LOS_CurTaskIDGet:获取当前任务的ID。
<br/>
LOS_NextTaskIDGet:获取任务就绪队列中优先级最高的任务的ID。
<br/>
LOS_NewTaskIDGet:等同LOS_NextTaskIDGet。
<br/>
LOS_CurTaskNameGet:获取当前任务的名称。
<br/>
LOS_TaskNameGet:获取指定任务的名称。
<br/>
LOS_TaskStatusGet:获取指定任务的状态。
<br/>
LOS_TaskInfoGet:获取指定任务的信息,包括任务状态、优先级、任务栈大小、栈顶指针SP、任务入口函数、已使用的任务栈大小等。
<br/>
LOS_TaskIsRunning:获取任务模块是否已经开始调度运行。 |
| 任务信息维测 | LOS_TaskSwitchInfoGet:获取任务切换信息,需要开启宏LOSCFG_BASE_CORE_EXC_TSK_SWITCH。 |
| 创建和删除任务 | LOS_TaskCreateOnly:创建任务,并使该任务进入suspend状态。
<br/>
LOS_TaskCreate:创建任务,并使该任务进入ready状态,如果就绪队列中没有更高优先级的任务,则运行该任务。
<br/>
LOS_TaskDelete:删除指定的任务。 |
| 控制任务状态 | LOS_TaskResume:恢复挂起的任务,使该任务进入ready状态。
<br/>
LOS_TaskSuspend:挂起指定的任务,然后切换任务。
<br/>
LOS_TaskJoin:挂起当前任务,等待指定任务运行结束并回收其任务控制块资源
<br/>
LOS_TaskDelay:任务延时等待,释放CPU,等待时间到期后该任务会重新进入ready状态。传入参数为Tick数目。
<br/>
LOS_Msleep:任务延时等待,释放CPU,等待时间到期后该任务会重新进入ready状态。传入参数为毫秒数。
<br/>
LOS_TaskYield:当前任务时间片设置为0,释放CPU,触发调度运行就绪任务队列中优先级最高的任务。 |
| 控制任务调度 | LOS_TaskLock:锁任务调度,但任务仍可被中断打断。
<br/>
LOS_TaskUnlock:解锁任务调度。
<br/>
LOS_Schedule:触发任务调度。 |
| 控制任务优先级 | LOS_CurTaskPriSet:设置当前任务的优先级。
<br/>
LOS_TaskPriSet:设置指定任务的优先级。
<br/>
LOS_TaskPriGet:获取指定任务的优先级。 |
| 获取任务信息 | LOS_CurTaskIDGet:获取当前任务的ID。
<br/>
LOS_NextTaskIDGet:获取任务就绪队列中优先级最高的任务的ID。
<br/>
LOS_NewTaskIDGet:等同LOS_NextTaskIDGet。
<br/>
LOS_CurTaskNameGet:获取当前任务的名称。
<br/>
LOS_TaskNameGet:获取指定任务的名称。
<br/>
LOS_TaskStatusGet:获取指定任务的状态。
<br/>
LOS_TaskInfoGet:获取指定任务的信息,包括任务状态、优先级、任务栈大小、栈顶指针SP、任务入口函数、已使用的任务栈大小等。
<br/>
LOS_TaskIsRunning:获取任务模块是否已经开始调度运行。 |
| 任务信息维测 | LOS_TaskSwitchInfoGet:获取任务切换信息,需要开启编译控制宏:LOSCFG_BASE_CORE_EXC_TSK_SWITCH。 |
## 开发流程
...
...
@@ -161,20 +162,24 @@ OpenHarmony LiteOS-M内核的任务管理模块提供下面几种功能,接口
本实例介绍基本的任务操作方法,包含2个不同优先级任务的创建、任务延时、任务锁与解锁调度、挂起和恢复等操作,阐述任务优先级调度的机制以及各接口的应用。示例代码如下:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleTask。
```
#include "los_task.h"
UINT32 g_taskHiId;
UINT32 g_taskLoId;
#define TSK_PRIOR_HI
4
#define TSK_PRIOR_LO
5
#define TSK_PRIOR_HI
3 /* 高优先级任务的优先级 */
#define TSK_PRIOR_LO
4 /* 低优先级任务的优先级 */
UINT32 Example
_
TaskHi(VOID)
UINT32 ExampleTaskHi(VOID)
{
UINT32 ret;
printf("Enter TaskHi Handler.\n");
/* 延时100个Ticks,延时后该任务会挂起,执行剩余任务中最高优先级的任务(TaskLo任务) */
/* 延时100个Ticks,延时后该任务会挂起,执行剩余任务中最高优先级的任务(
即
TaskLo任务) */
ret = LOS_TaskDelay(100);
if (ret != LOS_OK) {
printf("Delay TaskHi Failed.\n");
...
...
@@ -195,7 +200,7 @@ UINT32 Example_TaskHi(VOID)
}
/* 低优先级任务入口函数 */
UINT32 Example
_
TaskLo(VOID)
UINT32 ExampleTaskLo(VOID)
{
UINT32 ret;
...
...
@@ -220,24 +225,25 @@ UINT32 Example_TaskLo(VOID)
}
/* 任务测试入口函数,创建两个不同优先级的任务 */
UINT32 Example
_TskCaseEntry
(VOID)
UINT32 Example
Task
(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S initParam;
TSK_INIT_PARAM_S taskParam1 = { 0 };
TSK_INIT_PARAM_S taskParam2 = { 0 };
/* 锁任务调度,防止新创建的任务比本任务高而发生调度 */
LOS_TaskLock();
printf("LOS_TaskLock() Success!\n");
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_
TaskHi;
initParam
.usTaskPrio = TSK_PRIOR_HI;
initParam
.pcName = "TaskHi";
initParam
.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
initParam.uwResved = 0
; /* detach 属性 */
taskParam1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example
TaskHi;
taskParam1
.usTaskPrio = TSK_PRIOR_HI;
taskParam1
.pcName = "TaskHi";
taskParam1
.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
taskParam1.uwResved = LOS_TASK_ATTR_JOINABLE
; /* detach 属性 */
/* 创建高优先级任务,由于锁任务调度,任务创建成功后不会马上执行 */
ret = LOS_TaskCreate(&g_taskHiId, &
initParam
);
ret = LOS_TaskCreate(&g_taskHiId, &
taskParam1
);
if (ret != LOS_OK) {
LOS_TaskUnlock();
...
...
@@ -247,13 +253,13 @@ UINT32 Example_TskCaseEntry(VOID)
printf("Example_TaskHi create Success!\n");
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_
TaskLo;
initParam
.usTaskPrio = TSK_PRIOR_LO;
initParam
.pcName = "TaskLo";
initParam
.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
taskParam2.pfnTaskEntry = (TSK_ENTRY_FUNC)Example
TaskLo;
taskParam2
.usTaskPrio = TSK_PRIOR_LO;
taskParam2
.pcName = "TaskLo";
taskParam2
.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
/* 创建低优先级任务,由于锁任务调度,任务创建成功后不会马上执行 */
ret = LOS_TaskCreate(&g_taskLoId, &
initParam
);
ret = LOS_TaskCreate(&g_taskLoId, &
taskParam2
);
if (ret != LOS_OK) {
LOS_TaskUnlock();
printf("Example_TaskLo create Failed!\n");
...
...
@@ -266,7 +272,7 @@ UINT32 Example_TskCaseEntry(VOID)
LOS_TaskUnlock();
ret = LOS_TaskJoin(g_taskHiId, NULL);
if (ret != LOS_OK) {
printf("Join Example_TaskHi Failed!
\n"
);
printf("Join Example_TaskHi Failed!
, 0x%x\n", ret
);
} else {
printf("Join Example_TaskHi Success!\n");
}
...
...
@@ -279,8 +285,8 @@ UINT32 Example_TskCaseEntry(VOID)
编译运行得到的结果为:
```
```
LOS_TaskLock() Success!
Example_TaskHi create Success!
Example_TaskLo create Success!
...
...
zh-cn/device-dev/kernel/kernel-
basic-mini
-time.md
→
zh-cn/device-dev/kernel/kernel-
mini-basic
-time.md
浏览文件 @
ad9b265f
...
...
@@ -29,8 +29,8 @@ OpenHarmony LiteOS-M内核的时间管理提供下面几种功能,接口详细
| 接口名 | 描述 |
| -------- | -------- |
| LOS_MS2Tick | 毫秒转换成Tick |
| LOS_Tick2MS | Tick转化为毫秒 |
| LOS_MS2Tick | 毫秒转换成Tick
。
|
| LOS_Tick2MS | Tick转化为毫秒
。
|
| OsCpuTick2MS | Cycle数目转化为毫秒,使用2个UINT32类型的数值分别表示结果数值的高、低32位。 |
| OsCpuTick2US | Cycle数目转化为微秒,使用2个UINT32类型的数值分别表示结果数值的高、低32位。 |
...
...
@@ -38,10 +38,23 @@ OpenHarmony LiteOS-M内核的时间管理提供下面几种功能,接口详细
| 接口名 | 描述 |
| -------- | -------- |
| LOS_SysClockGet | 获取系统时钟 |
| LOS_TickCountGet | 获取自系统启动以来的Tick数 |
| LOS_CyclePerTickGet | 获取每个Tick多少Cycle数 |
| LOS_SysClockGet | 获取系统时钟。 |
| LOS_TickCountGet | 获取自系统启动以来的Tick数。 |
| LOS_CyclePerTickGet | 获取每个Tick多少Cycle数。 |
| LOS_CurrNanosec | 获取当前的时间,单位纳秒。 |
**表3**
时间注册
| 接口名 | 描述 |
| --------------------- | ---------------------------------------------- |
| LOS_TickTimerRegister | 重新注册系统时钟的定时器和对应的中断处理函数。 |
**表4**
延时
| 接口名 | 描述 |
| ---------- | ------------------------ |
| LOS_MDelay | 延时函数,延时单位毫秒。 |
| LOS_UDelay | 延时函数,延时单位微秒。 |
## 开发流程
...
...
@@ -56,7 +69,7 @@ OpenHarmony LiteOS-M内核的时间管理提供下面几种功能,接口详细
>
> - 系统的Tick数在关中断的情况下不进行计数,故系统Tick数不能作为准确时间使用。
>
> -
配置选项维护在开发板工程的文件target_config.h
。
> -
上文描述的配置选项维护在开发板工程 target_config.h 中,部分配置项未定义的缺省值定义在内核 los_config.h中
。
## 编程实例
...
...
@@ -81,17 +94,22 @@ OpenHarmony LiteOS-M内核的时间管理提供下面几种功能,接口详细
时间转换:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleTransformTime和ExampleGetTime。
```
VOID Example
_
TransformTime(VOID)
VOID ExampleTransformTime(VOID)
{
UINT32 ms;
UINT32 tick;
tick = LOS_MS2Tick(10000); // 10000ms转换为tick
dprintf("tick = %d \n", tick);
ms = LOS_Tick2MS(100); // 100tick转换为ms
dprintf("ms = %d \n", ms);
/* 10000ms转换为tick */
tick = LOS_MS2Tick(10000);
printf("tick = %d \n", tick);
/* 100tick转换为ms */
ms = LOS_Tick2MS(100);
printf("ms = %d \n", ms);
}
```
...
...
@@ -99,26 +117,21 @@ VOID Example_TransformTime(VOID)
```
VOID Example
_
GetTime(VOID)
VOID ExampleGetTime(VOID)
{
UINT32 cyclePerTick;
UINT64 tickCount;
UINT64 tickCountBefore;
UINT64 tickCountAfter;
cyclePerTick = LOS_CyclePerTickGet();
if(0 != cyclePerTick) {
dprintf("LOS_CyclePerTickGet = %d \n", cyclePerTick);
}
tickCount = LOS_TickCountGet();
if(0 != tickCount) {
dprintf("LOS_TickCountGet = %d \n", (UINT32)tickCount);
if (0 != cyclePerTick) {
printf("LOS_CyclePerTickGet = %d \n", cyclePerTick);
}
tickCountBefore = LOS_TickCountGet();
LOS_TaskDelay(200);
tickCount = LOS_TickCountGet();
if(0 != tickCount) {
dprintf("LOS_TickCountGet after delay = %d \n", (UINT32)tickCount);
}
tickCountAfter = LOS_TickCountGet();
printf("LOS_TickCountGet after delay rising = %d \n", (UINT32)(tickCountAfter - tickCountBefore));
}
```
...
...
@@ -138,8 +151,7 @@ ms = 1000
时间统计和时间延迟:
```
LOS_CyclePerTickGet = 495000
LOS_TickCountGet = 1
LOS_TickCountGet after delay = 201
```
LOS_CyclePerTickGet = 250000 (根据实际运行环境,数据会有差异)
LOS_TickCountGet after delay rising = 200
```
zh-cn/device-dev/porting/porting-stm32f407-on-minisystem-eth.md
浏览文件 @
ad9b265f
...
...
@@ -715,7 +715,7 @@ board_ld_flags :链接选项,与Makefile中的LDFLAGS变量对应。
### 内核基础功能适配
内核基础功能适配项包括:
**[中断管理](../kernel/kernel-mini-basic-interrupt.md)**
、
**[任务管理](../kernel/kernel-mini-basic-task.md)**
、
**[内存管理](../kernel/kernel-mini-basic-memory.md)**
、
**[内核通信机制](../kernel/kernel-mini-basic-ipc-event.md)**
、
**[时间管理](../kernel/kernel-
basic-mini
-time.md)**
、
**[软件定时器](../kernel/kernel-mini-basic-soft.md)**
,可以参考对应链接中的编程实例进行内核基础功能验证。在验证的过程中发现问题,针对相应问题进行具体的适配。
内核基础功能适配项包括:
**[中断管理](../kernel/kernel-mini-basic-interrupt.md)**
、
**[任务管理](../kernel/kernel-mini-basic-task.md)**
、
**[内存管理](../kernel/kernel-mini-basic-memory.md)**
、
**[内核通信机制](../kernel/kernel-mini-basic-ipc-event.md)**
、
**[时间管理](../kernel/kernel-
mini-basic
-time.md)**
、
**[软件定时器](../kernel/kernel-mini-basic-soft.md)**
,可以参考对应链接中的编程实例进行内核基础功能验证。在验证的过程中发现问题,针对相应问题进行具体的适配。
从上一节中打印信息输出时间间隔可以看出,
`LOS_TaskDelay`
函数的延时时间不准确,我们可以在
`target_config.h`
中定义如下宏进行内核时钟适配:
...
...
zh-cn/device-dev/website.md
浏览文件 @
ad9b265f
...
...
@@ -166,8 +166,9 @@
-
[
互斥锁
](
kernel/kernel-mini-basic-ipc-mutex.md
)
-
[
消息队列
](
kernel/kernel-mini-basic-ipc-queue.md
)
-
[
信号量
](
kernel/kernel-mini-basic-ipc-sem.md
)
-
[
时间管理
](
kernel/kernel-
basic-mini
-time.md
)
-
[
时间管理
](
kernel/kernel-
mini-basic
-time.md
)
-
[
软件定时器
](
kernel/kernel-mini-basic-soft.md
)
-
[
双向链表
](
kernel/kernel-mini-basic-list.md
)
-
扩展组件
-
[
C++支持
](
kernel/kernel-mini-extend-support.md
)
-
[
CPU占用率
](
kernel/kernel-mini-extend-cpup.md
)
...
...
@@ -180,7 +181,6 @@
-
[
LMS调测
](
kernel/kernel-mini-memory-lms.md
)
-
附录
-
[
内核编码规范
](
kernel/kernel-mini-appx-code.md
)
-
[
双向链表
](
kernel/kernel-mini-appx-data-list.md
)
-
[
标准库支持
](
kernel/kernel-mini-appx-lib.md
)
-
小型系统内核(LiteOS-A)
-
[
小型系统内核概述
](
kernel/kernel-small-overview.md
)
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录