未验证 提交 3e873bf4 编写于 作者: O openharmony_ci 提交者: Gitee

!15979 Release3.2文档更新

Merge pull request !15979 from 乔克叔叔/OpenHarmony-3.2-Release
......@@ -26,6 +26,7 @@
- [异常调测](kernel-mini-memory-exception.md)
- [Trace调测](kernel-mini-memory-trace.md)
- [LMS调测](kernel-mini-memory-lms.md)
- [SHELL](kernel-mini-debug-shell.md)
- 附录
- [内核编码规范](kernel-mini-appx-code.md)
- [标准库支持](kernel-mini-appx-lib.md)
......@@ -135,7 +136,7 @@
- [魔法键使用方法](kernel-small-debug-shell-magickey.md)
- [用户态异常信息说明](kernel-small-debug-shell-error.md)
- [Trace调测](kernel-small-debug-trace.md)
- [Perf调测](kernel-mini-memory-perf.md)
- [Perf调测](kernel-small-debug-perf.md)
- [LMS调测](kernel-small-memory-lms.md)
- [进程调测](kernel-small-debug-process-cpu.md)
- 内核态内存调测
......
......@@ -58,7 +58,7 @@ OsMuxInit
## 注释
一般的,尽量通过清晰的软件架构,良好的符号命名来提高代码可读性;然后在需要的时候,才辅以注释说明。
一般的,尽量通过清晰的软件架构,良好的符号命名来提高代码可读性;在需要的时候,辅以注释说明。
注释是为了帮助阅读者快速读懂代码,所以要从读者的角度出发,按需注释。
......
......@@ -459,6 +459,8 @@ demo功能:
创建一个线程并将父线程中的信息传递给子线程,在子线程中打印传递过来的信息和自身线程id值。
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数DemoForTest。
```
#include <stdio.h>
......
......@@ -84,7 +84,7 @@ typedef struct tagEvent {
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> - 进行事件读写操作时,事件的第25位为保留位,不可以进行位设置。
> - 进行事件读写操作时,事件的第25bit(`0x02U << 24`)为保留bit位,不可以进行位设置。
>
> - 对同一事件反复写入,算作一次写入。
......
......@@ -7,7 +7,7 @@
任务能够从队列里面读取消息,当队列中的消息为空时,挂起读取任务;当队列中有新消息时,挂起的读取任务被唤醒并处理新消息。任务也能够往队列里写入消息,当队列已经写满消息时,挂起写入任务;当队列中有空闲消息节点时,挂起的写入任务被唤醒并写入消息。
可以通过调整读队列和写队列的超时时间来调整读写接口的阻塞模式,如果将读队列和写队列的超时时间设置为0,就不会挂起任务,接口会直接返回,这就是非阻塞模式。反之,如果将队列和写队列的超时时间设置为大于0的时间,就会以阻塞模式运行。
可以通过调整读队列和写队列的超时时间来调整读写接口的阻塞模式,如果将读队列和写队列的超时时间设置为0,就不会挂起任务,接口会直接返回,这就是非阻塞模式。反之,如果将队列和写队列的超时时间设置为大于0的时间,就会以阻塞模式运行。
消息队列提供了异步处理机制,允许将一个消息放入队列,但不立即处理。同时队列还有缓冲消息的作用,可以使用队列实现任务异步通信,队列具有如下特性:
......
# SHELL
OpenHarmony内核提供的Shell支持调试常用的基本功能,包含系统、文件、网络相关命令。同时OpenHarmony内核的Shell支持添加新的命令,可以根据需求来进行定制。
Shell功能仅供调试使用,暂不支持tab按键补全,按键回退等功能。
部分命令需要通过make menuconfig开启对应选项才可使用。
## 常用命令介绍
### cat
用于显示文本文件的内容。需开启LOSCFG_FS_VFS。
###### 命令格式
cat [FILE]
###### 参数说明
| 参数 | 参数说明 | 取值范围 |
| ---- | ---------- | -------------- |
| FILE | 文件路径。 | 已存在的文件。 |
### cd
用来改变当前目录。需开启LOSCFG_FS_VFS。
###### 命令格式
cd [path]
###### 参数说明
| 参数 | 参数说明 | 取值范围 |
| ---- | ---------- | -------------- |
| path | 文件路径。 | 已存在的路径。 |
### cp
拷贝文件,创建一份副本。需开启LOSCFG_FS_VFS。
###### 命令格式
cp [SOURCEFILE] [DESTFILE]
###### 参数说明
| 参数 | 参数说明 | 取值范围 |
| ---------- | ---------------------- | ----------------------------------------- |
| SOURCEFILE | 源文件路径。文件路径。 | 目前只支持文件,不支持目录;文件不可为空。 |
| DESTFILE | 目的文件路径。 | 支持目录以及文件名;目录需有效。 |
### date
用于查询系统日期和时间。
###### 命令格式
date
###### 参数说明
### free
显示系统内存的使用情况。
###### 命令格式
free [ -k | -m ]
###### 参数说明
| 参数 | 参数说明 | 取值范围 |
| ---- | ----------------- | -------- |
| -k | 以KiB为单位显示。 | N/A |
| -m | 以MiB为单位显示。 | N/A |
### help
显示当前操作系统内所有操作指令。
###### 命令格式
help
###### 参数说明
### ifconfig
用来查询网卡的IP地址、网络掩码、网关、硬件mac地址等参数。需开启LWIP_SHELLCMD_ENABLE。
###### 命令格式
ifconfig
###### 参数说明
### ls
用来显示当前目录的内容。需开启LOSCFG_FS_VFS。
###### 命令格式
###### ls [DIRECTORY]
| 参数 | 参数说明 | 取值范围 |
| --------- | ---------- | ------------------------------------------------------------ |
| DIRECTORY | 文件路径。 | DIRECTORY为空时,显示当前目录的内容。<br/>DIRECTORY为有效目录路径时,会显示对应目录下的内容。<br/>当前LiteOS-M不支持根目录 "/"。 |
### memusage
用来显示内存使用水线。
###### 命令格式
###### memusage [-k/-m]
| 参数 | 参数说明 | 取值范围 |
| ---- | ----------------- | -------- |
| -k | 以KiB为单位显示。 | N/A |
| -m | 以MiB为单位显示。 | N/A |
### mkdir
用来创建一个目录。需开启LOSCFG_FS_VFS。
###### 命令格式
###### mkdir [DIRECTORY]
| 参数 | 参数说明 | 取值范围 |
| --------- | ---------- | ------------------------------------- |
| DIRECTORY | 文件路径。 | DIRECTORY可以传入绝对路径和相对路径。 |
### ping
用来测试网络连接是否正常。需开启LWIP_SHELLCMD_ENABLE。
###### 命令格式
###### ping [ip]
| 参数 | 参数说明 | 取值范围 |
| ---- | ------------------------------ | -------- |
| ip | 要测试是否网络连通的IPv4地址。 | N/A |
### pwd
用来显示当前路径。需开启LOSCFG_FS_VFS。
###### 命令格式
###### pwd
### rm
用来删除文件或文件夹。需开启LOSCFG_FS_VFS。
###### 命令格式
###### rm [FILE] or rm [-r/-R] [FILE]
| 参数 | 参数说明 | 取值范围 |
| ----- | ------------------------------- | -------------------------------- |
| FILE | 文件名,可以是文件或者文件夹 。 | FILE可以传入绝对路径和相对路径。 |
| -r/-R | FILE是文件夹时需要有-r /-R。 | N/A |
### rmdir
用来删除文件夹。需开启LOSCFG_FS_VFS。
###### 命令格式
###### rmdir [DIRECTORY]
| 参数 | 参数说明 | 取值范围 |
| --------- | ---------- | ------------------------------------- |
| DIRECTORY | 文件路径。 | DIRECTORY可以传入绝对路径和相对路径。 |
### task
用来显示当前各任务状态。
###### 命令格式
###### task
显示内容主要包括任务号,优先级,状态,栈信息,信号,事件,CPU占用率,任务名称等
### touch
用来创建文件。需开启LOSCFG_FS_VFS。
###### 命令格式
###### touch [FILE]
| 参数 | 参数说明 | 取值范围 |
| ---- | -------- | -------------------------------- |
| FILE | 文件名。 | FILE可以传入绝对路径和相对路径。 |
### stack
用来查看指定任务的栈内容。需开启编译选项 LOSCFG_DEBUG_TOOLS,功能开启会影响性能。
###### 命令格式
###### stack [ID]
| 参数 | 参数说明 | 取值范围 |
| ---- | -------- | ------------------------ |
| ID | 任务ID。 | 任务ID对应的任务需存在。 |
### hwi
用来查看中断的占用率。需开启编译选项 LOSCFG_DEBUG_TOOLS,功能开启会影响性能。
###### 命令格式
###### hwi
### st
用来查看调度信息。需开启编译选项 LOSCFG_DEBUG_TOOLS,功能开启会影响性能。
###### 命令格式
###### st -s | st -e
| 参数 | 参数说明 | 取值范围 |
| ---- | ---------------------- | -------- |
| -s | 开始记录调度信息 | N/A |
| -e | 停止记录并打印调度信息 | N/A |
......@@ -5,14 +5,18 @@
CPU(中央处理器,Central Processing Unit)占用率分为系统CPU占用率和任务CPU占用率。
系统CPU占用率(CPU Percent)是指周期时间内系统的CPU占用率,用于表示系统一段时间内的闲忙程度,也表示CPU的负载情况。系统CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分比。100表示系统满负荷运转。
**系统CPU占用率**:是指周期时间内系统的CPU占用率,用于表示系统一段时间内的闲忙程度,也表示CPU的负载情况。系统CPU占用率的有效表示范围为0~100,其单位为百分比。100表示系统满负荷运转。
任务CPU占用率指单个任务的CPU占用率,用于表示单个任务在一段时间内的闲忙程度。任务CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分比。100表示在一段时间内系统一直在运行该任务。
**任务CPU占用率**:指单个任务的CPU占用率,用于表示单个任务在一段时间内的闲忙程度。任务CPU占用率的有效表示范围为0~100,其单位为百分比。100表示在一段时间内系统一直在运行该任务。
用户通过系统级的CPU占用率,判断当前系统负载是否超出设计规格。
通过系统中各个任务的CPU占用情况,判断各个任务的CPU占用率是否符合设计的预期。
此外开启CPUP的情况下,可选择开启中断占用率统计。
**中断占用率**:是指单个中断在全部中断消耗时间的占用率。占用率的有效表示范围为0~100。100表示在一段时间内仅触发该中断。
## 运行机制
......@@ -23,44 +27,51 @@ OpenHarmony LiteOS-M的CPUP(CPU Percent,系统CPU占用率)采用任务
OpenHarmony LiteOS-M提供以下两种CPU占用率的信息查询:
- 系统CPU占用率。
- 任务CPU占用率。
**CPU占用率的计算方法:**
此外,系统还提供了中断占用率的信息查询能力(需同时开启CPUP和定时器)。
**占用率的计算方法:**
系统CPU占用率=系统中除idle任务外其他任务运行总时间/系统运行总时间
任务CPU占用率=任务运行总时间/系统运行总时间
中断占用率=单个中断运行时间/中断运行总时间
## 接口说明
**表1** 功能列表
| 功能分类 | 接口描述 |
| 功能分类 | 接口描述 |
| -------- | -------- |
| 获取系统CPU占用率 | -&nbsp;LOS_SysCpuUsage:获取当前系统CPU占用率<br/>-&nbsp;LOS_HistorySysCpuUsage:获取系统历史CPU占用率 |
| 获取任务CPU占用率 | -&nbsp;LOS_TaskCpuUsage:获取指定任务CPU占用率<br/>-&nbsp;LOS_HistoryTaskCpuUsage:获取指定任务历史CPU占用率<br/>-&nbsp;LOS_HistoryTaskCpuUsage:获取指定任务历史CPU占用率 |
| 输出任务CPU占用率 | LOS_CpupUsageMonitor:输出任务历史CPU占用率 |
| 获取系统CPU占用率 | &nbsp;LOS_SysCpuUsage:获取当前系统CPU占用率<br/>&nbsp;LOS_HistorySysCpuUsage:获取系统历史CPU占用率 |
| 获取任务CPU占用率 | &nbsp;LOS_TaskCpuUsage:获取指定任务CPU占用率<br/>&nbsp;LOS_HistoryTaskCpuUsage:获取指定任务历史CPU占用率<br/>&nbsp;LOS_AllTaskCpuUsage:获取所有任务CPU占用率 |
| 输出任务CPU占用率 | LOS_CpupUsageMonitor:输出任务历史CPU占用率 |
| 获取中断CPU占用率 | LOS_GetAllIrqCpuUsage:获取所有中断CPU占用率 |
## 开发流程
在kernel/liteos_m目录下执行 make menuconfig 命令配置"Kernel->Enable Cpup"中打开YES开启任务cpup;
开启后出现新选项“Enable Cpup include irq”打开YES开启中断cpup。
CPU占用率的典型开发流程:
1. 调用获取系统CPU使用率函数LOS_SysCpuUsage。
1. 调用获取系统CPU用率函数LOS_SysCpuUsage。
2. 调用获取系统历史CPU使用率函数LOS_HistorySysCpuUsage。
2. 调用获取系统历史CPU用率函数LOS_HistorySysCpuUsage。
3. 调用获取指定任务CPU使用率函数LOS_TaskCpuUsage。
3. 调用获取指定任务CPU用率函数LOS_TaskCpuUsage。
- 若任务已创建,则关中断,正常获取,恢复中断;
- 若任务未创建,则返回错误码;
4. 调用获取指定任务历史CPU使用率函数LOS_HistoryTaskCpuUsage。
4. 调用获取指定任务历史CPU用率函数LOS_HistoryTaskCpuUsage。
- 若任务已创建,则关中断,根据不同模式正常获取,恢复中断;
- 若任务未创建,则返回错误码;
5. 调用获取所有任务CPU使用率函数LOS_AllCpuUsage。
5. 调用获取所有任务CPU用率函数LOS_AllCpuUsage。
- 若CPUP已初始化,则关中断,根据不同模式正常获取,恢复中断;
- 若CPUP未初始化或有非法入参,则返回错误码;
......@@ -87,58 +98,67 @@ CPU占用率的典型开发流程:
前提条件:
target_config.h中将LOSCFG_BASE_CORE_CPUP配置项打开。
kernel/liteos_m目录下执行 make menuconfig命令配置"Kernel->Enable Cpup"中开启任务cpup:
代码实现如下:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleCpup。
```
#include "los_task.h"
#include "los_cpup.h"
#define MODE 4
UINT32 g_cpuTestTaskID;
VOID ExampleCpup(VOID)
{
#include "los_cpup.h"
#define TEST_TASK_PRIO 5
#define TASK_DELAY_TIME 100
VOID CpupTask(VOID)
{
printf("entry cpup test example\n");
while(1) {
usleep(100);
}
usleep(TASK_DELAY_TIME);
usleep(TASK_DELAY_TIME);
printf("exit cpup test example\n");
}
UINT32 ItCpupTest(VOID)
{
UINT32 ExampleCpup(VOID)
{
UINT32 ret;
UINT32 cpupUse;
TSK_INIT_PARAM_S cpupTestTask = { 0 };
memset(&cpupTestTask, 0, sizeof(TSK_INIT_PARAM_S));
cpupTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleCpup;
cpupTestTask.pcName = "TestCpupTsk";
cpupTestTask.uwStackSize = 0x800;
cpupTestTask.usTaskPrio = 5;
ret = LOS_TaskCreate(&g_cpuTestTaskID, &cpupTestTask);
UINT32 taskID;
TSK_INIT_PARAM_S cpupTestTask = { 0 };
cpupTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)CpupTask;
cpupTestTask.pcName = "TestCpupTsk";
cpupTestTask.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
cpupTestTask.usTaskPrio = TEST_TASK_PRIO;
ret = LOS_TaskCreate(&taskID, &cpupTestTask);
if(ret != LOS_OK) {
printf("cpupTestTask create failed .\n");
return LOS_NOK;
}
usleep(100);
usleep(TASK_DELAY_TIME);
/* 获取当前系统CPU占用率 */
cpupUse = LOS_SysCpuUsage();
printf("the current system cpu usage is: %u.%u\n",
cpupUse / LOS_CPUP_PRECISION_MULT, cpupUse % LOS_CPUP_PRECISION_MULT);
cpupUse / LOS_CPUP_PRECISION_MULT, cpupUse % LOS_CPUP_PRECISION_MULT);
cpupUse = LOS_HistorySysCpuUsage(CPU_LESS_THAN_1S);
/* 获取指定任务的CPU占用率,该测试例程中指定的任务为以上创建的cpup测试任务 */
/* 获取当前系统历史CPU占用率 */
cpupUse = LOS_HistorySysCpuUsage(CPUP_LESS_THAN_1S);
printf("the history system cpu usage in all time:%u.%u\n",
cpupUse / LOS_CPUP_PRECISION_MULT, cpupUse % LOS_CPUP_PRECISION_MULT);
cpupUse = LOS_TaskCpuUsage(g_cpuTestTaskID);
/* 获取指定历史任务在系统启动到现在的CPU占用率,该测试例程中指定的任务为以上创建的cpup测试任务 */
/* 获取指定任务的CPU占用率 */
cpupUse = LOS_TaskCpuUsage(taskID);
printf("cpu usage of the cpupTestTask:\n TaskID: %d\n usage: %u.%u\n",
g_cpuTestTaskID, cpupUse / LOS_CPUP_PRECISION_MULT, cpupUse % LOS_CPUP_PRECISION_MULT);
cpupUse = LOS_HistoryTaskCpuUsage(g_cpuTestTaskID, CPU_LESS_THAN_1S);
taskID, cpupUse / LOS_CPUP_PRECISION_MULT, cpupUse % LOS_CPUP_PRECISION_MULT);
/* 获取指定任务在系统启动到现在的CPU占用率 */
cpupUse = LOS_HistoryTaskCpuUsage(taskID, CPUP_LESS_THAN_1S);
printf("cpu usage of the cpupTestTask in all time:\n TaskID: %d\n usage: %u.%u\n",
g_cpuTestTaskID, cpupUse / LOS_CPUP_PRECISION_MULT, cpupUse % LOS_CPUP_PRECISION_MULT);
return LOS_OK;
taskID, cpupUse / LOS_CPUP_PRECISION_MULT, cpupUse % LOS_CPUP_PRECISION_MULT);
return LOS_OK;
}
```
......@@ -146,11 +166,19 @@ UINT32 ItCpupTest(VOID)
### 结果验证
编译运行得到的结果为:
```
entry cpup test example
the current system cpu usage is : 1.5
the history system cpu usage in all time: 3.0
cpu usage of the cpupTestTask: TaskID:10 usage: 0.0
cpu usage of the cpupTestTask&nbsp;in all time: TaskID:10 usage: 0.0
entry cpup test example
the current system cpu usage is: 8.2
the history system cpu usage in all time:8.9
cpu usage of the cpupTestTask:
TaskID: 5
usage: 0.5
cpu usage of the cpupTestTask in all time:
TaskID: 5
usage: 0.5
exit cpup test example
根据实际运行环境,上文中的数据会有差异,非固定结果
```
......@@ -62,7 +62,9 @@ typedef struct {
#### 示例代码
代码实现如下:
代码实现如下:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数MemTest。
```
#include <stdio.h>
......@@ -71,20 +73,20 @@ typedef struct {
#include "los_memory.h"
#include "los_config.h"
#define TEST_TASK_PRIO 5
void MemInfoTaskFunc(void)
{
LOS_MEM_POOL_STATUS poolStatus = {0};
/* pool为要统计信息的内存地址,此处以OS_SYS_MEM_ADDR为例 */
/* pool为要统计信息的内存地址,此处以OS_SYS_MEM_ADDR为例 */
void *pool = OS_SYS_MEM_ADDR;
LOS_MemInfoGet(pool, &poolStatus);
/* 算出内存池当前的碎片率百分比 */
unsigned char fragment = 100 - poolStatus.maxFreeNodeSize * 100 / poolStatus.totalFreeSize;
float fragment = 100 - poolStatus.maxFreeNodeSize * 100.0 / poolStatus.totalFreeSize;
/* 算出内存池当前的使用率百分比 */
unsigned char usage = LOS_MemTotalUsedGet(pool) * 100 / LOS_MemPoolSizeGet(pool);
printf("usage = %d, fragment = %d, maxFreeSize = %d, totalFreeSize = %d, waterLine = %d\n", usage, fragment, poolStatus.maxFreeNodeSize,
poolStatus.totalFreeSize, poolStatus.usageWaterLine);
float usage = LOS_MemTotalUsedGet(pool) * 100.0 / LOS_MemPoolSizeGet(pool);
printf("usage = %f, fragment = %f, maxFreeSize = %d, totalFreeSize = %d, waterLine = %d\n", usage, fragment,
poolStatus.maxFreeNodeSize, poolStatus.totalFreeSize, poolStatus.usageWaterLine);
}
int MemTest(void)
......@@ -93,9 +95,9 @@ int MemTest(void)
unsigned int taskID;
TSK_INIT_PARAM_S taskStatus = {0};
taskStatus.pfnTaskEntry = (TSK_ENTRY_FUNC)MemInfoTaskFunc;
taskStatus.uwStackSize = 0x1000;
taskStatus.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
taskStatus.pcName = "memInfo";
taskStatus.usTaskPrio = 10;
taskStatus.usTaskPrio = TEST_TASK_PRIO;
ret = LOS_TaskCreate(&taskID, &taskStatus);
if (ret != LOS_OK) {
printf("task create failed\n");
......@@ -112,7 +114,9 @@ int MemTest(void)
```
usage = 22, fragment = 3, maxFreeSize = 49056, totalFreeSize = 50132, waterLine = 1414
usage = 0.458344, fragment = 0.000000, maxFreeSize = 16474928, totalFreeSize = 16474928, waterLine = 76816
根据实际运行环境,上文中的数据会有差异,非固定结果
```
## 内存泄漏检测
......@@ -179,6 +183,12 @@ node size LR[0] LR[1] LR[2]
代码实现如下:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数MemLeakTest。
qemu平台运行时需确保target_config.h 中对应的LOSCFG_MEM_FREE_BY_TASKID为0。
由于打开内存检测后,部分平台有其他任务运行,会频繁调用内存相关打印如:psp, start = xxxxx, end = xxxxxxx,请忽略打印或删除OsStackAddrGet函数中调用的打印即可。
```
#include <stdio.h>
......@@ -198,7 +208,7 @@ void MemLeakTest(void)
#### 结果验证
编译运行输出log如下:
编译运行输出示例log如下:
```
......@@ -216,7 +226,9 @@ node size LR[0] LR[1] LR[2]
0x20002594: 0x120 0x08000e0c 0x08000e56 0x08000c8a
0x20002aac: 0x56 0x08000e0c 0x08000e56 0x08004220
0x20003ac4: 0x1d 0x08001458 0x080014e0 0x080041e6
0x20003ae0: 0x1d 0x080041ee 0x08000cc2 0x00000000
0x20003ae0: 0x1d 0x080041ee 0x08000cc2 0x00000000
根据实际运行环境,上文中的数据会有差异,非固定结果
```
对比两次log,差异如下,这些内存节点就是疑似泄漏的内存块:
......@@ -224,7 +236,9 @@ node size LR[0] LR[1] LR[2]
```
0x20003ac4: 0x1d 0x08001458 0x080014e0 0x080041e6
0x20003ae0: 0x1d 0x080041ee 0x08000cc2 0x00000000
0x20003ae0: 0x1d 0x080041ee 0x08000cc2 0x00000000
根据实际运行环境,上文中的数据会有差异,非固定结果
```
部分汇编文件如下:
......@@ -246,6 +260,8 @@ node size LR[0] LR[1] LR[2]
0x80041f0: 0xf7fd 0xf933 BL LOS_MemUsedNodeShow ; 0x800145a
0x80041f4: 0xbd10 POP {R4, PC}
0x80041f6: 0x0000 MOVS R0, R0
根据实际运行环境,上文中的数据会有差异,非固定结果
```
其中,通过查找0x080041ee,就可以发现该内存节点是在MemLeakTest接口里申请的且是没有释放的。
......@@ -295,6 +311,12 @@ LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK:开关宏,默认关闭;若打开这
代码实现如下:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数MemIntegrityTest。
qemu平台运行时需确保target_config.h 中对应的LOSCFG_MEM_FREE_BY_TASKID为0。
由于执行时主动触发异常,执行结束后需要重启qemu(例如打开一个新的终端界面输入killall qemu-system-arm)
```
#include <stdio.h>
......@@ -320,20 +342,28 @@ void MemIntegrityTest(void)
```
[ERR][OsMemMagicCheckPrint], 2028, memory check error!
memory used but magic num wrong, magic num = 0x00000000 /* 提示信息,检测到哪个字段被破坏了,用例构造了将下个节点的头4个字节清零,即魔鬼数字字段 */
broken node head: 0x20003af0 0x00000000 0x80000020, prev node head: 0x20002ad4 0xabcddcba 0x80000020
/* 被破坏节点和其前节点关键字段信息,分别为其前节点地址、节点的魔鬼数字、节点的sizeAndFlag;可以看出被破坏节点的魔鬼数字字段被清零,符合用例场景 */
broken node head LR info: /* 节点的LR信息需要开启内存检测功能才有有效输出 */
LR[0]:0x0800414e
LR[1]:0x08000cc2
LR[2]:0x00000000
pre node head LR info: /* 通过LR信息,可以在汇编文件中查找前节点是哪里申请,然后排查其使用的准确性 */
LR[0]:0x08004144
LR[1]:0x08000cc2
LR[2]:0x00000000
[ERR]Memory interity check error, cur node: 0x20003b10, pre node: 0x20003af0 /* 被破坏节点和其前节点的地址 */
/* 提示信息,检测到哪个字段被破坏了,用例构造了将下个节点的头4个字节清零,即魔鬼数字字段 */
[ERR][IT_TST_INI][OsMemMagicCheckPrint], 1664, memory check error!
memory used but magic num wrong, magic num = 0x0
/* 被破坏节点和其前节点关键字段信息,分别为其前节点地址、节点的魔鬼数字、节点的sizeAndFlag;可以看出被破坏节点的魔鬼数字字段被清零,符合用例场景 */
broken node head: 0x2103d7e8 0x0 0x80000020, prev node head: 0x2103c7cc 0xabcddcba 0x80000020
/* 节点的LR信息需要开启前文的内存泄漏检测功能才有有效输出 */
broken node head LR info:
LR[0]:0x2101906c
LR[1]:0x0
LR[2]:0x0
/* 通过LR信息,可以在汇编文件中查找前节点是哪里申请,然后排查其使用的准确性 */
pre node head LR info:
LR[0]:0x2101906c
LR[1]:0x0
LR[2]:0x0
/* 被破坏节点和其前节点的地址 */
[ERR][IT_TST_INI]Memory integrity check error, cur node: 0x2103d784, pre node: 0x0
根据实际运行环境,上文中的数据会有差异,非固定结果
```
......@@ -49,6 +49,8 @@ OpenHarmony LiteOS-M内核的回溯栈模块提供下面几种功能,接口详
1. 使用示例中有问题的代码,编译、运行工程,在串口终端中查看异常信息输出。示例代码模拟异常代码,实际产品开发时使用异常调测机制定位异常问题。
本示例演示异常输出,包含1个任务,该任务入口函数模拟若干函数调用,最终调用一个模拟异常的函数。代码实现如下:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleExcEntry。
```
#include <stdio.h>
#include "los_config.h"
......@@ -59,29 +61,30 @@ OpenHarmony LiteOS-M内核的回溯栈模块提供下面几种功能,接口详
#define TSK_PRIOR 4
/* 模拟异常函数 */
UINT32 Get_Result_Exception_0(UINT16 dividend){
UINT32 divisor = 0;
UINT32 result = dividend / divisor;
UINT32 GetResultException0(UINT16 dividend){
UINT32 result = *(UINT32 *)(0xffffffff);
printf("Enter GetResultException0. %u\r\n", result);
return result;
}
UINT32 Get_Result_Exception_1(UINT16 dividend){
return Get_Result_Exception_0(dividend);
UINT32 GetResultException1(UINT16 dividend){
printf("Enter GetResultException1.\r\n");
return GetResultException0(dividend);
}
UINT32 Get_Result_Exception_2(UINT16 dividend){
return Get_Result_Exception_1(dividend);
UINT32 GetResultException2(UINT16 dividend){
printf("Enter GetResultException2.\r\n");
return GetResultException1(dividend);
}
UINT32 Example_Exc(VOID)
UINT32 ExampleExc(VOID)
{
UINT32 ret;
printf("Enter Example_Exc Handler.\r\n");
/* 模拟函数调用 */
ret = Get_Result_Exception_2(TSK_PRIOR);
ret = GetResultException2(TSK_PRIOR);
printf("Divided result =%u.\r\n", ret);
printf("Exit Example_Exc Handler.\r\n");
......@@ -90,20 +93,20 @@ OpenHarmony LiteOS-M内核的回溯栈模块提供下面几种功能,接口详
/* 任务测试入口函数,创建一个会发生异常的任务 */
UINT32 Example_Exc_Entry(VOID)
UINT32 ExampleExcEntry(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S initParam;
TSK_INIT_PARAM_S initParam = { 0 };
/* 锁任务调度,防止新创建的任务比本任务高而发生调度 */
LOS_TaskLock();
printf("LOS_TaskLock() Success!\r\n");
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Exc;
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleExc;
initParam.usTaskPrio = TSK_PRIOR;
initParam.pcName = "Example_Exc";
initParam.uwStackSize = LOSCFG_SECURE_STACK_DEFAULT_SIZE;
initParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
/* 创建高优先级任务,由于锁任务调度,任务创建成功后不会马上执行 */
ret = LOS_TaskCreate(&g_taskExcId, &initParam);
if (ret != LOS_OK) {
......@@ -115,99 +118,91 @@ OpenHarmony LiteOS-M内核的回溯栈模块提供下面几种功能,接口详
printf("Example_Exc create Success!\r\n");
/* 解锁任务调度,此时会发生任务调度,执行就绪队列中最高优先级任务 */
LOS_TaskUnlock();
return LOS_OK;
}
```
1. 上述代码串口终端输出异常信息如下:
/* 解锁任务调度,此时会发生任务调度,执行就绪队列中最高优先级任务 */
LOS_TaskUnlock();
return LOS_OK;
}
```
述代码串口终端输出异常信息如下:
```
entering kernel init...
LOS_TaskLock() Success!
Example_Exc create Success!
Entering scheduler
Enter Example_Exc Handler.
*Exception Information**
Type = 10
ThrdPid = 4
Enter GetResultException2.
Enter GetResultException1.
*************Exception Information**************
Type = 4
ThrdPid = 5
Phase = exc in task
FaultAddr = 0xabababab
FaultAddr = 0xfffffffc
Current task info:
Task name = Example_Exc
Task ID = 4
Task SP = 0x200051ac
Task ST = 0x20004ff0
Task SS = 0x200
Task ID = 5
Task SP = 0x210549bc
Task ST = 0x21053a00
Task SS = 0x1000
Exception reg dump:
PC = 0x80037da
LR = 0x80037fe
SP = 0x20005190
PC = 0x2101c61a
LR = 0x2101c64d
SP = 0x210549a8
R0 = 0x4
R1 = 0x40
R2 = 0x4
R3 = 0x0
R4 = 0x4040404
R1 = 0xa
R2 = 0x0
R3 = 0xffffffff
R4 = 0x2103fb20
R5 = 0x5050505
R6 = 0x6060606
R7 = 0x20005190
R7 = 0x210549a8
R8 = 0x8080808
R9 = 0x9090909
R10 = 0x10101010
R11 = 0x11111111
R12 = 0x12121212
R12 = 0x0
PriMask = 0x0
xPSR = 0x41000000
----- backtrace start -----
backtrace 0 -- lr = 0x800381a
backtrace 1 -- lr = 0x8003836
backtrace 2 -- lr = 0x8005a4e
backtrace 3 -- lr = 0x8000494
backtrace 4 -- lr = 0x8008620
backtrace 5 -- lr = 0x800282c
backtrace 6 -- lr = 0x80008a0
backtrace 7 -- lr = 0x80099f8
backtrace 8 -- lr = 0x800a01a
backtrace 9 -- lr = 0x800282c
backtrace 10 -- lr = 0x80008a0
backtrace 11 -- lr = 0x80099f8
backtrace 12 -- lr = 0x8009bf0
backtrace 13 -- lr = 0x8009c52
backtrace 14 -- lr = 0x80099aa
backtrace 0 -- lr = 0x2101c64c
backtrace 1 -- lr = 0x2101c674
backtrace 2 -- lr = 0x2101c696
backtrace 3 -- lr = 0x2101b1ec
----- backtrace end -----
TID Priority Status StackSize WaterLine StackPoint TopOfStack EventMask SemID name
--- -------- -------- --------- ---------- ---------- ---------- --------- ----- ----
0 0 Pend 0x2d0 0x104 0x200029bc 0x200027f0 0x0 0xffff Swt_Task
1 31 Ready 0x500 0x44 0x20002f84 0x20002ac8 0x0 0xffff IdleCore000
2 6 Ready 0x1000 0x44 0x20003f94 0x20002fd8 0x0 0xffff TaskSampleEntry1
3 7 Ready 0x1000 0x44 0x20004f9c 0x20003fe0 0x0 0xffff TaskSampleEntry2
4 4 Running 0x200 0xec 0x200051ac 0x20004ff0 0x0 0xffff Example_Exc
TID Priority Status StackSize WaterLine StackPoint TopOfStack EventMask SemID CPUUSE CPUUSE10s CPUUSE1s TaskEntry name
--- -------- -------- --------- --------- ---------- ---------- --------- ------ ------- --------- -------- ---------- ----
0 0 Pend 0x1000 0xdc 0x2104730c 0x210463e8 0 0xffff 0.0 0.0 0.0 0x2101a199 Swt_Task
1 31 Ready 0x500 0x44 0x210478e4 0x21047428 0 0xffff 0.0 0.0 0.0 0x2101a9c9 IdleCore000
2 5 PendTime 0x6000 0xd4 0x2104e8f4 0x210489c8 0 0xffff 5.7 5.7 0.0 0x21016149 tcpip_thread
3 3 Pend 0x1000 0x488 0x2104f90c 0x2104e9e8 0x1 0xffff 8.6 8.6 0.0 0x21016db5 ShellTaskEntry
4 25 Ready 0x4000 0x460 0x21053964 0x2104f9f0 0 0xffff 9.0 8.9 0.0 0x2101c765 IT_TST_INI
5 4 Running 0x1000 0x458 0x210549bc 0x21053a00 0 0xffff 76.5 76.6 0.0 0x2101c685 Example_Exc
OS exception NVIC dump:
interrupt enable register, base address: 0xe000e100, size: 0x20
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x2001 0x0 0x0 0x0 0x0 0x0 0x0 0x0
interrupt pending register, base address: 0xe000e200, size: 0x20
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
interrupt active register, base address: 0xe000e300, size: 0x20
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
interrupt priority register, base address: 0xe000e400, size: 0xf0
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
interrupt exception register, base address: 0xe000ed18, size: 0xc
0x0 0x0 0xf0f00000
0x0 0x0 0xf0f00000
interrupt shcsr register, base address: 0xe000ed24, size: 0x4
0x70008
0x70002
interrupt control register, base address: 0xe000ed04, size: 0x4
0x400f806
0x1000e805
memory pools check:
system heap memcheck over, all passed!
memory pool check end!
根据实际运行环境,上文中的数据会有差异,非固定结果
```
......@@ -215,61 +210,68 @@ OpenHarmony LiteOS-M内核的回溯栈模块提供下面几种功能,接口详
异常接管一般的定位步骤如下:
0. 确认编译时关掉优化选项,否则下述的描述内容可能被优化掉。
1. 打开编译后生成的镜像反汇编(asm)文件。如果默认没有生成,可以使用objdump工具生成,命令为:
```
arm-none-eabi-objdump -S -l XXX.elf
```
1. 搜索PC指针(指向当前正在执行的指令)在asm中的位置,找到发生异常的函数。
PC地址指向发生异常时程序正在执行的指令。在当前执行的二进制文件对应的asm文件中,查找PC值0x80037da,找到当前CPU正在执行的指令行,反汇编如下所示:
PC地址指向发生异常时程序正在执行的指令。在当前执行的二进制文件对应的asm文件中,查找PC值0x2101c61a,找到当前CPU正在执行的指令行,反汇编如下所示:
```
UINT32 Get_Result_Exception_0(UINT16 dividend){
80037c8: b480 push {r7}
80037ca: b085 sub sp, #20
80037cc: af00 add r7, sp, #0
80037ce: 4603 mov r3, r0
80037d0: 80fb strh r3, [r7, #6]
kernel_liteos_m\targets\cortex-m7_nucleo_f767zi_gcc/Core/Src/exc_example.c:10
UINT32 divisor = 0;
80037d2: 2300 movs r3, #0
80037d4: 60fb str r3, [r7, #12]
kernel_liteos_m\targets\cortex-m7_nucleo_f767zi_gcc/Core/Src/exc_example.c:11
UINT32 result = dividend / divisor;
80037d6: 88fa ldrh r2, [r7, #6]
80037d8: 68fb ldr r3, [r7, #12]
80037da: fbb2 f3f3 udiv r3, r2, r3
80037de: 60bb str r3, [r7, #8]
2101c60c <GetResultException0>:
2101c60c: b580 push {r7, lr}
2101c60e: b084 sub sp, #16
2101c610: af00 add r7, sp, #0
2101c612: 4603 mov r3, r0
2101c614: 80fb strh r3, [r7, #6]
2101c616: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff
2101c61a: 681b ldr r3, [r3, #0]
2101c61c: 60fb str r3, [r7, #12]
2101c61e: 68f9 ldr r1, [r7, #12]
2101c620: 4803 ldr r0, [pc, #12] ; (2101c630 <GetResultException0+0x24>)
2101c622: f001 f92b bl 2101d87c <printf>
2101c626: 68fb ldr r3, [r7, #12]
2101c628: 4618 mov r0, r3
2101c62a: 3710 adds r7, #16
2101c62c: 46bd mov sp, r7
2101c62e: bd80 pop {r7, pc}
2101c630: 21025f90 .word 0x21025f90
```
1. 可以看到:
1. 异常时CPU正在执行的指令是udiv r3, r2, r3,其中r3取值为0,导致发生除零异常。
2. 异常发生在函数Get_Result_Exception_0中。
1. 异常时CPU正在执行的指令是ldr r3, [r3, #0],其中r3取值为0xffffffff,导致发生非法地址异常。
2. 异常发生在函数GetResultException0中。
2. 根据LR值查找异常函数的父函数。
包含LR值0x80037fe的反汇编如下所示:
包含LR值0x2101c64d的反汇编如下所示:
```
080037ec <Get_Result_Exception_1>:
Get_Result_Exception_1():
kernel_liteos_m\targets\cortex-m7_nucleo_f767zi_gcc/Core/Src/exc_example.c:15
UINT32 Get_Result_Exception_1(UINT16 dividend){
80037ec: b580 push {r7, lr}
80037ee: b082 sub sp, #8
80037f0: af00 add r7, sp, #0
80037f2: 4603 mov r3, r0
80037f4: 80fb strh r3, [r7, #6]
kernel_liteos_m\targets\cortex-m7_nucleo_f767zi_gcc/Core/Src/exc_example.c:16
return Get_Result_Exception_0(dividend);
80037f6: 88fb ldrh r3, [r7, #6]
80037f8: 4618 mov r0, r3
80037fa: f7ff ffe5 bl 80037c8 <Get_Result_Exception_0>
80037fe: 4603 mov r3, r0
2101c634 <GetResultException1>:
2101c634: b580 push {r7, lr}
2101c636: b082 sub sp, #8
2101c638: af00 add r7, sp, #0
2101c63a: 4603 mov r3, r0
2101c63c: 80fb strh r3, [r7, #6]
2101c63e: 4806 ldr r0, [pc, #24] ; (2101c658 <GetResultException1+0x24>)
2101c640: f001 f91c bl 2101d87c <printf>
2101c644: 88fb ldrh r3, [r7, #6]
2101c646: 4618 mov r0, r3
2101c648: f7ff ffe0 bl 2101c60c <GetResultException0>
2101c64c: 4603 mov r3, r0
2101c64e: 4618 mov r0, r3
2101c650: 3708 adds r7, #8
2101c652: 46bd mov sp, r7
2101c654: bd80 pop {r7, pc}
2101c656: bf00 nop
2101c658: 21025fb0 .word 0x21025fb0
```
1. LR值80037fe上一行是bl 80037c8 &lt;Get_Result_Exception_0&gt;,此处调用了异常函数,调用异常函数的父函数为Get_Result_Exception_1()
1. LR值2101c648上一行是bl 2101c60c <GetResultException0&gt;,此处调用了异常函数,调用异常函数的父函数为GetResultException1
2. 重复步骤3,解析异常信息中backtrace start至backtrace end之间的LR值,得到调用产生异常的函数调用栈关系,找到异常原因。
......@@ -3,7 +3,7 @@
## 基本概念
LMS全称为Lite Memory Sanitizer,是一种实时检测内存操作合法性的调测工具。LMS能够实时检测缓冲区溢出(buffer overflow),释放后使用(use after free) 和重复释放(double Free), 在异常发生的第一时间通知操作系统,结合backtrace等定位手段,能准确定位到产生内存问题的代码行,极大提升内存问题定位效率。
LMS(Lite Memory Sanitizer)是一种实时检测内存操作合法性的调测工具。LMS能够实时检测缓冲区溢出(buffer overflow),释放后使用(use after free) 和重复释放(double free), 在异常发生的第一时间通知操作系统,结合backtrace等定位手段,能准确定位到产生内存问题的代码行,极大提升内存问题定位效率。
OpenHarmony LiteOS-M内核的LMS模块提供下面几种功能:
......@@ -51,7 +51,7 @@ OpenHarmony LiteOS-M内核的LMS模块提供下面几种功能,接口详细信
开启LMS调测的典型流程如下:
1. 配置LMS模块相关宏。
配置LMS控制宏LOSCFG_KERNEL_LMS,默认关,在kernel/liteos_m目录下执行 make update_config命令配置"Kernel-&gt;Enable Lite Memory Sanitizer"中打开YES
配置LMS控制宏LOSCFG_KERNEL_LMS,默认关,在kernel/liteos_m目录下执行 make menuconfig命令配置"Kernel-&gt;Enable Lite Memory Sanitizer"中打开YES(如果没有这个选项,需要先勾选Enable Backtrace)
| 宏 | menuconfig选项 | 含义 | 取值 |
| -------- | -------- | -------- | -------- |
......@@ -65,7 +65,7 @@ OpenHarmony LiteOS-M内核的LMS模块提供下面几种功能,接口详细信
增加LMS检测编译选项-fsanitize=kernel-address。为避免编译器优化,增加-O0编译选项。
gcc与clang编译选项存在差异,参照如下示例:
```
if ("$ohos_build_compiler_specified" == "gcc") {
cflags_c = [
......@@ -103,7 +103,11 @@ OpenHarmony LiteOS-M内核的LMS模块提供下面几种功能,接口详细信
### 示例代码
实例代码如下:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数Example_Lms_test。
请按上文避免编译器优化一节内容,修改osTest.c对应的 ./kernel/liteos_m/testsuites/BUILD.gn。
```
#define PAGE_SIZE (0x1000U)
#define INDEX_MAX 20
......@@ -162,65 +166,114 @@ UINT32 Example_Lms_test(VOID){
### 结果验证
输出结果如下
输出结果示例如下 (根据实际运行环境,数据会有差异)
```
######LmsTestOsmallocOverflow start ######
[ERR]* Kernel Address Sanitizer Error Detected Start *
[ERR]Heap buffer overflow error detected
[ERR]Illegal READ address at: [0x4157a3c8]
[ERR]Shadow memory address: [0x4157be3c : 4] Shadow memory value: [2]
OsBackTrace fp = 0x402c0f88
runTask->taskName = LmsTestCaseTask
runTask->taskID = 2
***backtrace begin***
traceback fp fixed, trace using fp = 0x402c0fd0
traceback 0 -- lr = 0x400655a4 fp = 0x402c0ff8
traceback 1 -- lr = 0x40065754 fp = 0x402c1010
traceback 2 -- lr = 0x40044bd0 fp = 0x402c1038
traceback 3 -- lr = 0x40004e14 fp = 0xcacacaca
[LMS] Dump info around address [0x4157a3c8]:
[0x4157a3a0]: 00 00 00 00 00 00 00 00 | [0x4157be3a | 0]: 1 1
[0x4157a3a8]: ba dc cd ab 00 00 00 00 | [0x4157be3a | 4]: 2 2
[0x4157a3b0]: 20 00 00 80 00 00 00 00 | [0x4157be3b | 0]: 2 0
[0x4157a3b8]: 00 00 00 00 00 00 00 00 | [0x4157be3b | 4]: 0 0
[0x4157a3c0]: 00 00 00 00 00 00 00 00 | [0x4157be3c | 0]: 0 0
[0x4157a3c8]: [ba] dc cd ab a8 a3 57 41 | [0x4157be3c | 4]: [2] 2
[0x4157a3d0]: 2c 1a 00 00 00 00 00 00 | [0x4157be3d | 0]: 2 3
[0x4157a3d8]: 00 00 00 00 00 00 00 00 | [0x4157be3d | 4]: 3 3
[0x4157a3e0]: 00 00 00 00 00 00 00 00 | [0x4157be3e | 0]: 3 3
[0x4157a3e8]: 00 00 00 00 00 00 00 00 | [0x4157be3e | 4]: 3 3
[0x4157a3f0]: 00 00 00 00 00 00 00 00 | [0x4157be3f | 0]: 3 3
[ERR]* Kernel Address Sanitizer Error Detected End *
str[20]=0xffffffba
[ERR][TestLmsTsk]***** Kernel Address Sanitizer Error Detected Start *****
[ERR][TestLmsTsk]Heap buffer overflow error detected
[ERR][TestLmsTsk]Illegal READ address at: [0x21040414]
[ERR][TestLmsTsk]Shadow memory address: [0x21041e84 : 6] Shadow memory value: [2]
psp, start = 21057d88, end = 21057e80
taskName = TestLmsTsk
taskID = 5
----- traceback start -----
traceback 0 -- lr = 0x210099f4
traceback 1 -- lr = 0x2101da6e
traceback 2 -- lr = 0x2101db38
traceback 3 -- lr = 0x2101c494
----- traceback end -----
[LMS] Dump info around address [0x21040414]:
[0x21040390]: 00 00 00 00 00 00 00 00 | [0x21041e7c | 4]: 1 1
[0x21040398]: 00 00 00 00 00 00 00 00 | [0x21041e7d | 0]: 1 1
[0x210403a0]: 00 00 00 00 00 00 00 00 | [0x21041e7d | 4]: 1 1
[0x210403a8]: 00 00 00 00 00 00 00 00 | [0x21041e7e | 0]: 1 1
[0x210403b0]: 00 00 00 00 00 00 00 00 | [0x21041e7e | 4]: 1 1
[0x210403b8]: 00 00 00 00 00 00 00 00 | [0x21041e7f | 0]: 1 1
[0x210403c0]: 00 00 00 00 00 00 00 00 | [0x21041e7f | 4]: 1 1
[0x210403c8]: 00 00 00 00 00 00 00 00 | [0x21041e80 | 0]: 1 1
[0x210403d0]: 00 00 00 00 00 00 00 00 | [0x21041e80 | 4]: 1 1
[0x210403d8]: 00 00 00 00 00 00 00 00 | [0x21041e81 | 0]: 1 1
[0x210403e0]: 00 00 00 00 00 00 00 00 | [0x21041e81 | 4]: 1 1
[0x210403e8]: 00 00 00 00 00 00 00 00 | [0x21041e82 | 0]: 1 1
[0x210403f0]: 00 00 00 00 00 00 00 00 | [0x21041e82 | 4]: 1 1
[0x210403f8]: 40 1e 04 21 05 07 00 80 | [0x21041e83 | 0]: 2 2
[0x21040400]: 00 00 00 00 00 00 00 00 | [0x21041e83 | 4]: 0 0
[0x21040408]: 00 00 00 00 00 00 00 00 | [0x21041e84 | 0]: 0 0
[0x21040410]: 00 00 00 00 [f8] 03 04 21 | [0x21041e84 | 4]: 0 [2]
[0x21040418]: 00 8b 06 00 00 00 00 00 | [0x21041e85 | 0]: 2 3
[0x21040420]: 00 00 00 00 00 00 00 00 | [0x21041e85 | 4]: 3 3
[0x21040428]: 00 00 00 00 00 00 00 00 | [0x21041e86 | 0]: 3 3
[0x21040430]: 00 00 00 00 00 00 00 00 | [0x21041e86 | 4]: 3 3
[0x21040438]: 00 00 00 00 00 00 00 00 | [0x21041e87 | 0]: 3 3
[0x21040440]: 00 00 00 00 00 00 00 00 | [0x21041e87 | 4]: 3 3
[0x21040448]: 00 00 00 00 00 00 00 00 | [0x21041e88 | 0]: 3 3
[0x21040450]: 00 00 00 00 00 00 00 00 | [0x21041e88 | 4]: 3 3
[0x21040458]: 00 00 00 00 00 00 00 00 | [0x21041e89 | 0]: 3 3
[0x21040460]: 00 00 00 00 00 00 00 00 | [0x21041e89 | 4]: 3 3
[0x21040468]: 00 00 00 00 00 00 00 00 | [0x21041e8a | 0]: 3 3
[0x21040470]: 00 00 00 00 00 00 00 00 | [0x21041e8a | 4]: 3 3
[0x21040478]: 00 00 00 00 00 00 00 00 | [0x21041e8b | 0]: 3 3
[0x21040480]: 00 00 00 00 00 00 00 00 | [0x21041e8b | 4]: 3 3
[0x21040488]: 00 00 00 00 00 00 00 00 | [0x21041e8c | 0]: 3 3
[0x21040490]: 00 00 00 00 00 00 00 00 | [0x21041e8c | 4]: 3 3
[ERR][TestLmsTsk]***** Kernel Address Sanitizer Error Detected End *****
str[20]=0xfffffff8
######LmsTestOsmallocOverflow stop ######
###### LmsTestUseAfterFree start ######
[ERR]* Kernel Address Sanitizer Error Detected Start *
[ERR]Use after free error detected
[ERR]Illegal READ address at: [0x4157a3d4]
[ERR]Shadow memory address: [0x4157be3d : 2] Shadow memory value: [3]
OsBackTrace fp = 0x402c0f90
runTask->taskName = LmsTestCaseTask
runTask->taskID = 2
***backtrace begin***
traceback fp fixed, trace using fp = 0x402c0fd8
traceback 0 -- lr = 0x40065680 fp = 0x402c0ff8
traceback 1 -- lr = 0x40065758 fp = 0x402c1010
traceback 2 -- lr = 0x40044bd0 fp = 0x402c1038
traceback 3 -- lr = 0x40004e14 fp = 0xcacacaca
[LMS] Dump info around address [0x4157a3d4]:
[0x4157a3a8]: ba dc cd ab 00 00 00 00 | [0x4157be3a | 4]: 2 2
[0x4157a3b0]: 20 00 00 80 00 00 00 00 | [0x4157be3b | 0]: 2 0
[0x4157a3b8]: 00 00 00 00 00 00 00 00 | [0x4157be3b | 4]: 0 0
[0x4157a3c0]: 00 00 00 00 00 00 00 00 | [0x4157be3c | 0]: 0 0
[0x4157a3c8]: ba dc cd ab a8 a3 57 41 | [0x4157be3c | 4]: 2 2
[0x4157a3d0]: 2c 1a 00 00 [00] 00 00 00 | [0x4157be3d | 0]: 2 [3]
[0x4157a3d8]: 00 00 00 00 00 00 00 00 | [0x4157be3d | 4]: 3 3
[0x4157a3e0]: 00 00 00 00 00 00 00 00 | [0x4157be3e | 0]: 3 3
[0x4157a3e8]: ba dc cd ab c8 a3 57 41 | [0x4157be3e | 4]: 2 2
[0x4157a3f0]: 0c 1a 00 00 00 00 00 00 | [0x4157be3f | 0]: 2 3
[0x4157a3f8]: 00 00 00 00 00 00 00 00 | [0x4157be3f | 4]: 3 3
[ERR]* Kernel Address Sanitizer Error Detected End *
######LmsTestUseAfterFree start ######
[ERR][TestLmsTsk]***** Kernel Address Sanitizer Error Detected Start *****
[ERR][TestLmsTsk]Use after free error detected
[ERR][TestLmsTsk]Illegal READ address at: [0x2104041c]
[ERR][TestLmsTsk]Shadow memory address: [0x21041e85 : 2] Shadow memory value: [3]
psp, start = 21057d90, end = 21057e80
taskName = TestLmsTsk
taskID = 5
----- traceback start -----
traceback 0 -- lr = 0x210099f4
traceback 1 -- lr = 0x2101daec
traceback 2 -- lr = 0x2101db3c
traceback 3 -- lr = 0x2101c494
----- traceback end -----
[LMS] Dump info around address [0x2104041c]:
[0x21040398]: 00 00 00 00 00 00 00 00 | [0x21041e7d | 0]: 1 1
[0x210403a0]: 00 00 00 00 00 00 00 00 | [0x21041e7d | 4]: 1 1
[0x210403a8]: 00 00 00 00 00 00 00 00 | [0x21041e7e | 0]: 1 1
[0x210403b0]: 00 00 00 00 00 00 00 00 | [0x21041e7e | 4]: 1 1
[0x210403b8]: 00 00 00 00 00 00 00 00 | [0x21041e7f | 0]: 1 1
[0x210403c0]: 00 00 00 00 00 00 00 00 | [0x21041e7f | 4]: 1 1
[0x210403c8]: 00 00 00 00 00 00 00 00 | [0x21041e80 | 0]: 1 1
[0x210403d0]: 00 00 00 00 00 00 00 00 | [0x21041e80 | 4]: 1 1
[0x210403d8]: 00 00 00 00 00 00 00 00 | [0x21041e81 | 0]: 1 1
[0x210403e0]: 00 00 00 00 00 00 00 00 | [0x21041e81 | 4]: 1 1
[0x210403e8]: 00 00 00 00 00 00 00 00 | [0x21041e82 | 0]: 1 1
[0x210403f0]: 00 00 00 00 00 00 00 00 | [0x21041e82 | 4]: 1 1
[0x210403f8]: 40 1e 04 21 05 07 00 80 | [0x21041e83 | 0]: 2 2
[0x21040400]: 00 00 00 00 00 00 00 00 | [0x21041e83 | 4]: 0 0
[0x21040408]: 00 00 00 00 00 00 00 00 | [0x21041e84 | 0]: 0 0
[0x21040410]: 00 00 00 00 f8 03 04 21 | [0x21041e84 | 4]: 0 2
[0x21040418]: 05 8b 06 00 [00] 00 00 00 | [0x21041e85 | 0]: 2 [3]
[0x21040420]: 00 00 00 00 00 00 00 00 | [0x21041e85 | 4]: 3 3
[0x21040428]: 00 00 00 00 00 00 00 00 | [0x21041e86 | 0]: 3 3
[0x21040430]: 14 04 04 21 00 84 06 00 | [0x21041e86 | 4]: 2 2
[0x21040438]: 00 00 00 00 00 00 00 00 | [0x21041e87 | 0]: 3 3
[0x21040440]: 00 00 00 00 00 00 00 00 | [0x21041e87 | 4]: 3 3
[0x21040448]: 00 00 00 00 00 00 00 00 | [0x21041e88 | 0]: 3 3
[0x21040450]: 00 00 00 00 00 00 00 00 | [0x21041e88 | 4]: 3 3
[0x21040458]: 00 00 00 00 00 00 00 00 | [0x21041e89 | 0]: 3 3
[0x21040460]: 00 00 00 00 00 00 00 00 | [0x21041e89 | 4]: 3 3
[0x21040468]: 00 00 00 00 00 00 00 00 | [0x21041e8a | 0]: 3 3
[0x21040470]: 00 00 00 00 00 00 00 00 | [0x21041e8a | 4]: 3 3
[0x21040478]: 00 00 00 00 00 00 00 00 | [0x21041e8b | 0]: 3 3
[0x21040480]: 00 00 00 00 00 00 00 00 | [0x21041e8b | 4]: 3 3
[0x21040488]: 00 00 00 00 00 00 00 00 | [0x21041e8c | 0]: 3 3
[0x21040490]: 00 00 00 00 00 00 00 00 | [0x21041e8c | 4]: 3 3
[0x21040498]: 00 00 00 00 00 00 00 00 | [0x21041e8d | 0]: 3 3
[ERR][TestLmsTsk]***** Kernel Address Sanitizer Error Detected End *****
str[ 0]=0x 0
######LmsTestUseAfterFree stop ######
```
......
......@@ -42,7 +42,7 @@ OpenHarmony LiteOS-M内核的Trace模块提供下面几种功能,接口详细
- IDENTITY类型UINTPTR,表示事件操作的主体对象。
- Params类型UINTPTR,表示事件的参数。
- 对文件fd读写操作的简易插桩示例:
```
/* 假设自定义读操作为type: 1, 写操作为type: 2 */
LOS_TRACE_EASY(1, fd, flag, size); /* 在读fd文件的适当位置插入 */
......@@ -62,7 +62,7 @@ OpenHarmony LiteOS-M内核的Trace模块提供下面几种功能,接口详细
2. 定义FS模块的具体事件类型
```
/* 定义规范为#TYPE# = TRACE_#MOD#_FLAG | NUMBER, */
FS_READ = TRACE_FS_FLAG | 0; /* 读文件 */
......@@ -71,7 +71,7 @@ OpenHarmony LiteOS-M内核的Trace模块提供下面几种功能,接口详细
3. 定义事件参数
```
/* 定义规范为#TYPE#_PARAMS(IDENTITY, parma1...) IDENTITY, ... */
#define FS_READ_PARAMS(fp, fd, flag, size) fp, fd, flag, size /* 宏定义的参数对应于Trace缓冲区中记录的事件参数,用户可对任意参数字段进行裁剪 */
......@@ -80,7 +80,7 @@ OpenHarmony LiteOS-M内核的Trace模块提供下面几种功能,接口详细
4. 在目标代码中插桩
```
/* 定义规范为LOS_TRACE(#TYPE#, #TYPE#_PARAMS(IDENTITY, parma1...)) */
LOS_TRACE(FS_READ, fp, fd, flag, size); /* 读文件操作的代码桩 */
......@@ -178,7 +178,9 @@ OpenHarmony LiteOS-M内核的Trace模块提供下面几种功能,接口详细
示例代码如下:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleTraceTest。
```
#include "los_trace.h"
UINT32 g_traceTestTaskId;
......@@ -200,9 +202,9 @@ VOID Example_Trace(VOID)
LOS_TraceStop();
LOS_TraceRecordDump(FALSE);
}
UINT32 Example_Trace_test(VOID){
UINT32 ExampleTraceTest(VOID){
UINT32 ret;
TSK_INIT_PARAM_S traceTestTask;
TSK_INIT_PARAM_S traceTestTask = { 0 };
/* 创建用于trace测试的任务 */
memset(&traceTestTask, 0, sizeof(TSK_INIT_PARAM_S));
traceTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Trace;
......@@ -229,7 +231,7 @@ UINT32 Example_Trace_test(VOID){
输出结果如下:
```
***TraceInfo begin***
clockFreq = 50000000
......@@ -244,6 +246,8 @@ Index Time(cycles) EventType CurTask Identity params
6 0x3706f804 0x45 0x1 0x0 0x1f 0x4 0x0
7 0x37070e59 0x45 0x0 0x1 0x0 0x8 0x1f
***TraceInfo end***
根据实际运行环境,上文中的数据会有差异,非固定结果
```
输出的事件信息包括:发生时间、事件类型、事件发生在哪个任务中、事件操作的主体对象、事件的其他参数。
......@@ -258,7 +262,7 @@ Index Time(cycles) EventType CurTask Identity params
下面以序号为0的输出项为例,进行说明。
```
Index Time(cycles) EventType CurTask Identity params
0 0x366d5e88 0x45 0x1 0x0 0x1f 0x4
......
......@@ -14,9 +14,9 @@
| **功能分类** | **接口描述** |
| -------- | -------- |
| 置1/清0标志位 | -&nbsp;LOS_BitmapSet:对状态字的某一标志位进行置1操作<br/>-&nbsp;LOS_BitmapClr:对状态字的某一标志位进行清0操作 |
| 获取标志位为1的bit位 | -&nbsp;LOS_HighBitGet:获取状态字中为1的最高位<br/>-&nbsp;LOS_LowBitGet:获取状态字中为1的最低位 |
| 连续bit位操作 | -&nbsp;LOS_BitmapSetNBits:对状态字的连续标志位进行置1操作<br/>-&nbsp;LOS_BitmapClrNBits:对状态字的连续标志位进行清0操作<br/>-&nbsp;LOS_BitmapFfz:获取从最低有效位开始的第一个0的bit位 |
| 置1/清0标志位 | -&nbsp;LOS_BitmapSet:对状态字的某一标志位进行置1操作<br/>-&nbsp;LOS_BitmapClr:对状态字的某一标志位进行清0操作 |
| 获取标志位为1的bit位 | -&nbsp;LOS_HighBitGet:获取状态字中为1的最高位<br/>-&nbsp;LOS_LowBitGet:获取状态字中为1的最低位 |
| 连续bit位操作 | -&nbsp;LOS_BitmapSetNBits:对状态字的连续标志位进行置1操作<br/>-&nbsp;LOS_BitmapClrNBits:对状态字的连续标志位进行清0操作<br/>-&nbsp;LOS_BitmapFfz:获取从最低有效位开始的第一个0的bit位 |
## 编程实例
......@@ -34,7 +34,10 @@
4. 获取标志位为1的最低bit位。
### 编程示例
本演示代码在./kernel/liteos_a/testsuites/kernel/src/osTest.c中编译验证,在TestTaskEntry中调用验证入口函数BitSample。
```
#include "los_bitmap.h"
#include "los_printf.h"
......
......@@ -17,10 +17,10 @@
| 增加节点 | -&nbsp;LOS_ListAdd:将指定节点插入到双向链表头端<br/>-&nbsp;LOS_ListHeadInsert:将指定节点插入到双向链表头端,同LOS_ListAdd<br/>-&nbsp;LOS_ListTailInsert:将指定节点插入到双向链表尾端 |
| 增加链表 | -&nbsp;LOS_ListAddList:将指定链表的头端插入到双向链表头端<br/>-&nbsp;LOS_ListHeadInsertList:将指定链表的头端插入到双向链表头端<br/>-&nbsp;LOS_ListTailInsertList:将指定链表的尾端插入到双向链表头端 |
| 删除节点 | -&nbsp;LOS_ListDelete:将指定节点从链表中删除<br/>-&nbsp;LOS_ListDelInit:将指定节点从链表中删除,并使用该节点初始化链表 |
| 判断双向链表 | -&nbsp;LOS_ListEmpty:判断链表是否为空<br/>-&nbsp;LOS_DL_LIST_IS_END:判断指定链表节点是否为链表尾端LOS_DL_LIST_IS_ON_QUEUE:判断链表节点是否在双向链表里 |
| 判断双向链表 | -&nbsp;LOS_ListEmpty:判断链表是否为空<br/>-&nbsp;LOS_DL_LIST_IS_END:判断指定链表节点是否为链表尾端<br/>-&nbsp;LOS_DL_LIST_IS_ON_QUEUE:判断链表节点是否在双向链表里 |
| 获取结构体信息 | -&nbsp;LOS_OFF_SET_OF:获取指定结构体内的成员相对于结构体起始地址的偏移量<br/>-&nbsp;LOS_DL_LIST_ENTRY:获取双向链表中第一个链表节点所在的结构体地址,接口的第一个入参表示的是链表中的头节点,第二个入参是要获取的结构体名称,第三个入参是链表在该结构体中的名称<br/>-&nbsp;LOS_ListPeekHeadType:获取双向链表中第一个链表节点所在的结构体地址,接口的第一个入参表示的是链表中的头节点,第二个入参是要获取的结构体名称,第三个入参是链表在该结构体中的名称。如果链表为空,返回NULL。<br/>-&nbsp;LOS_ListRemoveHeadType:获取双向链表中第一个链表节点所在的结构体地址,并把第一个链表节点从链表中删除。接口的第一个入参表示的是链表中的头节点,第二个入参是要获取的结构体名称,第三个入参是链表在该结构体中的名称。如果链表为空,返回NULL。<br/>-&nbsp;LOS_ListNextType:获取双向链表中指定链表节点的下一个节点所在的结构体地址。接口的第一个入参表示的是链表中的头节点,第二个入参是指定的链表节点,第三个入参是要获取的结构体名称,第四个入参是链表在该结构体中的名称。如果链表节点下一个为链表头结点为空,返回NULL。|
| 遍历双向链表 | -&nbsp;LOS_DL_LIST_FOR_EACH:遍历双向链表<br/>-&nbsp;LOS_DL_LIST_FOR_EACH_SAFE:遍历双向链表,并存储当前节点的后继节点用于安全校验 |
| 遍历包含双向链表的结构体 | -&nbsp;LOS_DL_LIST_FOR_EACH_ENTRY:遍历指定双向链表,获取包含该链表节点的结构体地址<br/>-&nbsp;LOS_DL_LIST_FOR_EACH_ENTRY_SAFE:遍历指定双向链表,获取包含该链表节点的结构体地址,并存储包含当前节点的后继节点的结构体地址 |
| 遍历双向链表 | -&nbsp;LOS_DL_LIST_FOR_EACH:遍历双向链表<br/>-&nbsp;LOS_DL_LIST_FOR_EACH_SAFE:遍历双向链表,并存储当前节点的后继节点用于安全校验 |
| 遍历包含双向链表的结构体 | -&nbsp;LOS_DL_LIST_FOR_EACH_ENTRY:遍历指定双向链表,获取包含该链表节点的结构体地址<br/>-&nbsp;LOS_DL_LIST_FOR_EACH_ENTRY_SAFE:遍历指定双向链表,获取包含该链表节点的结构体地址,并存储包含当前节点的后继节点的结构体地址 |
## 开发流程
......@@ -48,10 +48,10 @@
> - 如果链表节点的内存是动态申请的,删除节点时,要注意释放内存。
**编程实例**
## 编程实例
**实例描述**
### 实例描述
本实例实现如下功能:
......@@ -64,7 +64,11 @@
4. 测试操作是否成功。
### 编程示例
本演示代码在./kernel/liteos_a/testsuites/kernel/src/osTest.c中编译验证,在TestTaskEntry中调用验证入口函数ListSample
示例代码如下:
```
#include "stdio.h"
......
......@@ -15,22 +15,24 @@ musl libc库支持POSIX标准,涉及的系统调用相关接口由OpenHarmony
标准库支持接口的详细情况请参考C库的API文档,其中也涵盖了与POSIX标准之间的差异说明。
## 操作实例
### 编程实例
#### 实例描述
在本示例中,主线程创建了THREAD_NUM个子线程,每个子线程启动后等待被主线程唤醒,主线程成功唤醒所有子线程后,子线程继续执行直至生命周期结束,同时主线程通过pthread_join方法等待所有线程执行结束。
#### 编程示例
本演示代码在./kernel/liteos_a/testsuites/kernel/src/osTest.c中编译验证,在TestTaskEntry中调用验证入口函数ExamplePosix。
示例代码如下:
```
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
#define THREAD_NUM 3
int g_startNum = 0; /* 启动的线程数 */
int g_wakenNum = 0; /* 唤醒的线程数 */
......@@ -40,10 +42,8 @@ struct testdata {
pthread_cond_t cond;
} g_td;
/*
* 子线程入口函数
*/
static void *ChildThreadFunc(void *arg)
/* 子线程入口函数 */
static VOID *ChildThreadFunc(VOID *arg)
{
int rc;
pthread_t self = pthread_self();
......@@ -51,7 +51,7 @@ static void *ChildThreadFunc(void *arg)
/* 获取mutex锁 */
rc = pthread_mutex_lock(&g_td.mutex);
if (rc != 0) {
printf("ERROR:take mutex lock failed, error code is %d!\n", rc);
dprintf("ERROR:take mutex lock failed, error code is %d!\n", rc);
goto EXIT;
}
......@@ -61,7 +61,7 @@ static void *ChildThreadFunc(void *arg)
/* 等待cond条件变量 */
rc = pthread_cond_wait(&g_td.cond, &g_td.mutex);
if (rc != 0) {
printf("ERROR: pthread condition wait failed, error code is %d!\n", rc);
dprintf("ERROR: pthread condition wait failed, error code is %d!\n", rc);
(void)pthread_mutex_unlock(&g_td.mutex);
goto EXIT;
}
......@@ -69,7 +69,7 @@ static void *ChildThreadFunc(void *arg)
/* 尝试获取mutex锁,正常场景,此处无法获取锁 */
rc = pthread_mutex_trylock(&g_td.mutex);
if (rc == 0) {
printf("ERROR: mutex gets an abnormal lock!\n");
dprintf("ERROR: mutex gets an abnormal lock!\n");
goto EXIT;
}
......@@ -79,14 +79,14 @@ static void *ChildThreadFunc(void *arg)
/* 释放mutex锁 */
rc = pthread_mutex_unlock(&g_td.mutex);
if (rc != 0) {
printf("ERROR: mutex release failed, error code is %d!\n", rc);
dprintf("ERROR: mutex release failed, error code is %d!\n", rc);
goto EXIT;
}
EXIT:
return NULL;
}
static int testcase(void)
static int ExamplePosix(VOID)
{
int i, rc;
pthread_t thread[THREAD_NUM];
......@@ -94,14 +94,14 @@ static int testcase(void)
/* 初始化mutex锁 */
rc = pthread_mutex_init(&g_td.mutex, NULL);
if (rc != 0) {
printf("ERROR: mutex init failed, error code is %d!\n", rc);
dprintf("ERROR: mutex init failed, error code is %d!\n", rc);
goto ERROROUT;
}
/* 初始化cond条件变量 */
rc = pthread_cond_init(&g_td.cond, NULL);
if (rc != 0) {
printf("ERROR: pthread condition init failed, error code is %d!\n", rc);
dprintf("ERROR: pthread condition init failed, error code is %d!\n", rc);
goto ERROROUT;
}
......@@ -109,10 +109,11 @@ static int testcase(void)
for (i = 0; i < THREAD_NUM; i++) {
rc = pthread_create(&thread[i], NULL, ChildThreadFunc, NULL);
if (rc != 0) {
printf("ERROR: pthread create failed, error code is %d!\n", rc);
dprintf("ERROR: pthread create failed, error code is %d!\n", rc);
goto ERROROUT;
}
}
dprintf("pthread_create ok\n");
/* 等待所有子线程都完成mutex锁的获取 */
while (g_startNum < THREAD_NUM) {
......@@ -122,14 +123,14 @@ static int testcase(void)
/* 获取mutex锁,确保所有子线程都阻塞在pthread_cond_wait上 */
rc = pthread_mutex_lock(&g_td.mutex);
if (rc != 0) {
printf("ERROR: mutex lock failed, error code is %d\n", rc);
dprintf("ERROR: mutex lock failed, error code is %d\n", rc);
goto ERROROUT;
}
/* 释放mutex锁 */
rc = pthread_mutex_unlock(&g_td.mutex);
if (rc != 0) {
printf("ERROR: mutex unlock failed, error code is %d!\n", rc);
dprintf("ERROR: mutex unlock failed, error code is %d!\n", rc);
goto ERROROUT;
}
......@@ -137,7 +138,7 @@ static int testcase(void)
/* 在cond条件变量上广播信号 */
rc = pthread_cond_signal(&g_td.cond);
if (rc != 0) {
printf("ERROR: pthread condition failed, error code is %d!\n", rc);
dprintf("ERROR: pthread condition failed, error code is %d!\n", rc);
goto ERROROUT;
}
}
......@@ -146,52 +147,42 @@ static int testcase(void)
/* 检查是否所有子线程都已被唤醒 */
if (g_wakenNum != THREAD_NUM) {
printf("ERROR: not all threads awaken, only %d thread(s) awaken!\n", g_wakenNum);
dprintf("ERROR: not all threads awaken, only %d thread(s) awaken!\n", g_wakenNum);
goto ERROROUT;
}
dprintf("all threads awaked\n");
/* join所有子线程,即等待其结束 */
for (i = 0; i < THREAD_NUM; i++) {
rc = pthread_join(thread[i], NULL);
if (rc != 0) {
printf("ERROR: pthread join failed, error code is %d!\n", rc);
dprintf("ERROR: pthread join failed, error code is %d!\n", rc);
goto ERROROUT;
}
}
dprintf("all threads join ok\n");
/* 销毁cond条件变量 */
rc = pthread_cond_destroy(&g_td.cond);
if (rc != 0) {
printf("ERROR: pthread condition destroy failed, error code is %d!\n", rc);
dprintf("ERROR: pthread condition destroy failed, error code is %d!\n", rc);
goto ERROROUT;
}
return 0;
ERROROUT:
return -1;
}
```
/*
* 示例代码主函数
*/
int main(int argc, char *argv[])
{
int rc;
#### 验证结果
/* 启动测试函数 */
rc = testcase();
if (rc != 0) {
printf("ERROR: testcase failed!\n");
}
输出结果如下:
return 0;
}
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */
```
pthread_create ok
all threads awaked
all threads join ok
```
## 与Linux标准库差异
......@@ -209,20 +200,17 @@ int main(int argc, char *argv[])
### 内存
**h2与Linux mmap的差异**
**与Linux mmap的差异**
mmap接口原型为:void \*mmap (void \*addr, size_t length, int prot, int flags, int fd, off_t offset)。
其中,参数fd的生命周期实现与Linux glibc存在差异。具体体现在,glibc在成功调用mmap进行映射后,可以立即释放fd句柄。在OpenHarmony内核中,不允许用户在映射成功后立即关闭相关fd,只允许在取消映射munmap后再进行fd的close操作。如果用户不进行fd的close操作,操作系统将在进程退出时对该fd进行回收。
**h2代码举例**
**代码举例**
Linux目前支持的情况如下:
```
int main(int argc, char *argv[])
{
......@@ -239,7 +227,7 @@ int main(int argc, char *argv[])
perror("mmap");
exit(EXIT_FAILURE);
}
close(fd); /* OpenHarmony does not support closing fd immediately after the mapping is successful. */
close(fd); /* OpenHarmony does not support closing fd immediately after the mapping is successful. */
...
exit(EXIT_SUCCESS);
}
......@@ -247,7 +235,7 @@ int main(int argc, char *argv[])
OpenHarmony支持的情况如下:
```
int main(int argc, char *argv[])
{
......@@ -266,7 +254,7 @@ int main(int argc, char *argv[])
}
...
munmap(addr, length);
close(fd); /* Close fd after the munmap is canceled. */
close(fd); /* Close fd after the munmap is canceled. */
exit(EXIT_SUCCESS);
}
```
......
......@@ -16,19 +16,17 @@ OpenHarmony系统通过对ARMv6架构中的LDREX和STREX进行封装,向用户
- LDREX Rx, [Ry]
读取内存中的值,并标记对该段内存的独占访问:
- 读取寄存器Ry指向的4字节内存数据,保存到Rx寄存器中。
- 对Ry指向的内存区域添加独占访问标记。
- STREX Rf, Rx, [Ry]
检查内存是否有独占访问标记,如果有则更新内存值并清空标记,否则不更新内存:
- 有独占访问标记
- 将寄存器Rx中的值更新到寄存器Ry指向的内存。
- 标志寄存器Rf置为0。
- 将寄存器Rx中的值更新到寄存器Ry指向的内存。
- 标志寄存器Rf置为0。
- 没有独占访问标记
- 不更新内存。
- 标志寄存器Rf置为1。
- 不更新内存。
- 标志寄存器Rf置为1。
- 判断标志寄存器
- 标志寄存器为0时,退出循环,原子操作结束。
......@@ -40,36 +38,36 @@ OpenHarmony系统通过对ARMv6架构中的LDREX和STREX进行封装,向用户
### 接口说明
OpenHarmony LiteOS-A内核的原子操作模块提供下面几种功能,接口详细信息可以查看API参考
OpenHarmony LiteOS-A内核的原子操作模块提供以下几种功能
**表1** 原子操作接口说明
| 功能分类 | 接口**名称** | 描述 |
| -------- | -------- | -------- |
| 读 | LOS_AtomicRead | 读取32bit原子数据 |
| 读 | LOS_Atomic64Read | 读取64bit原子数据 |
| 写 | LOS_AtomicSet | 设置32bit原子数据 |
| 写 | LOS_Atomic64Set | 设置64bit原子数据 |
| 加 | LOS_AtomicAdd | 对32bit原子数据做加法 |
| 加 | LOS_Atomic64Add | 对64bit原子数据做加法 |
| 加 | LOS_AtomicInc | 对32bit原子数据做加1 |
| 加 | LOS_Atomic64Inc | 对64bit原子数据做加1 |
| 加 | LOS_AtomicIncRet | 对32bit原子数据做加1并返回 |
| 加 | LOS_Atomic64IncRet | 对64bit原子数据做加1并返回 |
| 减 | LOS_AtomicSub | 对32bit原子数据做减法 |
| 减 | LOS_Atomic64Sub | 对64bit原子数据做减法 |
| 减 | LOS_AtomicDec | 对32bit原子数据做减1 |
| 减 | LOS_Atomic64Dec | 对64bit原子数据做减1 |
| 减 | LOS_AtomicDecRet | 对32bit原子数据做减1并返回 |
| 减 | LOS_Atomic64DecRet | 对64bit原子数据做减1并返回 |
| 交换 | LOS_AtomicXchgByte | 交换8bit内存数据 |
| 交换 | LOS_AtomicXchg16bits | 交换16bit内存数据 |
| 交换 | LOS_AtomicXchg32bits | 交换32bit内存数据 |
| 交换 | LOS_AtomicXchg64bits | 交换64bit内存数据 |
| 先比较后交换 | LOS_AtomicCmpXchgByte | 比较相同后交换8bit内存数据 |
| 先比较后交换 | LOS_AtomicCmpXchg16bits | 比较相同后交换16bit内存数据 |
| 先比较后交换 | LOS_AtomicCmpXchg32bits | 比较相同后交换32bit内存数据 |
| 先比较后交换 | LOS_AtomicCmpXchg64bits | 比较相同后交换64bit内存数据 |
| 功能分类 | 接口**名称** | 描述 |
| ------------ | ----------------------- | --------------------------- |
| 读 | LOS_AtomicRead | 读取32bit原子数据 |
| 读 | LOS_Atomic64Read | 读取64bit原子数据 |
| 写 | LOS_AtomicSet | 设置32bit原子数据 |
| 写 | LOS_Atomic64Set | 设置64bit原子数据 |
| 加 | LOS_AtomicAdd | 对32bit原子数据做加法 |
| 加 | LOS_Atomic64Add | 对64bit原子数据做加法 |
| 加 | LOS_AtomicInc | 对32bit原子数据做加1 |
| 加 | LOS_Atomic64Inc | 对64bit原子数据做加1 |
| 加 | LOS_AtomicIncRet | 对32bit原子数据做加1并返回 |
| 加 | LOS_Atomic64IncRet | 对64bit原子数据做加1并返回 |
| 减 | LOS_AtomicSub | 对32bit原子数据做减法 |
| 减 | LOS_Atomic64Sub | 对64bit原子数据做减法 |
| 减 | LOS_AtomicDec | 对32bit原子数据做减1 |
| 减 | LOS_Atomic64Dec | 对64bit原子数据做减1 |
| 减 | LOS_AtomicDecRet | 对32bit原子数据做减1并返回 |
| 减 | LOS_Atomic64DecRet | 对64bit原子数据做减1并返回 |
| 交换 | LOS_AtomicXchgByte | 交换8bit内存数据 |
| 交换 | LOS_AtomicXchg16bits | 交换16bit内存数据 |
| 交换 | LOS_AtomicXchg32bits | 交换32bit内存数据 |
| 交换 | LOS_AtomicXchg64bits | 交换64bit内存数据 |
| 先比较后交换 | LOS_AtomicCmpXchgByte | 比较相同后交换8bit内存数据 |
| 先比较后交换 | LOS_AtomicCmpXchg16bits | 比较相同后交换16bit内存数据 |
| 先比较后交换 | LOS_AtomicCmpXchg32bits | 比较相同后交换32bit内存数据 |
| 先比较后交换 | LOS_AtomicCmpXchg64bits | 比较相同后交换64bit内存数据 |
### 开发流程
......@@ -77,7 +75,7 @@ OpenHarmony LiteOS-A内核的原子操作模块提供下面几种功能,接口
有多个任务对同一个内存数据进行加减或交换等操作时,使用原子操作保证结果的可预知性。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 原子操作接口仅支持整型数据。
> 原子操作接口仅支持整型数据。
### 编程实例
......@@ -96,7 +94,7 @@ OpenHarmony LiteOS-A内核的原子操作模块提供下面几种功能,接口
示例代码如下:
```
#include "los_hwi.h"
#include "los_atomic.h"
......@@ -159,7 +157,7 @@ UINT32 Example_AtomicTaskEntry(VOID)
**结果验证**
```
g_sum = 0
```
```
\ No newline at end of file
......@@ -3,16 +3,28 @@
## 基本概念
中断是指出现需要时,CPU暂停执行当前程序,转而执行新程序的过程。即在程序运行过程中,出现了一个必须由CPU立即处理的事务。此时,CPU暂时中止当前程序的执行转而处理这个事务,这个过程就叫做中断。通过中断机制,可以使CPU避免把大量时间耗费在等待、查询外设状态的操作上,大大提高系统实时性以及执行效率。
中断是指出现需要时,CPU暂停执行当前程序,转而执行新程序的过程。即在程序运行过程中,出现了一个必须由CPU立即处理的事务,此时CPU暂时中止当前程序的执行转而处理这个事务,这个过程就叫做中断。通过中断机制,可以使CPU避免把大量时间耗费在等待、查询外设状态的操作上,大大提高系统实时性以及执行效率。
目前的中断支持有
+ 中断初始化
+ 中断创建
+ 开/关中断
+ 恢复中断
+ 删除中断
异常处理是操作系统对运行期间发生的异常情况(芯片硬件异常)进行处理的一系列动作,例如虚拟内存缺页异常、打印异常发生时函数的调用栈信息、CPU现场信息、任务的堆栈情况等。
## 运行机制
外设可以在没有CPU介入的情况下完成一定的工作,但某些情况下也需要CPU为其执行一定的工作。通过中断机制,在外设不需要CPU介入时,CPU可以执行其它任务,而当外设需要CPU时,产生一个中断信号,该信号连接至中断控制器。中断控制器是一方面接收其它外设中断引脚的输入,另一方面它会发出中断信号给CPU。可以通过对中断控制器编程来打开和关闭中断源、设置中断源的优先级和触发方式。常用的中断控制器有VIC(Vector Interrupt Controller)和GIC(General Interrupt Controller)。在ARM Cortex-A7中使用的中断控制器是GIC。CPU收到中断控制器发送的中断信号后,中断当前任务来响应中断请求。
外设可以在没有CPU介入的情况下完成一定的工作,但某些情况下也需要CPU为其执行一定的工作。通过中断机制,在外设不需要CPU介入时,CPU可以执行其它任务,而当外设需要CPU时,产生一个中断信号,该信号连接至中断控制器。
中断控制器一方面接收其它外设中断引脚的输入,另一方面会发出中断信号给CPU。可以通过对中断控制器编程来打开和关闭中断源、设置中断源的优先级和触发方式。常用的中断控制器有VIC(Vector Interrupt Controller)和GIC(General Interrupt Controller)。在ARM Cortex-A7中使用的中断控制器是GIC。
异常处理就是可以打断CPU正常运行流程的一些事情,如未定义指令异常、试图修改只读的数据异常、不对齐的地址访问异常等。当异常发生时,CPU暂停当前的程序,先处理异常事件,然后再继续执行被异常打断的程序。
CPU收到中断控制器发送的中断信号后,中断当前任务来响应中断请求。
异常指可以打断CPU正常运行流程的一些事情,如未定义指令异常、试图修改只读的数据异常、不对齐的地址访问异常等。当异常发生时,CPU暂停当前的程序,先处理异常事件,然后再继续执行被异常打断的程序。
以ARMv7-a架构为例,中断和异常处理的入口为中断向量表,中断向量表包含各个中断和异常处理的入口函数。
......@@ -28,11 +40,27 @@
异常处理为内部机制,不对外提供接口,中断模块提供对外接口如下:
| 功能分类 | 接口描述 |
| -------- | -------- |
| 创建和删除中断 | -&nbsp;LOS_HwiCreate:中断创建,注册中断号、中断触发模式、中断优先级、中断处理程序。中断被触发时,会调用该中断处理程序<br/>-&nbsp;LOS_HwiDelete:删除中断 |
| 打开和关闭所有中断 | -&nbsp;LOS_IntUnLock:打开当前处理器所有中断响应<br/>-&nbsp;LOS_IntLock:关闭当前处理器所有中断响应<br/>-&nbsp;LOS_IntRestore:恢复到使用LOS_IntLock关闭所有中断之前的状态 |
| 获取系统支持的最大中断数 | LOS_GetSystemHwiMaximum:获取系统支持的最大中断数 |
##### 创建删除中断
| 接口名 | 接口描述 |
| :------------ | :----------------------------------------------------------- |
| LOS_HwiCreate | 中断创建,注册中断号、中断触发模式、中断优先级、中断处理程序。中断被触发时,会调用该中断处理程序 |
| LOS_HwiDelete | 根据所提供的中断号删除中断 |
##### 开/关中断
| 接口名 | 接口描述 |
| -------------- | ------------------------------------------- |
| LOS_IntUnlock | 打开当前处理器所有中断响应 |
| LOS_IntLock | 关闭当前处理器所有中断响应 |
| LOS_IntRestore | 与LOS_IntLock配套使用,恢复到使用LOS_IntLock关闭所有中断之前的状态 |
##### 获取系统中断信息
| 接口名 | 接口描述 |
| ----------------------- | ------------------------ |
| LOS_GetSystemHwiMaximum | 获取系统支持的最大中断数 |
### 开发流程
......@@ -53,32 +81,30 @@
2. 删除中断。
代码实现如下,演示如何创建中断和删除中断,当指定的中断号HWI_NUM_TEST产生中断时,会调用中断处理函数:
代码实现如下,演示如何创建中断和删除中断,当指定的中断号HWI_NUM_TEST产生中断时,会调用中断处理函数(该示例代码的测试函数可以加在kernel/liteos_a/testsuites/kernel/src/osTest.c中的TestTaskEntry中进行测试):
```
```c
#include "los_hwi.h"
/*中断处理函数*/
STATIC VOID HwiUsrIrq(VOID)
{
printf("in the func HwiUsrIrq \n");
PRINK("in the func HwiUsrIrq \n");
}
static UINT32 Example_Interrupt(VOID)
{
UINT32 ret;
HWI_HANDLE_T hwiNum = 7;
HWI_PRIOR_T hwiPrio = 3;
HWI_HANDLE_T hwiNum = 7; // 7: 使用的中断号
HWI_PRIOR_T hwiPrio = 3; // 3: 中断优先级
HWI_MODE_T mode = 0;
HWI_ARG_T arg = 0;
/*创建中断*/
ret = LOS_HwiCreate(hwiNum, hwiPrio, mode, (HWI_PROC_FUNC)HwiUsrIrq, (HwiIrqParam *)arg);
if(ret == LOS_OK){
printf("Hwi create success!\n");
if (ret == LOS_OK) {
PRINK("Hwi create success!\n");
} else {
printf("Hwi create failed!\n");
PRINK("Hwi create failed!\n");
return LOS_NOK;
}
......@@ -86,11 +112,11 @@ static UINT32 Example_Interrupt(VOID)
LOS_TaskDelay(50);
/*删除中断*/
ret = LOS_HwiDelete(hwiNum, (HwiIrqParam *)arg);
if(ret == LOS_OK){
printf("Hwi delete success!\n");
ret = LOS_HwiDelete(hwiNum, (HwiIrqParam *)arg);
if (ret == LOS_OK) {
PRINK("Hwi delete success!\n");
} else {
printf("Hwi delete failed!\n");
PRINK("Hwi delete failed!\n");
return LOS_NOK;
}
return LOS_OK;
......@@ -102,7 +128,6 @@ static UINT32 Example_Interrupt(VOID)
编译运行得到的结果为:
```
Hwi create success!
Hwi delete success!
......
......@@ -3,7 +3,7 @@
## 基本概念
进程是系统资源管理的最小单元。OpenHarmony LiteOS-A内核提供的进程模块主要用于实现用户态进程的隔离,内核态被视为一个进程空间,不存在其它进程(KIdle除外,KIdle进程是系统提供的空闲进程,和KProcess共享一个进程空间)。
进程是系统资源管理的最小单元。OpenHarmony LiteOS-A 内核提供的进程模块主要用于实现用户态进程的隔离,内核态被视为一个进程空间,不存在其它进程(KIdle除外,KIdle进程是系统提供的空闲进程,和KProcess共享一个进程空间。KProcess 是内核态进程的根进程,KIdle 是其子进程)。
- 进程模块主要为用户提供多个进程,实现了进程之间的切换和通信,帮助用户管理业务程序流程。
......@@ -36,10 +36,10 @@
**进程状态迁移说明:**
- Init→Ready:
进程创建或fork时,拿到该进程控制块后进入Init状态,处于进程初始化阶段,当进程初始化完成将进程插入调度队列,此时进程进入就绪状态。
进程创建或 fork 时,拿到对应进程控制块后进入 Init 状态,即进程初始化阶段,当该阶段完成后进程将被插入调度队列,此时进程进入就绪状态。
- Ready→Running:
进程创建后进入就绪态,发生进程切换时,就绪列表中最高优先级的进程被执行,从而进入运行态。若此时该进程中已无其它线程处于就绪态,则进程从就绪列表删除,只处于运行态;若此时该进程中还有其它线程处于就绪态,则该进程依旧在就绪队列,此时进程的就绪态和运行态共存,但对外呈现的进程状态为运行态。
进程创建后进入就绪态,发生进程切换时,就绪列表中优先级最高且获得时间片的进程被执行,从而进入运行态。若此时该进程中已无其它线程处于就绪态,则进程从就绪列表删除,只处于运行态;若此时该进程中还有其它线程处于就绪态,则该进程依旧在就绪队列,此时进程的就绪态和运行态共存,但对外呈现的进程状态为运行态。
- Running→Pending:
进程在最后一个线程转为阻塞态时, 进程内所有的线程均处于阻塞态,此时进程同步进入阻塞态,然后发生进程切换。
......@@ -54,7 +54,7 @@
进程由运行态转为就绪态的情况有以下两种:
1. 有更高优先级的进程创建或者恢复后,会发生进程调度,此刻就绪列表中最高优先级进程变为运行态,那么原先运行的进程由运行态变为就绪态。
2. 若进程的调度策略为LOS_SCHED_RR,且存在同一优先级的另一个进程处于就绪态,则该进程的时间片消耗光之后,该进程由运行态转为就绪态,另一个同优先级的进程由就绪态转为运行态。
2. 若进程的调度策略为 LOS_SCHED_RR(时间片轮转),且存在同一优先级的另一个进程处于就绪态,则该进程的时间片消耗光之后,该进程由运行态转为就绪态,另一个同优先级的进程由就绪态转为运行态。
- Running→Zombies:
当进程的主线程或所有线程运行结束后,进程由运行态转为僵尸态,等待父进程回收资源。
......@@ -78,16 +78,47 @@ OpenHarmony 提供的进程模块主要用于实现用户态进程的隔离,
### 接口说明
**表1** 进程管理模块接口
##### 表1 进程及进程组
| 接口名 | 接口描述 |
| ------------------------- | ---------------------- |
| LOS_GetCurrProcessID | 获取当前进程的进程ID |
| LOS_GetProcessGroupID | 获取指定进程的进程组ID |
| LOS_GetCurrProcessGroupID | 获取当前进程的进程组ID |
##### 表2 用户及用户组
| 接口名 | 接口描述 |
| ----------------- | ---------------------------------------- |
| LOS_GetUserID | 获取当前进程的用户ID |
| LOS_GetGroupID | 获取当前进程的用户组ID |
| LOS_CheckInGroups | 检查指定用户组ID是否在当前进程的用户组内 |
##### 表3 进程调度控制
| 接口名 | 接口 |
| ----------------------- | -------------------------------------------- |
| LOS_GetProcessScheduler | 获取指定进程的调度策略 |
| LOS_SetProcessScheduler | 设置指定进程的调度参数,包括优先级和调度策略 |
| LOS_SetProcessPriority | 设置进程优先级 |
| LOS_GetProcessPriority | 获取进程优先级 |
##### 表4 系统进程信息获取
| 接口名 | 接口描述 |
| --------------------------- | -------------------------- |
| LOS_GetSystemProcessMaximum | 获取系统支持的最大进程数目 |
| LOS_GetUsedPIDList | 获得已使用的进程ID列表 |
##### 表5 进程创建与结束
| 接口名 | 接口描述 |
| ---------- | -------------------------- |
| LOS_Fork | 创建子进程 |
| LOS_Wait | 等待子进程结束并回收子进程 |
| LOS_Waitid | 等待相应ID的进程结束 |
| LOS_Exit | 退出进程 |
| 功能分类 | 接口描述 |
| -------- | -------- |
| 进程调度参数控制 | -&nbsp;LOS_GetProcessScheduler:获取指定进程的调度策略<br/>-&nbsp;LOS_SetProcessScheduler:设置指定进程的调度参数,包括优先级和调度策略 |
| 等待回收子进程 | LOS_Wait:等待子进程结束并回收子进程 |
| 进程组 | -&nbsp;LOS_GetProcessGroupID:获取指定进程的进程组ID<br/>-&nbsp;LOS_GetCurrProcessGroupID:获取当前进程的进程组ID |
| 获取进程ID | LOS_GetCurrProcessID:获取当前进程的进程ID |
| 用户及用户组 | -&nbsp;LOS_GetUserID:获取当前进程的用户ID<br/>-&nbsp;LOS_GetGroupID:获取当前进程的用户组ID<br/>-&nbsp;LOS_CheckInGroups:检查指定用户组ID是否在当前进程的用户组内 |
| 系统支持的最大进程数 | LOS_GetSystemProcessMaximum:获取系统支持的最大进程数目 |
### 开发流程
......@@ -96,7 +127,7 @@ OpenHarmony 提供的进程模块主要用于实现用户态进程的隔离,
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> - idle线程的数量跟随CPU核心数,每个CPU均有一个相应的idle线程。
>
>
> - 不支持创建除KProcess和KIdle进程之外的其它内核态进程。
>
>
> - 用户态进程通过系统调用进入内核态后创建的线程属于KProcess, 不属于当前用户态进程。
......@@ -3,18 +3,18 @@
## 基本概念
OpenHarmony LiteOS-A内核 了高优先级优先+同优先级时间片轮转的抢占式调度机制,系统从启动开始基于real time的时间轴向前运行,使得该调度算法具有很好的实时性。
OpenHarmony LiteOS-A内核采用了高优先级优先 + 同优先级时间片轮转的抢占式调度机制,系统从启动开始基于real time的时间轴向前运行,使得该调度算法具有很好的实时性。
OpenHarmony 的调度算法将tickless机制天然嵌入到调度算法中,一方面使得系统具有更低的功耗,另一方面也使得tick中断按需响应,减少无用的tick中断响应,进一步提高系统的实时性。
OpenHarmony 的调度算法将 tickless 机制天然嵌入到调度算法中,一方面使得系统具有更低的功耗,另一方面也使得 tick 中断按需响应,减少无用的 tick 中断响应,进一步提高系统的实时性。
OpenHarmony 的进程调度策略支持SCHED_RR,线程调度策略支持SCHED_RR和SCHED_FIFO
OpenHarmony 的进程调度策略支持 SCHED_RR(时间片轮转),线程调度策略支持 SCHED_RR 和 SCHED_FIFO(先进先出)
OpenHarmony 调度的最小单元为线程。
## 运行机制
OpenHarmony 采用进程优先级队列+线程优先级队列的方式,进程优先级范围为0-31,共有32个进程优先级桶队列,每个桶队列对应一个线程优先级桶队列;线程优先级范围也为0-31,一个线程优先级桶队列也有32个优先级队列。
OpenHarmony 采用进程优先级队列 + 线程优先级队列的方式,进程优先级范围为0-31,共有32个进程优先级桶队列,每个桶队列对应一个线程优先级桶队列;线程优先级范围也为0-31,一个线程优先级桶队列也有32个优先级队列。
**图1** 调度优先级桶队列示意图
......@@ -31,9 +31,13 @@ OpenHarmony 在系统启动内核初始化之后开始调度,运行过程中
### 接口说明
| 功能分类 | 接口**名称** | 描述 |
| -------- | -------- | -------- |
| 触发系统调度 | LOS_Schedule | 触发系统调度 |
| 接口**名称** | 描述 |
| -------- | -------- |
| LOS_Schedule | 触发系统调度 |
| LOS_GetTaskScheduler | 获取指定任务的调度策略 |
| LOS_SetTaskScheduler | 设置指定任务的调度策略 |
| LOS_GetProcessScheduler | 获取指定进程的调度策略 |
| LOS_SetProcessScheduler | 设置指定进程的调度参数,包括优先级和调度策略 |
### 开发流程
......
......@@ -52,7 +52,7 @@ OpenHarmony 内核的任务一共有32个优先级(0-31),最高优先级为0
有更高优先级任务创建或者恢复后,会发生任务调度,此刻就绪列表中最高优先级任务变为运行态,那么原先运行的任务由运行态变为就绪态,并加入就绪列表中。
- Running→Exit:
运行中的任务运行结束,任务状态由运行态变为退出态。若为设置了分离属性(LOS_TASK_STATUS_DETACHED)的任务,运行结束后将直接销毁。
运行中的任务运行结束,任务状态由运行态变为退出态。若为设置了分离属性( 由头文件 los_task.h 中的宏定义 LOS_TASK_STATUS_DETACHED 设置)的任务,运行结束后将直接销毁。
## 运行机制
......@@ -67,16 +67,58 @@ OpenHarmony 任务管理模块提供任务创建、任务延时、任务挂起
### 接口说明
| 功能分类 | 接口描述 |
| -------- | -------- |
| 任务的创建和删除 | -&nbsp;LOS_TaskCreate:创建任务,并使该任务进入Init状态,不执行任务调度<br/>-&nbsp;LOS_TaskDelete:创建任务,并使该任务进入Ready状态,并调度<br/>-&nbsp;LOS_TaskDelete:删除指定的任务 |
| 任务状态控制 | -&nbsp;LOS_TaskResume:恢复挂起的任务<br/>-&nbsp;LOS_TaskSuspend:挂起指定的任务<br/>-&nbsp;LOS_TaskJoin:挂起当前任务,等待指定任务运行结束并回收其任务控制块资源<br/>-&nbsp;LOS_TaskDetach:修改任务的joinable属性为detach属性,detach属性的任务运行结束会自动回收任务控制块资源<br/>-&nbsp;LOS_TaskDelay:任务延时等待<br/>-&nbsp;LOS_TaskYield:显式放权,调整调用任务优先级的任务调度顺序 |
| 任务调度的控制 | -&nbsp;LOS_TaskLock:锁任务调度<br/>-&nbsp;LOS_TaskUnlock:解锁任务调度 |
| 任务优先级的控制 | -&nbsp;LOS_CurTaskPriSet:设置当前任务的优先级<br/>-&nbsp;LOS_TaskPriSet:设置指定任务的优先级<br/>-&nbsp;LOS_TaskPriGet:获取指定任务的优先级 |
| 任务信息获取 | -&nbsp;LOS_CurTaskIDGet:获取当前任务的ID<br/>-&nbsp;LOS_TaskInfoGet:获取指定任务的信息 |
| 任务绑核操作 | -&nbsp;LOS_TaskCpuAffiSet:绑定指定任务到指定CPU上运行,仅在多核下使用<br/>-&nbsp;LOS_TaskCpuAffiGet:获取指定任务的绑核信息,仅在多核下使用 |
| 任务调度参数的控制 | -&nbsp;LOS_GetTaskScheduler:获取指定任务的调度策略<br/>-&nbsp;LOS_SetTaskScheduler:设置指定任务的调度参数,包括优先级和调度策略 |
| 系统支持的最大任务数 | LOS_GetSystemTaskMaximum |
##### 表1 任务的创建和删除
| 接口名 | 接口描述 |
| ------------------ | ------------------------------------------------------------ |
| LOS_TaskCreate | 创建任务,若所创建任务的优先级比当前的运行的任务优先级高且任务调度没有锁定,<br/>则该任务将被调度进入运行态 |
| LOS_TaskCreateOnly | 创建任务并阻塞,任务恢复前不会将其加入就绪队列中 |
| LOS_TaskDelete | 删除指定的任务,回收其任务控制块和任务栈所消耗的资源 |
##### 表2 任务的状态控制
| 接口名 | 接口描述 |
| --------------- | ------------------------------------------------------------ |
| LOS_TaskResume | 恢复挂起的任务 |
| LOS_TaskSuspend | 挂起指定的任务,该任务将从就绪任务队列中移除 |
| LOS_TaskJoin | 阻塞当前任务,等待指定任务运行结束并回收其资源 |
| LOS_TaskDetach | 修改任务的 joinable 属性为 detach 属性,detach 属性的任务运行结束会自动回收任务控制块资源 |
| LOS_TaskDelay | 延迟当前任务的执行,在延后指定的时间(tick数)后可以被调度 |
| LOS_TaskYield | 将当前任务从具有相同优先级的任务队列,移动到就绪任务队列的末尾 |
##### 表3 任务调度
| 接口名 | 接口描述 |
| -------------------- | ------------------------------------------------------------ |
| LOS_TaskLock | 锁定任务调度,阻止任务切换 |
| LOS_TaskUnlock | 解锁任务调度。通过该接口可以使任务锁数量减1,若任务多次加锁,那么<br/>任务调度在锁数量减为0时才会完全解锁 |
| LOS_GetTaskScheduler | 获取指定任务的调度策略 |
| LOS_SetTaskScheduler | 设置指定任务的调度参数,包括优先级和调度策略 |
| LOS_Schedule | 触发主动的任务调度 |
##### 表4 任务相关信息获取
| 接口名 | 接口描述 |
| ------------------------ | ------------------------ |
| LOS_CurTaskIDGet | 获取当前任务的ID |
| LOS_TaskInfoGet | 获取指定任务的信息 |
| LOS_GetSystemTaskMaximum | 获取系统支持的最大任务数 |
##### 表5 任务优先级
| 接口名 | 接口描述 |
| ----------------- | ------------------------------ |
| LOS_CurTaskPriSet | 设置当前正在运行的任务的优先级 |
| LOS_TaskPriSet | 设置指定任务的优先级 |
| LOS_TaskPriGet | 获取指定任务的优先级 |
##### 表6 任务绑核操作
| 接口名 | 接口描述 |
| ------------------ | ------------------------------------------- |
| LOS_TaskCpuAffiSet | 绑定指定任务到指定CPU上运行,仅在多核下使用 |
| LOS_TaskCpuAffiGet | 获取指定任务的绑核信息,仅在多核下使用 |
### 开发流程
......@@ -93,78 +135,79 @@ OpenHarmony 任务管理模块提供任务创建、任务延时、任务挂起
2. 任务参与调度运行,执行用户指定的业务代码。
3. 任务执行结束,如果设置了LOS_TASK_STATUS_DETACHED属性,则自动回收任务资源,如果任务设置了LOS_TASK_ATTR_JOINABLE属性,则需要调用LOS_TaskJoin回收任务资源,默认为LOS_TASK_STATUS_DETACHED属性。
3. 任务执行结束,如果设置了 LOS_TASK_STATUS_DETACHED 属性,则自动回收任务资源,如果任务设置了 LOS_TASK_ATTR_JOINABLE 属性,则需要调用LOS_TaskJoin 回收任务资源,默认为 LOS_TASK_STATUS_DETACHED 属性。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> - 内核态具有最高权限,可以操作任意进程内的任务。
>
>
> - 用户态进程通过系统调用进入内核态后创建的任务属于KProcess, 不属于当前用户态进程。
### 编程实例
代码实现如下:
代码实现如下(该示例代码的测试函数可以加在 kernel /liteos_a/testsuites /kernel /src /osTest.c 中的 TestTaskEntry 中进行测试。)
```
```c
UINT32 g_taskLoID;
UINT32 g_taskHiID;
#define TSK_PRIOR_HI 4
#define TSK_PRIOR_LO 5
UINT32 ExampleTaskHi(VOID)
{
UINT32 g_taskHiID;
#define TSK_PRIOR_HI 4
#define TSK_PRIOR_LO 5
UINT32 ExampleTaskHi(VOID)
{
UINT32 ret;
PRINTK("Enter TaskHi Handler.\n");
/* 延时2个Tick,延时后该任务会挂起,执行剩余任务中最高优先级的任务(g_taskLoID任务) */
PRINTK("Enter TaskHi Handler.\n");
/* 延时2个Tick,延时后该任务会挂起,执行剩余任务中最高优先级的任务(g_taskLoID任务) */
ret = LOS_TaskDelay(2);
if (ret != LOS_OK) {
if (ret != LOS_OK) {
PRINTK("Delay Task Failed.\n");
return LOS_NOK;
}
/* 2个Tick时间到了后,该任务恢复,继续执行 */
PRINTK("TaskHi LOS_TaskDelay Done.\n");
/* 挂起自身任务 */
ret = LOS_TaskSuspend(g_taskHiID);
return LOS_NOK;
}
/* 2个Tick时间到了后,该任务恢复,继续执行 */
PRINTK("TaskHi LOS_TaskDelay Done.\n");
/* 挂起自身任务 */
ret = LOS_TaskSuspend(g_taskHiID);
if (ret != LOS_OK) {
PRINTK("Suspend TaskHi Failed.\n");
PRINTK("Suspend TaskHi Failed.\n");
return LOS_NOK;
}
PRINTK("TaskHi LOS_TaskResume Success.\n");
}
PRINTK("TaskHi LOS_TaskResume Success.\n");
return LOS_OK;
}
/* 低优先级任务入口函数 */
/* 低优先级任务入口函数 */
UINT32 ExampleTaskLo(VOID)
{
UINT32 ret;
PRINTK("Enter TaskLo Handler.\n");
{
UINT32 ret;
PRINTK("Enter TaskLo Handler.\n");
/* 延时2个Tick,延时后该任务会挂起,执行剩余任务中就高优先级的任务(背景任务) */
ret = LOS_TaskDelay(2);
if (ret != LOS_OK) {
PRINTK("Delay TaskLo Failed.\n");
return LOS_NOK;
}
ret = LOS_TaskDelay(2);
if (ret != LOS_OK) {
PRINTK("Delay TaskLo Failed.\n");
return LOS_NOK;
}
PRINTK("TaskHi LOS_TaskSuspend Success.\n");
/* 恢复被挂起的任务g_taskHiID */
ret = LOS_TaskResume(g_taskHiID);
if (ret != LOS_OK) {
PRINTK("Resume TaskHi Failed.\n");
return LOS_NOK;
}
PRINTK("TaskHi LOS_TaskDelete Success.\n");
}
PRINTK("TaskHi LOS_TaskDelete Success.\n");
return LOS_OK;
}
/* 任务测试入口函数,在里面创建优先级不一样的两个任务 */
UINT32 ExampleTaskCaseEntry(VOID)
{
UINT32 ret;
}
/* 任务测试入口函数,在里面创建优先级不一样的两个任务 */
UINT32 ExampleTaskCaseEntry(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S initParam = {0};
/* 锁任务调度 */
LOS_TaskLock();
PRINTK("LOS_TaskLock() Success!\n");
/* 高优先级任务的初始化参数,其资源回收需要其他任务调用 LOS_TaskJoin */
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleTaskHi;
initParam.usTaskPrio = TSK_PRIOR_HI;
initParam.usTaskPrio = TSK_PRIOR_HI;
initParam.pcName = "HIGH_NAME";
initParam.uwStackSize = LOS_TASK_MIN_STACK_SIZE;
initParam.uwResved = LOS_TASK_ATTR_JOINABLE;
......@@ -175,23 +218,24 @@ UINT32 ExampleTaskCaseEntry(VOID)
LOS_TaskUnlock();
PRINTK("ExampleTaskHi create Failed! ret=%d\n", ret);
return LOS_NOK;
}
}
PRINTK("ExampleTaskHi create Success!\n");
/* 低优先级任务的初始化参数,任务结束后会自行结束销毁 */
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleTaskLo;
initParam.usTaskPrio = TSK_PRIOR_LO;
initParam.pcName = "LOW_NAME";
initParam.uwStackSize = LOS_TASK_MIN_STACK_SIZE;
initParam.uwResved = LOS_TASK_STATUS_DETACHED;
/* 创建低优先级任务,由于锁任务调度,任务创建成功后不会马上执行 */
/* 创建低优先级任务,由于锁任务调度,任务创建成功后不会马上执行 */
ret = LOS_TaskCreate(&g_taskLoID, &initParam);
if (ret!= LOS_OK) {
LOS_TaskUnlock();
if (ret!= LOS_OK) {
LOS_TaskUnlock();
PRINTK("ExampleTaskLo create Failed!\n");
return LOS_NOK;
}
PRINTK("ExampleTaskLo create Success!\n");
return LOS_NOK;
}
PRINTK("ExampleTaskLo create Success!\n");
/* 解锁任务调度,此时会发生任务调度,执行就绪列表中最高优先级任务 */
LOS_TaskUnlock();
......@@ -203,12 +247,12 @@ UINT32 ExampleTaskCaseEntry(VOID)
}
while(1){};
return LOS_OK;
}
}
```
编译运行得到的结果为:
```
LOS_TaskLock() Success!
ExampleTaskHi create Success!
......
......@@ -3,9 +3,13 @@
## 基本概念
软件定时器,是基于系统Tick时钟中断且由软件来模拟的定时器,当经过设定的Tick时钟计数值后会触发用户定义的回调函数。定时精度与系统Tick时钟的周期有关。硬件定时器受硬件的限制,数量上不足以满足用户的实际需求,因此为了满足用户需求,提供更多的定时器,OpenHarmony LiteOS-A内核提供软件定时器功能。软件定时器扩展了定时器的数量,允许创建更多的定时业务。
软件定时器,是基于系统Tick时钟中断且由软件来模拟的定时器,当经过设定的Tick时钟计数值后会触发用户定义的回调函数。定时精度与系统Tick时钟的周期有关。
软件定时器功能上支持:
硬件定时器受硬件的限制,数量上不足以满足用户的实际需求,因此为了满足用户需求,提供更多的定时器,OpenHarmony LiteOS-A内核提供软件定时器功能。
软件定时器扩展了定时器的数量,允许创建更多的定时业务。
**软件定时器支持以下功能:**
- 静态裁剪:能通过宏关闭软件定时器功能。
......@@ -22,13 +26,17 @@
## 运行机制
软件定时器是系统资源,在模块初始化的时候已经分配了一块连续的内存,系统支持的最大定时器个数由los_config.h中的LOSCFG_BASE_CORE_SWTMR_LIMIT宏配置。软件定时器使用了系统的一个队列和一个任务资源,软件定时器的触发遵循队列规则,先进先出。同一时刻设置的定时时间短的定时器总是比定时时间长的靠近队列头,满足优先被触发的准则。软件定时器以Tick为基本计时单位,当用户创建并启动一个软件定时器时,OpenHarmony系统会根据当前系统Tick时间及用户设置的定时间隔确定该定时器的到期Tick时间,并将该定时器控制结构挂入计时全局链表。
软件定时器是系统资源,在模块初始化的时候已经分配了一块连续的内存,系统支持的最大定时器个数由los_config.h中的LOSCFG_BASE_CORE_SWTMR_LIMIT宏配置。
软件定时器使用了系统的一个队列和一个任务资源,软件定时器的触发遵循队列规则,先进先出。同一时刻设置的定时时间短的定时器总是比定时时间长的靠近队列头,满足优先被触发的准则。
软件定时器以Tick为基本计时单位,当用户创建并启动一个软件定时器时,OpenHarmony系统会根据当前系统Tick时间及用户设置的定时间隔确定该定时器的到期Tick时间,并将该定时器控制结构挂入计时全局链表。
当Tick中断到来时,在Tick中断处理函数中扫描软件定时器的计时全局链表,看是否有定时器超时,若有则将超时的定时器记录下来。
Tick中断处理函数结束后,软件定时器任务(优先级为最高)被唤醒,在该任务中调用之前记录下来的定时器的超时回调函数。
定时器状态
**定时器状态:**
- OS_SWTMR_STATUS_UNUSED(未使用)
系统在定时器模块初始化的时候将系统中所有定时器资源初始化成该状态。
......@@ -39,9 +47,7 @@ Tick中断处理函数结束后,软件定时器任务(优先级为最高)
- OS_SWTMR_STATUS_TICKING(计数)
在定时器创建后调用LOS_SwtmrStart接口,定时器将变成该状态,表示定时器运行时的状态。
定时器模式
OpenHarmony系统的软件定时器提供三类定时器机制:
**定时器模式:**
- 第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动删除。
......@@ -55,20 +61,20 @@ OpenHarmony系统的软件定时器提供三类定时器机制:
### 接口说明
OpenHarmony LiteOS-A内核的软件定时器模块提供下面几种功能,接口详细信息可以查看API参考
OpenHarmony LiteOS-A内核的软件定时器模块提供以下几种功能
**表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数 |
### 开发流程
软件定时器的典型开发流程:
**软件定时器的典型开发流程:**
1. 配置软件定时器。
- 确认配置项LOSCFG_BASE_CORE_SWTMR和LOSCFG_BASE_IPC_QUEUE为打开状态;
......@@ -88,14 +94,15 @@ OpenHarmony LiteOS-A内核的软件定时器模块提供下面几种功能,接
6. 删除定时器LOS_SwtmrDelete。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
>
> - 软件定时器的回调函数中不要做过多操作,不要使用可能引起任务挂起或者阻塞的接口或操作。
>
>
> - 软件定时器使用了系统的一个队列和一个任务资源,软件定时器任务的优先级设定为0,且不允许修改 。
>
>
> - 系统可配置的软件定时器资源个数是指:整个系统可使用的软件定时器资源总个数,而并非是用户可使用的软件定时器资源个数。例如:系统软件定时器多占用一个软件定时器资源数,那么用户能使用的软件定时器资源就会减少一个。
>
>
> - 创建单次软件定时器,该定时器超时执行完回调函数后,系统会自动删除该软件定时器,并回收资源。
>
>
> - 创建单次不自删除属性的定时器,用户需要调用定时器删除接口删除定时器,回收定时器资源,避免资源泄露。
......@@ -110,7 +117,7 @@ OpenHarmony LiteOS-A内核的软件定时器模块提供下面几种功能,接
- 配置好OS_SWTMR_HANDLE_QUEUE_SIZE软件定时器队列最大长度。
**编程示例**
```
#include "los_swtmr.h"
......@@ -176,7 +183,7 @@ void Timer_example(void)
**运行结果**
```
create Timer1 success
start Timer1 success
......@@ -206,4 +213,4 @@ g_timercount2 =9
tick_last1=2101
g_timercount2 =10
tick_last1=2201
```
```
\ No newline at end of file
......@@ -3,9 +3,11 @@
## 基本概念
时间管理以系统时钟为基础。时间管理提供给应用程序所有和时间有关的服务。系统时钟是由定时/计数器产生的输出脉冲触发中断而产生的,一般定义为整数或长整数。输出脉冲的周期叫做一个“时钟滴答”。系统时钟也称为时标或者Tick。一个Tick的时长可以静态配置。用户是以秒、毫秒为单位计时,而操作系统时钟计时是以Tick为单位的,当用户需要对系统操作时,例如任务挂起、延时等,输入秒为单位的数值,此时需要时间管理模块对二者进行转换。
时间管理以系统时钟为基础。时间管理提供给应用程序所有和时间有关的服务。系统时钟是由定时/计数器产生的输出脉冲触发中断而产生的,一般定义为整数或长整数。输出脉冲的周期叫做一个“时钟滴答”。
Tick与秒之间的对应关系可以配置。
系统时钟也称为时标或者Tick。一个Tick的时长可以静态配置。用户是以秒、毫秒为单位计时,而操作系统时钟计时是以Tick为单位的,当用户需要对系统操作时,例如任务挂起、延时等,输入秒为单位的数值,此时需要时间管理模块对二者进行转换。
**Tick与秒之间的对应关系可以配置。**
- **Cycle**
系统最小的计时单位。Cycle的时长由系统主频决定,系统主频就是每秒钟的Cycle数。
......@@ -13,24 +15,24 @@ Tick与秒之间的对应关系可以配置。
- **Tick**
Tick是操作系统的基本时间单位,对应的时长由系统主频及每秒Tick数决定,由用户配置。
OpenHarmony系统的时间管理模块提供时间转换、统计、延迟功能以满足用户对时间相关需求的实现。
**OpenHarmony系统的时间管理模块提供时间转换、统计、延迟功能以满足用户对时间相关需求的实现。**
## 开发指导
用户需要了解当前系统运行的时间以及Tick与秒、毫秒之间的转换关系时,需要使用到时间管理模块的接口。
用户需要了解当前系统运行的时间以及Tick与秒、毫秒之间的转换关系,以及需要使用到时间管理模块的接口。
### 接口说明
OpenHarmony LiteOS-A内核的时间管理提供下面几种功能,接口详细信息可以查看API参考。
OpenHarmony LiteOS-A内核的时间管理提供以下几种功能,接口详细信息可查看API参考。
**表1** 时间管理相关接口说明
**表1** 时间管理相关接口说明
| 功能分类 | 接口描述 |
| -------- | -------- |
| 时间转换 | LOS_MS2Tick:毫秒转换成Tick<br/>LOS_Tick2MS:Tick转换成毫秒 |
| 时间统计 | LOS_TickCountGet:获取当前Tick数<br/>LOS_CyclePerTickGet:每个Tick的cycle数 |
| 功能分类 | 接口描述 |
| -------- | ------------------------------------------------------------ |
| 时间转换 | LOS_MS2Tick:毫秒转换成Tick<br/>LOS_Tick2MS:Tick转换成毫秒 |
| 时间统计 | LOS_TickCountGet:获取当前Tick数<br/>LOS_CyclePerTickGet:每个Tick的cycle数 |
### 开发流程
......@@ -40,25 +42,26 @@ OpenHarmony LiteOS-A内核的时间管理提供下面几种功能,接口详细
2. 获取系统Tick数完成时间统计等。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> - 获取系统Tick数需要在系统时钟使能之后。
>
> - 时间管理不是单独的功能模块,依赖于los_config.h中的OS_SYS_CLOCK和LOSCFG_BASE_CORE_TICK_PER_SECOND两个配置选项。
>
> - 系统的Tick数在关中断的情况下不进行计数,故系统Tick数不能作为准确时间计算。
>
> - 获取系统Tick数需要在系统时钟使能之后。
>
> - 时间管理不是单独的功能模块,依赖于los_config.h中的OS_SYS_CLOCK和LOSCFG_BASE_CORE_TICK_PER_SECOND两个配置选项。
>
> - 系统的Tick数在关中断的情况下不进行计数,故系统Tick数不能作为准确时间计算。
### 编程实例
前置条件:
- 配置好LOSCFG_BASE_CORE_TICK_PER_SECOND,即系统每秒的Tick数,范围(0, 1000]
- 配置好LOSCFG_BASE_CORE_TICK_PER_SECOND,即系统每秒的Tick数,范围(0, 1000
- 配置好OS_SYS_CLOCK 系统时钟频率,单位:Hz。
**示例代码**
时间转换:
```
VOID Example_TransformTime(VOID)
{
......@@ -73,7 +76,7 @@ VOID Example_TransformTime(VOID)
时间统计和时间延迟:
```
VOID Example_GetTime(VOID)
{
......@@ -106,7 +109,7 @@ VOID Example_GetTime(VOID)
时间转换:
```
uwTick = 10000
uwMs = 100
......@@ -114,9 +117,9 @@ uwMs = 100
时间统计和时间延迟:
```
LOS_CyclePerTickGet = 49500
LOS_TickCountGet = 5042
LOS_TickCountGet after delay = 5242
```
LOS_TickCountGet = 347931
LOS_TickCountGet after delay = 348134
```
\ No newline at end of file
......@@ -15,7 +15,7 @@ OpenHarmony LiteOS-A的事件模块提供的事件,具有如下特点:
- 任务通过创建事件控制块来触发事件或等待事件。
- 事件间相互独立,内部实现为一个32位无符号整型,每一位标识一种事件类型。第25位不可用,因此最多可支持31种事件类型。
- 事件间相互独立,内部实现为一个32位无符号整型,每一位标识一种事件类型。(0表示该时间类型未发生,1表示该事件类型已经发生,一共31种事件类型,第25bit位(`0x02U << 24`)系统保留)
- 事件仅用于任务间的同步,不提供数据传输功能。
......@@ -31,7 +31,7 @@ OpenHarmony LiteOS-A的事件模块提供的事件,具有如下特点:
### 事件控制块
```
/**
* 事件控制块数据结构
......@@ -80,10 +80,10 @@ OpenHarmony LiteOS-A内核的事件模块提供下面几种功能。
| 功能分类 | 接口描述 |
| -------- | -------- |
| 初始化事件 | LOS_EventInit:初始化一个事件控制块 |
| 读/写事件 | -&nbsp;LOS_EventRead:读取指定事件类型,超时时间为相对时间:单位为Tick<br/>-&nbsp;LOS_EventWrite:写指定的事件类型 |
| 读/写事件 | -&nbsp;LOS_EventRead:读取指定事件类型,超时时间为相对时间:单位为Tick<br/>-&nbsp;LOS_EventWrite写指定的事件类型 |
| 清除事件 | LOS_EventClear:清除指定的事件类型 |
| 校验事件掩码 | -&nbsp;LOS_EventPoll:根据用户传入的事件ID、事件掩码及读取模式,返回用户传入的事件是否符合预期<br/>-&nbsp;LOS_EventDestroy:销毁指定的事件控制块 |
| 销毁事件 | LOS_EventDestroy:销毁指定的事件控制块 |
| 校验事件掩码 | -&nbsp;LOS_EventPoll根据用户传入的事件ID、事件掩码及读取模式,返回用户传入的事件是否符合预期<br/>-&nbsp;LOS_EventDestroy:销毁指定的事件控制块 |
| 销毁事件 | LOS_EventDestroy销毁指定的事件控制块 |
### 开发流程
......@@ -103,7 +103,7 @@ OpenHarmony LiteOS-A内核的事件模块提供下面几种功能。
6. 事件控制块销毁
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> - 进行事件读写操作时,事件的第25位为保留位,不可以进行位设置。
> - 进行事件读写操作时,事件的第25bit(`0x02U << 24`)为保留bit位,不可以进行位设置。
>
> - 对同一事件反复写入,算作一次写入。
......@@ -128,9 +128,10 @@ OpenHarmony LiteOS-A内核的事件模块提供下面几种功能。
### 编程示例
本演示代码在./kernel/liteos_a/testsuites/kernel/src/osTest.c中编译验证,在TestTaskEntry中调用验证入口函数Example_EventEntry。
示例代码如下:
```
#include "los_event.h"
#include "los_task.h"
......@@ -143,21 +144,21 @@ UINT32 g_testTaskId;
EVENT_CB_S g_exampleEvent;
/* 等待的事件类型 */
#define EVENT_WAIT 0x00000001
#define EVENT_WAIT 0x00000001
#define EVENT_TIMEOUT 500
/* 用例任务入口函数 */
VOID Example_Event(VOID)
{
UINT32 event;
/* 超时等待方式读事件,超时时间为100 ticks, 若100 ticks后未读取到指定事件,读事件超时,任务直接唤醒 */
printf("Example_Event wait event 0x%x \n", EVENT_WAIT);
dprintf("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);
dprintf("Example_Event,read event :0x%x\n", event);
} else {
printf("Example_Event,read event timeout\n");
dprintf("Example_Event,read event timeout\n");
}
}
......@@ -169,7 +170,7 @@ UINT32 Example_EventEntry(VOID)
/* 事件初始化 */
ret = LOS_EventInit(&g_exampleEvent);
if (ret != LOS_OK) {
printf("init event failed .\n");
dprintf("init event failed .\n");
return -1;
}
......@@ -181,30 +182,23 @@ UINT32 Example_EventEntry(VOID)
task1.usTaskPrio = 5;
ret = LOS_TaskCreate(&g_testTaskId, &task1);
if (ret != LOS_OK) {
printf("task create failed.\n");
dprintf("task create failed.\n");
return LOS_NOK;
}
/* 写g_testTaskId 等待事件 */
printf("Example_TaskEntry write event.\n");
dprintf("Example_TaskEntry write event.\n");
ret = LOS_EventWrite(&g_exampleEvent, EVENT_WAIT);
if (ret != LOS_OK) {
printf("event write failed.\n");
dprintf("event write failed.\n");
return LOS_NOK;
}
/* 清标志位 */
printf("EventMask:%d\n", g_exampleEvent.uwEventID);
dprintf("EventMask:%d\n", g_exampleEvent.uwEventID);
LOS_EventClear(&g_exampleEvent, ~g_exampleEvent.uwEventID);
printf("EventMask:%d\n", g_exampleEvent.uwEventID);
/* 删除任务 */
ret = LOS_TaskDelete(g_testTaskId);
if (ret != LOS_OK) {
printf("task delete failed.\n");
return LOS_NOK;
}
dprintf("EventMask:%d\n", g_exampleEvent.uwEventID);
return LOS_OK;
}
......@@ -215,7 +209,7 @@ UINT32 Example_EventEntry(VOID)
编译运行得到的结果为:
```
Example_Event wait event 0x1
Example_TaskEntry write event.
......
......@@ -87,7 +87,7 @@
### 编程实例
**实例描述**
#### 实例描述
本实例实现如下流程:
......@@ -99,17 +99,18 @@
4. 100Tick休眠时间到达后,Example_MutexTask2被唤醒, 释放互斥锁,唤醒Example_MutexTask1。Example_MutexTask1成功获取到互斥锁后,释放,删除互斥锁。
**示例代码**
#### 编程示例
本演示代码在./kernel/liteos_a/testsuites/kernel/src/osTest.c中编译验证,在TestTaskEntry中调用验证入口函数Example_MutexEntry。
示例代码如下:
```
#include <string.h>
#include "los_mux.h"
/* 互斥锁 */
LosMux g_testMux;
LosMux g_testMutex;
/* 任务ID */
UINT32 g_testTaskId01;
UINT32 g_testTaskId02;
......@@ -117,48 +118,49 @@ UINT32 g_testTaskId02;
VOID Example_MutexTask1(VOID)
{
UINT32 ret;
LOS_TaskDelay(50);
printf("task1 try to get mutex, wait 10 ticks.\n");
dprintf("task1 try to get mutex, wait 10 ticks.\n");
/* 申请互斥锁 */
ret = LOS_MuxLock(&g_testMux, 10);
ret = LOS_MuxLock(&g_testMutex, 10);
if (ret == LOS_OK) {
printf("task1 get mutex g_testMux.\n");
dprintf("task1 get mutex g_testMux.\n");
/* 释放互斥锁 */
LOS_MuxUnlock(&g_testMux);
LOS_MuxUnlock(&g_testMutex);
return;
}
if (ret == LOS_ETIMEDOUT ) {
printf("task1 timeout and try to get mutex, wait forever.\n");
/* 申请互斥锁 */
ret = LOS_MuxLock(&g_testMux, LOS_WAIT_FOREVER);
if (ret == LOS_OK) {
printf("task1 wait forever, get mutex g_testMux.\n");
/* 释放互斥锁 */
LOS_MuxUnlock(&g_testMux);
/* 删除互斥锁 */
LOS_MuxDestroy(&g_testMux);
printf("task1 post and delete mutex g_testMux.\n");
return;
}
}
if (ret == LOS_ETIMEDOUT) {
dprintf("task1 timeout and try to get mutex, wait forever.\n");
/* 申请互斥锁 */
ret = LOS_MuxLock(&g_testMutex, LOS_WAIT_FOREVER);
if (ret == LOS_OK) {
dprintf("task1 wait forever, get mutex g_testMux.\n");
/* 释放互斥锁 */
LOS_MuxUnlock(&g_testMutex);
/* 删除互斥锁 */
LOS_MuxDestroy(&g_testMutex);
dprintf("task1 post and delete mutex g_testMux.\n");
return;
}
}
return;
}
VOID Example_MutexTask2(VOID)
{
printf("task2 try to get mutex, wait forever.\n");
dprintf("task2 try to get mutex, wait forever.\n");
/* 申请互斥锁 */
(VOID)LOS_MuxLock(&g_testMux, LOS_WAIT_FOREVER);
(VOID)LOS_MuxLock(&g_testMutex, LOS_WAIT_FOREVER);
printf("task2 get mutex g_testMux and suspend 100 ticks.\n");
dprintf("task2 get mutex g_testMux and suspend 100 ticks.\n");
/* 任务休眠100Ticks */
LOS_TaskDelay(100);
printf("task2 resumed and post the g_testMux\n");
dprintf("task2 resumed and post the g_testMux\n");
/* 释放互斥锁 */
LOS_MuxUnlock(&g_testMux);
LOS_MuxUnlock(&g_testMutex);
return;
}
......@@ -169,7 +171,7 @@ UINT32 Example_MutexEntry(VOID)
TSK_INIT_PARAM_S task2;
/* 初始化互斥锁 */
LOS_MuxInit(&g_testMux, NULL);
LOS_MuxInit(&g_testMutex, NULL);
/* 锁任务调度 */
LOS_TaskLock();
......@@ -182,7 +184,7 @@ UINT32 Example_MutexEntry(VOID)
task1.usTaskPrio = 5;
ret = LOS_TaskCreate(&g_testTaskId01, &task1);
if (ret != LOS_OK) {
printf("task1 create failed.\n");
dprintf("task1 create failed.\n");
return LOS_NOK;
}
......@@ -194,7 +196,7 @@ UINT32 Example_MutexEntry(VOID)
task2.usTaskPrio = 4;
ret = LOS_TaskCreate(&g_testTaskId02, &task2);
if (ret != LOS_OK) {
printf("task2 create failed.\n");
dprintf("task2 create failed.\n");
return LOS_NOK;
}
......@@ -209,11 +211,11 @@ UINT32 Example_MutexEntry(VOID)
编译运行得到的结果为:
```
task1 try to get mutex, wait 10 ticks.
task2 try to get mutex, wait forever.
task2 get mutex g_testMux and suspend 100 ticks.
task1 try to get mutex, wait 10 ticks.
task1 timeout and try to get mutex, wait forever.
task2 resumed and post the g_testMux
task1 wait forever, get mutex g_testMux.
......
......@@ -7,7 +7,7 @@
任务能够从队列里面读取消息,当队列中的消息为空时,挂起读取任务;当队列中有新消息时,挂起的读取任务被唤醒并处理新消息。任务也能够往队列里写入消息,当队列已经写满消息时,挂起写入任务;当队列中有空闲消息节点时,挂起的写入任务被唤醒并写入消息。
可以通过调整读队列和写队列的超时时间来调整读写接口的阻塞模式,如果将读队列和写队列的超时时间设置为0,就不会挂起任务,接口会直接返回,这就是非阻塞模式。反之,如果将队列和写队列的超时时间设置为大于0的时间,就会以阻塞模式运行。
可以通过调整读队列和写队列的超时时间来调整读写接口的阻塞模式,如果将读队列和写队列的超时时间设置为0,就不会挂起任务,接口会直接返回,这就是非阻塞模式。反之,如果将队列和写队列的超时时间设置为大于0的时间,就会以阻塞模式运行。
消息队列提供了异步处理机制,允许将一个消息放入队列,但不立即处理。同时队列还有缓冲消息的作用,可以使用队列实现任务异步通信,队列具有如下特性:
......@@ -31,7 +31,7 @@
### 队列控制块
```
/**
* 队列控制块数据结构
......@@ -137,9 +137,12 @@ typedef struct {
### 编程示例
本演示代码在./kernel/liteos_a/testsuites/kernel/src/osTest.c中编译验证,在TestTaskEntry中调用验证入口函数ExampleQueue,
为方便用户观察,建议调用ExampleQueue前先调用 LOS_Msleep(5000) 进行短时间延时,避免其他打印过多。
示例代码如下:
```
#include "los_task.h"
#include "los_queue.h"
......@@ -154,7 +157,7 @@ VOID SendEntry(VOID)
ret = LOS_QueueWriteCopy(g_queue, abuf, len, 0);
if(ret != LOS_OK) {
printf("send message failure, error: %x\n", ret);
dprintf("send message failure, error: %x\n", ret);
}
}
......@@ -164,30 +167,36 @@ VOID RecvEntry(VOID)
CHAR readBuf[BUFFER_LEN] = {0};
UINT32 readLen = BUFFER_LEN;
//休眠1s
usleep(1000000);
LOS_Msleep(1000);
ret = LOS_QueueReadCopy(g_queue, readBuf, &readLen, 0);
if(ret != LOS_OK) {
printf("recv message failure, error: %x\n", ret);
dprintf("recv message failure, error: %x\n", ret);
}
printf("recv message: %s\n", readBuf);
dprintf("recv message: %s\n", readBuf);
ret = LOS_QueueDelete(g_queue);
if(ret != LOS_OK) {
printf("delete the queue failure, error: %x\n", ret);
dprintf("delete the queue failure, error: %x\n", ret);
}
printf("delete the queue success!\n");
dprintf("delete the queue success!\n");
}
UINT32 ExampleQueue(VOID)
{
printf("start queue example\n");
dprintf("start queue example\n");
UINT32 ret = 0;
UINT32 task1, task2;
TSK_INIT_PARAM_S initParam = {0};
ret = LOS_QueueCreate("queue", 5, &g_queue, 0, 50);
if(ret != LOS_OK) {
dprintf("create queue failure, error: %x\n", ret);
}
dprintf("create the queue success!\n");
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)SendEntry;
initParam.usTaskPrio = 9;
initParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
......@@ -196,7 +205,8 @@ UINT32 ExampleQueue(VOID)
LOS_TaskLock();
ret = LOS_TaskCreate(&task1, &initParam);
if(ret != LOS_OK) {
printf("create task1 failed, error: %x\n", ret);
dprintf("create task1 failed, error: %x\n", ret);
LOS_QueueDelete(g_queue);
return ret;
}
......@@ -204,17 +214,13 @@ UINT32 ExampleQueue(VOID)
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)RecvEntry;
ret = LOS_TaskCreate(&task2, &initParam);
if(ret != LOS_OK) {
printf("create task2 failed, error: %x\n", ret);
dprintf("create task2 failed, error: %x\n", ret);
LOS_QueueDelete(g_queue);
return ret;
}
ret = LOS_QueueCreate("queue", 5, &g_queue, 0, 50);
if(ret != LOS_OK) {
printf("create queue failure, error: %x\n", ret);
}
printf("create the queue success!\n");
LOS_TaskUnlock();
LOS_Msleep(5000);
return ret;
}
```
......@@ -224,9 +230,9 @@ UINT32 ExampleQueue(VOID)
编译运行得到的结果为:
```
start test example
start queue example
create the queue success!
recv message: test message
delete the queue success!
......
......@@ -22,7 +22,7 @@
**信号量控制块**
```
/**
* 信号量控制块数据结构
......@@ -116,9 +116,10 @@ typedef struct {
### 编程示例
本演示代码在./kernel/liteos_a/testsuites/kernel/src/osTest.c中编译验证,在TestTaskEntry中调用验证入口函数ExampleSem。
示例代码如下:
```
#include "los_sem.h"
#include "securec.h"
......@@ -128,7 +129,8 @@ static UINT32 g_testTaskId01;
static UINT32 g_testTaskId02;
/* 测试任务优先级 */
#define TASK_PRIO_TEST 5
#define TASK_PRIO_LOW 5
#define TASK_PRIO_HI 4
/* 信号量结构体id */
static UINT32 g_semId;
......@@ -137,11 +139,10 @@ VOID ExampleSemTask1(VOID)
{
UINT32 ret;
printf("ExampleSemTask1 try get sem g_semId, timeout 10 ticks.\n");
dprintf("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);
......@@ -149,12 +150,13 @@ VOID ExampleSemTask1(VOID)
}
/* 定时时间到,未申请到信号量 */
if (ret == LOS_ERRNO_SEM_TIMEOUT) {
printf("ExampleSemTask1 timeout and try get sem g_semId wait forever.\n");
dprintf("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");
dprintf("ExampleSemTask1 wait_forever and get sem g_semId.\n");
if (ret == LOS_OK) {
dprintf("ExampleSemTask1 post sem g_semId.\n");
LOS_SemPost(g_semId);
return;
}
......@@ -164,19 +166,18 @@ VOID ExampleSemTask1(VOID)
VOID ExampleSemTask2(VOID)
{
UINT32 ret;
printf("ExampleSemTask2 try get sem g_semId wait forever.\n");
dprintf("ExampleSemTask2 try get sem g_semId wait forever.\n");
/* 永久阻塞模式申请信号量 */
ret = LOS_SemPend(g_semId, LOS_WAIT_FOREVER);
if (ret == LOS_OK) {
printf("ExampleSemTask2 get sem g_semId and then delay 20 ticks.\n");
dprintf("ExampleSemTask2 get sem g_semId and then delay 20 ticks.\n");
}
/* 任务休眠20 ticks */
LOS_TaskDelay(20);
printf("ExampleSemTask2 post sem g_semId.\n");
dprintf("ExampleSemTask2 post sem g_semId.\n");
/* 释放信号量 */
LOS_SemPost(g_semId);
return;
......@@ -199,10 +200,10 @@ UINT32 ExampleSem(VOID)
task1.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleSemTask1;
task1.pcName = "TestTask1";
task1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task1.usTaskPrio = TASK_PRIO_TEST;
task1.usTaskPrio = TASK_PRIO_LOW;
ret = LOS_TaskCreate(&g_testTaskId01, &task1);
if (ret != LOS_OK) {
printf("task1 create failed .\n");
dprintf("task1 create failed .\n");
return LOS_NOK;
}
......@@ -211,16 +212,19 @@ UINT32 ExampleSem(VOID)
task2.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleSemTask2;
task2.pcName = "TestTask2";
task2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task2.usTaskPrio = (TASK_PRIO_TEST - 1);
task2.usTaskPrio = TASK_PRIO_HI;
ret = LOS_TaskCreate(&g_testTaskId02, &task2);
if (ret != LOS_OK) {
printf("task2 create failed.\n");
dprintf("task2 create failed.\n");
return LOS_NOK;
}
/* 解锁任务调度 */
LOS_TaskUnlock();
/* 任务休眠400 ticks */
LOS_TaskDelay(400);
ret = LOS_SemPost(g_semId);
/* 任务休眠400 ticks */
......@@ -237,12 +241,13 @@ 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.
ExampleSemTask1 timeout and try get sem g_semId wait forever.
ExampleSemTask2 get sem g_semId and then delay 20 ticks.
ExampleSemTask2 post sem g_semId.
ExampleSemTask1 wait_forever and get sem g_semId.
ExampleSemTask1 post sem g_semId.
```
......@@ -10,26 +10,29 @@ Futex(Fast userspace mutex,用户态快速互斥锁)是内核提供的一种
当用户态线程释放锁时,先在用户态进行锁状态的判断维护,若此时没有其他线程被该锁阻塞,则直接在用户态进行解锁返回;反之,则需要进行阻塞线程的唤醒操作,通过Futex系统调用请求内核介入来唤醒阻塞队列中的线程。
## 运行机制
当用户态产生锁的竞争或释放需要进行相关线程的调度操作时,会触发Futex系统调用进入内核,此时会将用户态锁的地址传入内核,并在内核的Futex中以锁地址来区分用户态的每一把锁,因为用户态可用虚拟地址空间为1GiB,为了便于查找、管理,内核Futex采用哈希桶来存放用户态传入的锁。
当前哈希桶共有80个,0~63号桶用于存放私有锁(以虚拟地址进行哈希),64~79号桶用于存放共享锁(以物理地址进行哈希),私有/共享属性通过用户态锁的初始化以及Futex系统调用入参确定。
当前哈希桶共有80个,~~0-63号桶用于存放私有锁(以虚拟地址进行哈希),64-79~~号桶用于存放共享锁(以物理地址进行哈希),私有/共享属性通过用户态锁的初始化以及Futex系统调用入参确定。
## Futex设计图
**图1** Futex设计图
**图1**
![zh-cn_image_0000001127535690](figures/zh-cn_image_0000001127535690.jpg)
如图1,每个futex哈希桶中存放被futex_list串联起来的哈希值相同的futex node,每个futex node对应一个被挂起的task,node中key值唯一标识一把用户态锁,具有相同key值的node被queue_list串联起来表示被同一把锁阻塞的task队列。
Futex有以下三种操作:
## Futex有以下三种操作:
**表1** Futex模块接口
**Futex模块接口**
| 功能分类 | 接口**名称** | 描述 |
| -------- | -------- | -------- |
| 设置线程等待 | OsFutexWait | 向Futex表中插入代表被阻塞的线程的node |
| 唤醒被阻塞线程 | OsFutexWake | 唤醒一个被指定锁阻塞的线程 |
| 调整锁的地址 | OsFutexRequeue | 调整指定锁在Futex表中的位置 |
| 功能分类 | 接口**名称** | 描述 |
| -------------- | -------------- | ------------------------------------- |
| 设置线程等待 | OsFutexWait | 向Futex表中插入代表被阻塞的线程的node |
| 唤醒被阻塞线程 | OsFutexWake | 唤醒一个被指定锁阻塞的线程 |
| 调整锁的地址 | OsFutexRequeue | 调整指定锁在Futex表中的位置 |
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> Futex系统调用通常与用户态逻辑共同组成用户态锁,故推荐使用用户态POSIX接口的锁。
> Futex系统调用通常与用户态逻辑共同组成用户态锁,故推荐使用用户态POSIX接口的锁。
\ No newline at end of file
......@@ -12,46 +12,53 @@
**表1** 信号的运作流程及相关接口(用户态接口)
| 功能分类 | 接口**名称** | 描述 |
| -------- | -------- | -------- |
| 注册信号回调函数 | signal: | 注册信号总入口及注册和去注册某信号的回调函数。 |
| 注册信号回调函数 | sigaction | 功能同signal,但增加了信号发送相关的配置选项,目前仅支持SIGINFO结构体中的部分参数。 |
| 发送信号 | kill<br/>pthread_kill<br/>raise<br/>alarm<br/>abort | 发送信号给某个进程或进程内发送消息给某线程,为某进程下的线程设置信号标志位。 |
| 触发回调 | 无 | 由系统调用与中断触发,内核态与用户态切换前会先进入用户态指定函数并处理完相应回调函数,再回到原用户态程序继续运行。 |
| 功能分类 | 接口**名称** | 描述 |
| ---------------- | --------------------------------------------------- | ------------------------------------------------------------ |
| 注册信号回调函数 | signal | 注册信号总入口及注册和去注册某信号的回调函数。 |
| 注册信号回调函数 | sigaction | 功能同signal,但增加了信号发送相关的配置选项,目前仅支持SIGINFO结构体中的部分参数。 |
| 发送信号 | kill<br/>pthread_kill<br/>raise<br/>alarm<br/>abort | 发送信号给某个进程或进程内发送消息给某线程,为某进程下的线程设置信号标志位。 |
| 触发回调 | 无 | 由系统调用与中断触发,内核态与用户态切换前会先进入用户态指定函数并处理完相应回调函数,再回到原用户态程序继续运行。 |
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 信号机制为提供给用户态程序进程间通信的能力,故推荐使用上表1列出的用户态POSIX相关接口。
>
> 注册回调函数:
>
>
> ```
> void *signal(int sig, void (*func)(int))(int);
> ```
>
> a. 31 号信号,该信号用来注册该进程的回调函数处理入口,不可重复注册。
>
> b. 0-30 号信号,该信号段用来注册与去注册回调函数。
>
> 注册回调函数:
>
>
> ```
> int sigaction(int, const struct sigaction *__restrict, struct sigaction *__restrict);
> ```
>
> 支持信号注册的配置修改和配置获取,目前仅支持SIGINFO的选项,SIGINFO内容见sigtimedwait接口内描述。
>
> 发送信号:
>
> a. 进程接收信号存在默认行为,单不支持POSIX标准所给出的STOP及CONTINUE、COREDUMP功能。
>
> b. 进程无法屏蔽SIGSTOP、SIGKILL、SIGCONT信号。
>
> c. 某进程后被杀死后,若其父进程不回收该进程,其转为僵尸进程。
>
> d. 进程接收到某信号后,直到该进程被调度后才会执行信号回调。
>
> e. 进程结束后会发送SIGCHLD信号给父进程,该发送动作无法取消。
>
> f. 无法通过信号唤醒处于DELAY状态的进程。
> 信号机制为提供给用户态程序进程间通信的能力,故推荐使用上表1列出的用户态POSIX相关接口。
>
> **注册回调函数:**
>
>
> ```
> void *signal(int sig, void (*func)(int))(int);
> ```
>
> - 31 号信号,该信号用来注册该进程的回调函数处理入口,不可重复注册。
>
>
> - 0-30 号信号,该信号段用来注册与去注册回调函数。
>
>
> **注册回调函数:**
>
>
> ```
> int sigaction(int, const struct sigaction *__restrict, struct sigaction *__restrict);
> ```
>
> 支持信号注册的配置修改和配置获取,目前仅支持SIGINFO的选项,SIGINFO内容见sigtimedwait接口内描述。
>
> **发送信号:**
>
> - 进程接收信号存在默认行为,单不支持POSIX标准所给出的STOP及CONTINUE、COREDUMP功能。
>
>
> - 进程无法屏蔽SIGSTOP、SIGKILL、SIGCONT信号。
>
>
> - 某进程后被杀死后,若其父进程不回收该进程,其转为僵尸进程。
>
>
> - 进程接收到某信号后,直到该进程被调度后才会执行信号回调。
>
>
> - 进程结束后会发送SIGCHLD信号给父进程,该发送动作无法取消。
>
>
> - 无法通过信号唤醒处于DELAY状态的进程。
\ No newline at end of file
......@@ -5,12 +5,20 @@
LiteIPC是OpenHarmony LiteOS-A内核提供的一种新型IPC(Inter-Process Communication,即进程间通信)机制,不同于传统的System V IPC机制,LiteIPC主要是为RPC(Remote Procedure Call,即远程过程调用)而设计的,而且是通过设备文件的方式对上层提供接口的,而非传统的API函数方式。
LiteIPC中有两个主要概念,一个是ServiceManager,另一个是Service。整个系统只能有一个ServiceManager,而Service可以有多个。ServiceManager有两个主要功能:一是负责Service的注册和注销,二是负责管理Service的访问权限(只有有权限的任务(Task)可以向对应的Service发送IPC消息)。
LiteIPC中有两个主要概念,一个是ServiceManager,另一个是Service。整个系统只能有一个ServiceManager,而Service可以有多个。
**ServiceManager有两个主要功能:**
- 一是负责Service的注册和注销,
- 二是负责管理Service的访问权限(只有有权限的任务(Task)可以向对应的Service发送IPC消息)。
## 运行机制
首先将需要接收IPC消息的任务通过ServiceManager注册成为一个Service,然后通过ServiceManager为该Service任务配置访问权限,即指定哪些任务可以向该Service任务发送IPC消息。LiteIPC的核心思想就是在内核态为每个Service任务维护一个IPC消息队列,该消息队列通过LiteIPC设备文件向上层用户态程序分别提供代表收取IPC消息的读操作和代表发送IPC消息的写操作。
首先将需要接收IPC消息的任务通过ServiceManager注册成为一个Service,然后通过ServiceManager为该Service任务配置访问权限,即指定哪些任务可以向该Service任务发送IPC消息。
LiteIPC的核心思想就是在内核态为每个Service任务维护一个IPC消息队列,该消息队列通过LiteIPC设备文件向上层用户态程序分别提供代表收取IPC消息的读操作和代表发送IPC消息的写操作。
## 开发指导
......@@ -20,11 +28,11 @@ LiteIPC中有两个主要概念,一个是ServiceManager,另一个是Service
**表1** LiteIPC模块接口(仅LiteOS-A内部使用)
| 功能分类 | 接口描述 |
| -------- | -------- |
| 模块初始化 | OsLiteIpcInit:初始化LiteIPC模块 |
| IPC消息内存池 | -&nbsp;LiteIpcPoolInit:初始化进程的IPC消息内存池<br/>-&nbsp;LiteIpcPoolReInit:重新初始化进程的IPC消息内存池<br/>-&nbsp;LiteIpcPoolDelete:释放进程的IPC消息内存池 |
| Service管理 | LiteIpcRemoveServiceHandle:删除指定的Service |
| 功能分类 | 接口描述 |
| ------------- | ------------------------------------------------------------ |
| 模块初始化 | OsLiteIpcInit:初始化LiteIPC模块 |
| IPC消息内存池 | -&nbsp;LiteIpcPoolInit:初始化进程的IPC消息内存池<br/>-&nbsp;LiteIpcPoolReInit:重新初始化进程的IPC消息内存池<br/>-&nbsp;LiteIpcPoolDelete:释放进程的IPC消息内存池 |
| Service管理 | LiteIpcRemoveServiceHandle:删除指定的Service |
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> LiteIPC模块接口都只在LiteOS-A内部使用。
> LiteIPC模块接口都只在LiteOS-A内部使用。
\ No newline at end of file
......@@ -16,7 +16,7 @@ OpenHarmony系统的动态加载与链接机制主要是由内核加载器以及
## 运行机制
**图1** 动态加载流程
**图1** **动态加载流程**
![zh-cn_image_0000001133104502](figures/zh-cn_image_0000001133104502.png)
1. 内核将应用程序ELF文件的PT_LOAD段信息映射至进程空间。对于ET_EXEC类型的文件,根据PT_LOAD段中p_vaddr进行固定地址映射;对于ET_DYN类型(位置无关的可执行程序,通过编译选项“-fPIE”得到)的文件,内核通过mmap接口选择base基址进行映射(load_addr = base + p_vaddr)。
......@@ -25,18 +25,18 @@ OpenHarmony系统的动态加载与链接机制主要是由内核加载器以及
3. 动态链接器自举并查找应用程序依赖的所有共享库并对导入符号进行重定位,最后跳转至应用程序的e_entry(或base + e_entry),开始运行应用程序。
**图2** 程序执行流程
**图2** **程序执行流程**
![zh-cn_image_0000001133264664](figures/zh-cn_image_0000001133264664.png)
1. 加载器与链接器调用mmap映射PT_LOAD段
1. 加载器与链接器调用mmap映射PT_LOAD段
2. 内核调用map_pages接口查找并映射pagecache已有的缓存
2. 内核调用map_pages接口查找并映射pagecache已有的缓存
3. 程序执行时,虚拟内存区间若无具体的物理内存做映射,系统将触发缺页中断,将elf文件内容读入物理内存,并将该内存块加入pagecache
3. 程序执行时,虚拟内存区间若无具体的物理内存做映射,系统将触发缺页中断,将elf文件内容读入物理内存,并将该内存块加入pagecache
4. 将已读入文件内容的物理内存与虚拟地址区间做映射
4. 将已读入文件内容的物理内存与虚拟地址区间做映射
5. 程序继续执行
5. 程序继续执行
## 开发指导
......@@ -46,11 +46,11 @@ OpenHarmony系统的动态加载与链接机制主要是由内核加载器以及
**表1** 内核加载器模块接口
| 功能分类 | 接口**名称** | 描述 |
| -------- | -------- | -------- |
| 模块初始化 | LOS_DoExecveFile | 根据输入的参数执行指定的用户程序 |
| 功能分类 | 接口**名称** | 描述 |
| ---------- | ---------------- | -------------------------------- |
| 模块初始化 | LOS_DoExecveFile | 根据输入的参数执行指定的用户程序 |
### 开发流程
LOS_DoExecveFile接口一般由用户通过exec家族函数利用系统调用机制创建新的进程,内核不能直接调用该接口启动新进程。
LOS_DoExecveFile接口一般由用户通过exec家族函数利用系统调用机制创建新的进程,内核不能直接调用该接口启动新进程。
\ No newline at end of file
......@@ -12,7 +12,7 @@ OpenHarmony系统通过VDSO机制实现上层用户态程序可以快速读取
VDSO其核心思想就是内核看护一段内存,并将这段内存映射(只读)进用户态应用程序的地址空间,应用程序通过链接vdso.so后,将某些系统调用替换为直接读取这段已映射的内存从而避免系统调用达到加速的效果。
VDSO总体可分为数据页与代码页两部分:
**VDSO总体可分为数据页与代码页两部分:**
- 数据页提供内核映射给用户进程的内核时数据;
......@@ -21,7 +21,7 @@ VDSO总体可分为数据页与代码页两部分:
**图1** VDSO系统设计
![zh-cn_image_0000001173586763](figures/zh-cn_image_0000001173586763.jpg)
如图1所示,当前VDSO机制有以下几个主要步骤:
**如图1所示,当前VDSO机制有以下几个主要步骤:**
① 内核初始化时进行VDSO数据页的创建;
......@@ -42,6 +42,9 @@ VDSO总体可分为数据页与代码页两部分:
⑨ 将从VDSO数据页获取到的数据作为结果返回给用户程序;
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> - 当前VDSO机制支持LibC库clock_gettime接口的CLOCK_REALTIME_COARSE与CLOCK_MONOTONIC_COARSE功能,clock_gettime接口的使用方法详见POSIX标准。用户调用C库接口clock_gettime(CLOCK_REALTIME_COARSE, &amp;ts)或者clock_gettime(CLOCK_MONOTONIC_COARSE, &amp;ts)即可使用VDSO机制。
>
> - 使用VDSO机制得到的时间精度会与系统tick中断的精度保持一致,适用于对时间没有高精度要求且短时间内会高频触发clock_gettime或gettimeofday系统调用的场景,若有高精度要求,不建议采用VDSO机制。
>
> - 当前VDSO机制支持LibC库clock_gettime接口的CLOCK_REALTIME_COARSE与CLOCK_MONOTONIC_COARSE功能,clock_gettime接口的使用方法详见POSIX标准。
>
> - 用户调用C库接口clock_gettime(CLOCK_REALTIME_COARSE, &amp;ts)或者clock_gettime(CLOCK_MONOTONIC_COARSE, &amp;ts)即可使用VDSO机制。
>
> - 使用VDSO机制得到的时间精度会与系统tick中断的精度保持一致,适用于对时间没有高精度要求且短时间内会高频触发clock_gettime或gettimeofday系统调用的场景,若有高精度要求,不建议采用VDSO机制。
\ No newline at end of file
......@@ -42,11 +42,12 @@ LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK:开关宏,默认关闭;若打开这
**示例代码**
该示例代码的测试函数可以加在 kernel /liteos_a/testsuites /kernel /src /osTest.c 中的 TestTaskEntry 中进行测试.
代码实现如下:
```
```c
#include <stdio.h>
#include <string.h>
#include "los_memory.h"
......@@ -70,12 +71,12 @@ void MemIntegrityTest(void)
编译运行输出log如下:
```
[ERR][OsMemMagicCheckPrint], 2028, memory check error!
memory used but magic num wrong, magic num = 0x00000000 /* 提示信息,检测到哪个字段被破坏了,用例构造了将下个节点的头4个字节清零,即魔鬼数字字段 */
broken node head: 0x20003af0 0x00000000 0x80000020, prev node head: 0x20002ad4 0xabcddcba 0x80000020
broken node head: 0x20003af0 0x00000000 0x80000020, prev node head: 0x20002ad4 0xabcddcba 0x80000020
/* 被破坏节点和其前节点关键字段信息,分别为其前节点地址、节点的魔鬼数字、节点的sizeAndFlag;可以看出被破坏节点的魔鬼数字字段被清零,符合用例场景 */
broken node head LR info: /* 节点的LR信息需要开启内存检测功能才有有效输出 */
......
......@@ -7,14 +7,14 @@
- 内存水线:即内存池的最大使用量,每次申请和释放时,都会更新水线值,实际业务可根据该值,优化内存池大小;
- 碎片率:衡量内存池的碎片化程度,碎片率高表现为内存池剩余内存很多,但是最大空闲内存块很小,可以用公式(fragment=100-100\*最大空闲内存块大小/剩余内存大小)来度量
- 碎片率:衡量内存池的碎片化程度,碎片率高表现为内存池剩余内存很多,但是最大空闲内存块很小,可以用公式(fragment=100-100\*最大空闲内存块大小/剩余内存大小)来度量
- 其他统计信息:调用接口LOS_MemInfoGet时,会扫描内存池的节点信息,统计出相关信息。
## 功能配置
LOSCFG_MEM_WATERLINE:开关宏,默认关闭;若需要打开这个功能,可以在配置项中开启“Debug-&gt; Enable memory pool waterline or not”。如需获取内存水线,需要打开该配置。
LOSCFG_MEM_WATERLINE:开关宏,默认关闭;若需要打开这个功能,可以在配置项中开启“Debug-&gt; Enable MEM Debug-&gt; Enable memory pool waterline or not”。如需获取内存水线,需要打开该配置。
## 开发指导
......@@ -24,8 +24,8 @@ LOSCFG_MEM_WATERLINE:开关宏,默认关闭;若需要打开这个功能,
关键结构体介绍:
```
```c
typedef struct {
UINT32 totalUsedSize; // 内存池的内存使用量
UINT32 totalFreeSize; // 内存池的剩余内存大小
......@@ -38,7 +38,7 @@ typedef struct {
} LOS_MEM_POOL_STATUS;
```
- 内存水线获取:调用LOS_MemInfoGet接口,第1个参数是内存池首地址,第2个参数是LOS_MEM_POOL_STATUS类型的句柄,其中字段usageWaterLine即水线值。
- 内存水线获取:调用 LOS_MemInfoGet(VOID *pool, LOS_MEM_POOL_STATUS *poolStatus)接口,第1个参数是内存池首地址,第2个参数是LOS_MEM_POOL_STATUS类型的句柄,其中字段usageWaterLine即水线值。
- 内存碎片率计算:同样调用LOS_MemInfoGet接口,可以获取内存池的剩余内存大小和最大空闲内存块大小,然后根据公式(fragment=100-100\*最大空闲内存块大小/剩余内存大小)得出此时的动态内存池碎片率。
......@@ -53,13 +53,13 @@ typedef struct {
3. 利用公式算出使用率及碎片率。
**示例代码**
本演示代码在 . kernel /liteos_a/testsuites /kernel /src /osTest.c中编译验证,在TestTaskEntry中调用验证入口函数MemTest。
代码实现如下:
```
代码实现如下:
```c
#include <stdio.h>
#include <string.h>
#include "los_task.h"
......@@ -70,15 +70,14 @@ void MemInfoTaskFunc(void)
{
LOS_MEM_POOL_STATUS poolStatus = {0};
/* pool为要统计信息的内存地址,此处以OS_SYS_MEM_ADDR为例 */
/* pool为要统计信息的内存地址,此处以OS_SYS_MEM_ADDR为例 */
void *pool = OS_SYS_MEM_ADDR;
LOS_MemInfoGet(pool, &poolStatus);
/* 算出内存池当前的碎片率百分比 */
unsigned char fragment = 100 - poolStatus.maxFreeNodeSize * 100 / poolStatus.totalFreeSize;
/* 算出内存池当前的使用率百分比 */
unsigned char usage = LOS_MemTotalUsedGet(pool) * 100 / LOS_MemPoolSizeGet(pool);
printf("usage = %d, fragment = %d, maxFreeSize = %d, totalFreeSize = %d, waterLine = %d\n", usage, fragment, poolStatus.maxFreeNodeSize,
poolStatus.totalFreeSize, poolStatus.usageWaterLine);
dprintf("usage = %d, fragment = %d, maxFreeSize = %d, totalFreeSize = %d, waterLine = %d\n", usage, fragment, poolStatus.maxFreeNodeSize, poolStatus.totalFreeSize, poolStatus.usageWaterLine);
}
int MemTest(void)
......@@ -92,10 +91,10 @@ int MemTest(void)
taskStatus.usTaskPrio = 10;
ret = LOS_TaskCreate(&taskID, &taskStatus);
if (ret != LOS_OK) {
printf("task create failed\n");
return -1;
dprintf("task create failed\n");
return LOS_NOK;
}
return 0;
return LOS_OK;
}
```
......@@ -105,8 +104,8 @@ int MemTest(void)
编译运行输出的结果如下:
根据实际运行环境,数据会有差异
```
usage = 22, fragment = 3, maxFreeSize = 49056, totalFreeSize = 50132, waterLine = 1414
```
......@@ -8,11 +8,11 @@
## 功能配置
1. LOSCFG_MEM_LEAKCHECK:开关宏,默认关闭;如需要打开这个功能,可以在配置项中开启“Debug-&gt; Enable Function call stack of Mem operation recorded”。
1. LOSCFG_MEM_LEAKCHECK:开关宏,默认关闭;如需要打开这个功能,可以在配置项中开启“Debug-&gt; Enable MEM Debug-&gt; Enable Function call stack of Mem operation recorded”。
2. LOS_RECORD_LR_CNT:记录的LR层数,默认3层;每层LR消耗sizeof(void \*)字节数的内存。
3. LOS_OMIT_LR_CNT:忽略的LR层数,默认2层,即从调用LOS_MemAlloc的函数开始记录,可根据实际情况调整。为啥需要这个配置?有3点原因如下:
3. LOS_OMIT_LR_CNT:忽略的LR层数,默认2层,即从调用LOS_MemAlloc的函数开始记录,可根据实际情况调整。需要此配置原因如下:
- LOS_MemAlloc接口内部也有函数调用;
- 外部可能对LOS_MemAlloc接口有封装;
- LOS_RECORD_LR_CNT 配置的LR层数有限;
......@@ -27,17 +27,17 @@
该调测功能可以分析关键的代码逻辑中是否存在内存泄漏。开启这个功能,每次申请内存时,会记录LR信息。在需要检测的代码段前后,调用LOS_MemUsedNodeShow接口,每次都会打印指定内存池已使用的全部节点信息,对比前后两次的节点信息,新增的节点信息就是疑似泄漏的内存节点。通过LR,可以找到具体申请的代码位置,进一步确认是否泄漏。
调用LOS_MemUsedNodeShow接口输出的节点信息格式如下:每1行为一个节点信息;第1列为节点地址,可以根据这个地址,使用GDB等手段查看节点完整信息;第2列为节点的大小,等于节点头大小+数据域大小;第3~5列为函数调用关系LR地址,可以根据这个值,结合汇编文件,查看该节点具体申请的位置。
调用LOS_MemUsedNodeShow接口输出的节点信息格式如下:每1行为一个节点信息;第1列为节点地址,可以根据这个地址,使用GDB等工具查看节点完整信息;第2列为节点的大小,等于节点头大小+数据域大小;第3~5列为函数调用关系LR地址,可以根据这个值,结合汇编文件,查看该节点具体申请的位置。
```
node size LR[0] LR[1] LR[2]
0x10017320: 0x528 0x9b004eba 0x9b004f60 0x9b005002
0x10017848: 0xe0 0x9b02c24e 0x9b02c246 0x9b008ef0
0x10017928: 0x50 0x9b008ed0 0x9b068902 0x9b0687c4
node size LR[0] LR[1] LR[2]
0x10017320: 0x528 0x9b004eba 0x9b004f60 0x9b005002
0x10017848: 0xe0 0x9b02c24e 0x9b02c246 0x9b008ef0
0x10017928: 0x50 0x9b008ed0 0x9b068902 0x9b0687c4
0x10017978: 0x24 0x9b008ed0 0x9b068924 0x9b0687c4
0x1001799c: 0x30 0x9b02c24e 0x9b02c246 0x9b008ef0
0x100179cc: 0x5c 0x9b02c24e 0x9b02c246 0x9b008ef0
0x1001799c: 0x30 0x9b02c24e 0x9b02c246 0x9b008ef0
0x100179cc: 0x5c 0x9b02c24e 0x9b02c246 0x9b008ef0
```
> ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:**
......@@ -61,21 +61,36 @@ node size LR[0] LR[1] LR[2]
**示例代码**
本演示代码在 . kernel /liteos_a/testsuites /kernel /src /osTest.c中编译验证,在TestTaskEntry中调用验证入口函数MemLeakTest。
为了方便展示建议创建新的内存池,需要在target_config.h 中定义 LOSCFG_MEM_MUL_POOL
代码实现如下:
```
```c
#include <stdio.h>
#include <string.h>
#include "los_memory.h"
#include "los_config.h"
#define TEST_NEW_POOL_SIZE 2000
#define TEST_MALLOC_SIZE 8
void MemLeakTest(void)
{
OsMemUsedNodeShow(LOSCFG_SYS_HEAP_ADDR);
void *ptr1 = LOS_MemAlloc(LOSCFG_SYS_HEAP_ADDR, 8);
void *ptr2 = LOS_MemAlloc(LOSCFG_SYS_HEAP_ADDR, 8);
OsMemUsedNodeShow(LOSCFG_SYS_HEAP_ADDR);
VOID *pool = NULL;
/* 由于原内存池分配过多, 为了方便展示, 创建新的内存池 */
pool = LOS_MemAlloc(OS_SYS_MEM_ADDR, TEST_NEW_POOL_SIZE);
(VOID)LOS_MemInit(pool, TEST_NEW_POOL_SIZE);
OsMemUsedNodeShow(pool);
void *ptr1 = LOS_MemAlloc(pool, TEST_MALLOC_SIZE);
void *ptr2 = LOS_MemAlloc(pool, TEST_MALLOC_SIZE);
OsMemUsedNodeShow(pool);
/* 释放内存池 */
(VOID)LOS_MemDeInit(pool);
}
```
......@@ -86,51 +101,50 @@ void MemLeakTest(void)
编译运行输出log如下:
```
node size LR[0] LR[1] LR[2]
0x20001b04: 0x24 0x08001a10 0x080035ce 0x080028fc
0x20002058: 0x40 0x08002fe8 0x08003626 0x080028fc
0x200022ac: 0x40 0x08000e0c 0x08000e56 0x0800359e
0x20002594: 0x120 0x08000e0c 0x08000e56 0x08000c8a
0x20002aac: 0x56 0x08000e0c 0x08000e56 0x08004220
node size LR[0] LR[1] LR[2]
0x20001b04: 0x24 0x08001a10 0x080035ce 0x080028fc
0x20002058: 0x40 0x08002fe8 0x08003626 0x080028fc
0x200022ac: 0x40 0x08000e0c 0x08000e56 0x0800359e
0x20002594: 0x120 0x08000e0c 0x08000e56 0x08000c8a
0x20002aac: 0x56 0x08000e0c 0x08000e56 0x08004220
0x20003ac4: 0x1d 0x08001458 0x080014e0 0x080041e6
0x20003ae0: 0x1d 0x080041ee 0x08000cc2 0x00000000
/* 第一次OsMemUsedNodeShow打印,由于该内存池未分配,所以无内存节点 */
node LR[0] LR[1] LR[2]
/* 第二次OsMemUsedNodeShow打印,有两个新的内存节点 */
node LR[0] LR[1] LR[2]
0x00402e0d90: 0x004009f040 0x0040037614 0x0040005480
0x00402e0db0: 0x004009f04c 0x0040037614 0x0040005480
```
对比两次log,差异如下,这些内存节点就是疑似泄漏的内存块:
```
0x20003ac4: 0x1d 0x08001458 0x080014e0 0x080041e6
0x20003ae0: 0x1d 0x080041ee 0x08000cc2 0x00000000
0x00402e0d90: 0x004009f040 0x0040037614 0x0040005480
0x00402e0db0: 0x004009f04c 0x0040037614 0x0040005480
```
部分汇编文件如下:
```
MemLeakTest:
0x80041d4: 0xb510 PUSH {R4, LR}
0x80041d6: 0x4ca8 LDR.N R4, [PC, #0x2a0] ; g_memStart
0x80041d8: 0x0020 MOVS R0, R4
0x80041da: 0xf7fd 0xf93e BL LOS_MemUsedNodeShow ; 0x800145a
0x80041de: 0x2108 MOVS R1, #8
0x80041e0: 0x0020 MOVS R0, R4
0x80041e2: 0xf7fd 0xfbd9 BL LOS_MemAlloc ; 0x8001998
0x80041e6: 0x2108 MOVS R1, #8
0x80041e8: 0x0020 MOVS R0, R4
0x80041ea: 0xf7fd 0xfbd5 BL LOS_MemAlloc ; 0x8001998
0x80041ee: 0x0020 MOVS R0, R4
0x80041f0: 0xf7fd 0xf933 BL LOS_MemUsedNodeShow ; 0x800145a
0x80041f4: 0xbd10 POP {R4, PC}
0x80041f6: 0x0000 MOVS R0, R0
4009f014: 7d 1e a0 e3 mov r1, #2000
4009f018: 00 00 90 e5 ldr r0, [r0]
4009f01c: 67 7a fe eb bl #-398948 <LOS_MemAlloc>
4009f020: 7d 1e a0 e3 mov r1, #2000
4009f024: 00 40 a0 e1 mov r4, r0
4009f028: c7 79 fe eb bl #-399588 <LOS_MemInit>
4009f02c: 04 00 a0 e1 mov r0, r4
4009f030: 43 78 fe eb bl #-401140 <OsMemUsedNodeShow>
4009f034: 04 00 a0 e1 mov r0, r4
4009f038: 08 10 a0 e3 mov r1, #8
4009f03c: 5f 7a fe eb bl #-398980 <LOS_MemAlloc>
4009f040: 04 00 a0 e1 mov r0, r4
4009f044: 08 10 a0 e3 mov r1, #8
4009f048: 5c 7a fe eb bl #-398992 <LOS_MemAlloc>
4009f04c: 04 00 a0 e1 mov r0, r4
4009f050: 3b 78 fe eb bl #-401172 <OsMemUsedNodeShow>
4009f054: 3c 00 9f e5 ldr r0, [pc, #60]
4009f058: 40 b8 fe eb bl #-335616 <dprintf>
4009f05c: 04 00 a0 e1 mov r0, r4
4009f060: 2c 7a fe eb bl #-399184 <LOS_MemDeInit>
```
其中,通过查找0x080041ee,就可以发现该内存节点是在MemLeakTest接口里申请的且是没有释放的。
其中,通过查找0x4009f040,就可以发现该内存节点是在MemLeakTest接口里申请的且是没有释放的。
# Perf调测
## 基本概念
Perf为性能分析工具,依赖PMU(Performance Monitoring Unit)对采样事件进行计数和上下文采集,统计出热点分布(hot spot)和热路径(hot path)。
## 运行机制
基于事件采样原理,以性能事件为基础,当事件发生时,相应的事件计数器溢出发生中断,在中断处理函数中记录事件信息,包括当前的pc、当前运行的任务ID以及调用栈等信息。
Perf提供2种工作模式,计数模式和采样模式。
计数模式仅统计事件发生的次数和耗时,采样模式会收集上下文数据到环形buffer中,需要IDE进行数据解析生成热点函数与热点路径。
## 接口说明
OpenHarmony LiteOS-A内核的Perf模块提供下面几种功能,接口详细信息可以查看[API](https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_perf.h)参考。
**表1** Perf模块接口说明
| 功能分类 | 接口描述 |
| -------- | -------- |
| 开启/停止Perf采样 | LOS_PerfInit : 初始化Perf<br/>LOS_PerfStart:开启采样<br/>LOS_PerfStop:停止采样 |
| 配置Perf采样事件 | LOS_PerfConfig:配置采样事件的类型、周期等 |
| 读取采样数据 | LOS_PerfDataRead:读取采样数据到指定地址 |
| 注册采样数据缓冲区的钩子函数 | LOS_PerfNotifyHookReg:注册缓冲区水线到达的处理钩子<br/>LOS_PerfFlushHookReg:注册缓冲区刷cache的钩子 |
1. Perf采样事件的结构体为PerfConfigAttr,详细字段含义及取值详见 [kernel\include\los_perf.h](https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_perf.h)
2. 采样数据缓冲区为环形buffer,buffer中读过的区域可以覆盖写,未被读过的区域不能被覆盖写。
3. 缓冲区有限,用户可通过注册水线到达的钩子进行buffer溢出提醒或buffer读操作。默认水线值为buffer总大小的1/2。 示例如下:
```c
VOID Example_PerfNotifyHook(VOID)
{
CHAR buf[LOSCFG_PERF_BUFFER_SIZE] = {0};
UINT32 len;
PRINT_DEBUG("perf buffer reach the waterline!\n");
len = LOS_PerfDataRead(buf, LOSCFG_PERF_BUFFER_SIZE);
OsPrintBuff(buf, len); /* print data */
}
LOS_PerfNotifyHookReg(Example_PerfNotifyHook);
```
4. 若perf采样的buffer涉及到CPU跨cache,则用户可通过注册刷cache的钩子,进行cache同步。 示例如下:
```c
VOID Example_PerfFlushHook(VOID *addr, UINT32 size)
{
OsCacheFlush(addr, size); /* platform interface */
}
LOS_PerfNotifyHookReg(Example_PerfFlushHook);
```
刷cache接口视具体的平台自行配置。
## 开发指导
### 内核态开发流程
开启Perf调测的典型流程如下:
1. 配置Perf模块相关宏。
配置Perf控制宏LOSCFG_KERNEL_PERF,默认关,在kernel/liteos_a目录下执行 make update_config命令配置"Kernel-&gt;Enable Perf Feature"中打开:
| 配置项 | menuconfig选项 | 含义 | 设置值 |
| -------- | -------- | -------- | -------- |
| LOSCFG_KERNEL_PERF | Enable&nbsp;Perf&nbsp;Feature | Perf模块的裁剪开关 | YES/NO |
| LOSCFG_PERF_CALC_TIME_BY_TICK | Time-consuming&nbsp;Calc&nbsp;Methods-&gt;By&nbsp;Tick | Perf计时单位为tick | YES/NO |
| LOSCFG_PERF_CALC_TIME_BY_CYCLE | Time-consuming&nbsp;Calc&nbsp;Methods-&gt;By&nbsp;Cpu&nbsp;Cycle | Perf计时单位为cycle | YES/NO |
| LOSCFG_PERF_BUFFER_SIZE | Perf&nbsp;Sampling&nbsp;Buffer&nbsp;Size | Perf采样buffer的大小 | INT |
| LOSCFG_PERF_HW_PMU | Enable&nbsp;Hardware&nbsp;Pmu&nbsp;Events&nbsp;for&nbsp;Sampling | 使能硬件PMU事件,需要目标平台支持硬件PMU | YES/NO |
| LOSCFG_PERF_TIMED_PMU | Enable&nbsp;Hrtimer&nbsp;Period&nbsp;Events&nbsp;for&nbsp;Sampling | 使能高精度周期事件,需要目标平台支持高精度定时器 | YES/NO |
| LOSCFG_PERF_SW_PMU | Enable&nbsp;Software&nbsp;Events&nbsp;for&nbsp;Sampling | 使能软件事件,需要开启LOSCFG_KERNEL_HOOK | YES/NO |
2. 调用LOS_PerfConfig配置需要采样的事件。
Perf提供2种模式的配置,及3大类型的事件配置:
2种模式:计数模式(仅统计事件发生次数)、采样模式(收集上下文如任务ID、pc、backtrace等)。
3种事件类型:CPU硬件事件(cycle、branch、icache、dcache等)、高精度周期事件(cpu clock)、OS软件事件(task switch、mux pend、irq等)。
3. 在需要采样的代码起始点调用LOS_PerfStart(UINT32 sectionId), 入参sectionId标记不同的采样回话id。
4. 在需要采样的代码结束点调用LOS_PerfStop。
5. 调用输出缓冲区数据的接口LOS_PerfDataRead读取采样数据,并使用IDE工具进行解析。
#### 内核态编程实例
本实例实现如下功能:
1. 创建perf测试任务。
2. 配置采样事件。
3. 启动perf。
4. 执行需要统计的算法。
5. 停止perf。
6. 输出统计结果。
#### 内核态示例代码
前提条件:在menuconfig菜单中完成perf模块的配置, 并勾选Enable Hook Feature,Enable Software Events for Sampling。
为方便学习,本演示代码直接在 . kernel /liteos_a/testsuites /kernel /src /osTest.c中编译验证即可。
实例代码如下:
```c
#include "los_perf.h"
#define TEST_MALLOC_SIZE 200
#define TEST_TIME 5
/* 验证函数中进行malloc和free */
VOID test(VOID)
{
VOID *p = NULL;
int i;
for (i = 0; i < TEST_TIME; i++) {
p = LOS_MemAlloc(m_aucSysMem1, TEST_MALLOC_SIZE);
if (p == NULL) {
PRINT_ERR("test alloc failed\n");
return;
}
(VOID)LOS_MemFree(m_aucSysMem1, p);
}
}
STATIC VOID OsPrintBuff(const CHAR *buf, UINT32 num)
{
UINT32 i = 0;
PRINTK("num: ");
for (i = 0; i < num; i++) {
PRINTK(" %02d", i);
}
PRINTK("\n");
PRINTK("hex: ");
for (i = 0; i < num; i++) {
PRINTK(" %02x", buf[i]);
}
PRINTK("\n");
}
STATIC VOID perfTestHwEvent(VOID)
{
UINT32 ret;
CHAR *buf = NULL;
UINT32 len;
//LOS_PerfInit(NULL, 0);
PerfConfigAttr attr = {
.eventsCfg = {
.type = PERF_EVENT_TYPE_SW,
.events = {
[0] = {PERF_COUNT_SW_TASK_SWITCH, 0xff}, /* 抓取调度 */
[1] = {PERF_COUNT_SW_MEM_ALLOC, 0xff}, /* 抓取内存分配 */
PERF_COUNT_SW_TASK_SWITCH
},
.eventsNr = 2,
.predivided = 1, /* cycle counter increase every 64 cycles */
},
.taskIds = {0},
.taskIdsNr = 0,
.needSample = 0,
.sampleType = PERF_RECORD_IP | PERF_RECORD_CALLCHAIN,
};
ret = LOS_PerfConfig(&attr);
if (ret != LOS_OK) {
PRINT_ERR("perf config error %u\n", ret);
return;
}
PRINTK("------count mode------\n");
LOS_PerfStart(0);
test(); /* this is any test function*/
LOS_PerfStop();
PRINTK("--------sample mode------ \n");
attr.needSample = 1;
LOS_PerfConfig(&attr);
LOS_PerfStart(2); // 2: set the section id to 2.
test(); /* this is any test function*/
LOS_PerfStop();
buf = LOS_MemAlloc(m_aucSysMem1, LOSCFG_PERF_BUFFER_SIZE);
if (buf == NULL) {
PRINT_ERR("buffer alloc failed\n");
return;
}
/* get sample data */
len = LOS_PerfDataRead(buf, LOSCFG_PERF_BUFFER_SIZE);
OsPrintBuff(buf, len); /* print data */
(VOID)LOS_MemFree(m_aucSysMem1, buf);
}
UINT32 Example_Perf_test(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S perfTestTask = {0};
UINT32 taskID;
/* 创建用于perf测试的任务 */
perfTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)perfTestHwEvent;
perfTestTask.pcName = "TestPerfTsk"; /* 测试任务名称 */
perfTestTask.uwStackSize = 0x1000; // 0x8000: perf test task stack size
perfTestTask.usTaskPrio = 5; // 5: perf test task priority
ret = LOS_TaskCreate(&taskID, &perfTestTask);
if (ret != LOS_OK) {
PRINT_ERR("PerfTestTask create failed. 0x%x\n", ret);
return LOS_NOK;
}
return LOS_OK;
}
LOS_MODULE_INIT(perfTestHwEvent, LOS_INIT_LEVEL_KMOD_EXTENDED);
```
#### 内核态结果验证
输出结果如下:
```
type: 2
events[0]: 1, 0xff
events[1]: 3, 0xff
predivided: 1
sampleType: 0x60
needSample: 0
------count mode------
[task switch] eventType: 0x1 [core 0]: 0
[mem alloc] eventType: 0x3 [core 0]: 5
time used: 0.005000(s)
--------sample mode------
type: 2
events[0]: 1, 0xff
events[1]: 3, 0xff
predivided: 1
sampleType: 0x60
needSample: 1
dump perf data, addr: 0x402c3e6c length: 0x5000
time used: 0.000000(s)
num: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19
hex: 00 ffffffef ffffffef ffffffef 02 00 00 00 14 00 00 00 60 00 00 00 02 00 00 00
根据实际运行环境,过程打印会有差异
```
- 针对计数模式,系统在perf stop后会打印:
事件名称(cycles)、事件类型(0xff)、事件发生的次数(5466989440)。
当采样事件为硬件PMU事件时,打印的事件类型为实际的硬件事件id,非enum PmuHWId中定义的抽象类型。
- 针对采样模式,系统在perf stop后会打印采样数据的地址和长度:
dump section data, addr: (0x8000000) length: (0x5000)
用户可以通过JTAG口导出该片内存,再使用IDE线下工具解析。
或者通过LOS_PerfDataRead将数据读到指定地址,进行查看或进一步处理。示例中OsPrintBuff为测试接口,其按字节打印Read到的采样数据,num表示第几个字节,hex表示该字节中的数值。
......@@ -6,16 +6,16 @@
CPU(中央处理器,Central Processing Unit)占用率分为系统CPU占用率、进程CPU占用率、任务CPU占用率和中断CPU占用率。用户通过系统级的CPU占用率,判断当前系统负载是否超出设计规格。通过系统中各个进程/任务/中断的CPU占用情况,判断各个进程/任务/中断的CPU占用率是否符合设计的预期。
- 系统CPU占用率(CPU Percent)
指周期时间内系统的CPU占用率,用于表示系统一段时间内的闲忙程度,也表示CPU的负载情况。系统CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分。100表示系统满负荷运转。
指周期时间内系统的CPU占用率,用于表示系统一段时间内的闲忙程度,也表示CPU的负载情况。系统CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分之一。100表示系统满负荷运转。
- 进程CPU占用率
指单个进程的CPU占用率,用于表示单个进程在一段时间内的闲忙程度。进程CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分。100表示在一段时间内系统一直在运行该进程。
指单个进程的CPU占用率,用于表示单个进程在一段时间内的闲忙程度。进程CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分之一。100表示在一段时间内系统一直在运行该进程。
- 任务CPU占用率
指单个任务的CPU占用率,用于表示单个任务在一段时间内的闲忙程度。任务CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分。100表示在一段时间内系统一直在运行该任务。
指单个任务的CPU占用率,用于表示单个任务在一段时间内的闲忙程度。任务CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分之一。100表示在一段时间内系统一直在运行该任务。
- 中断CPU占用率
指单个中断的CPU占用率,用于表示单个中断在一段时间内的闲忙程度。中断CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分。100表示在一段时间内系统一直在运行该中断。
指单个中断的CPU占用率,用于表示单个中断在一段时间内的闲忙程度。中断CPU占用率的有效表示范围为0~100,其精度(可通过配置调整)为百分之一。100表示在一段时间内系统一直在运行该中断。
## 运行机制
......@@ -50,13 +50,14 @@ OpenHarmony 提供以下四种CPU占用率的信息查询:
**表1** CPUP模块接口
| 功能分类 | 接口**名称** | 描述 |
| 功能分类 | 接口**名称** | 描述 |
| -------- | -------- | -------- |
| 系统CPU占用率 | LOS_HistorySysCpuUsage | 获取系统历史CPU占用率 |
| 进程CPU占用率 | LOS_HistoryProcessCpuUsage | 获取指定进程历史CPU占用率 |
| 进程CPU占用率 | LOS_GetAllProcessCpuUsage | 获取系统所有进程的历史CPU占用率 |
| 任务CPU占用率 | LOS_HistoryTaskCpuUsage | 获取指定任务历史CPU占用率 |
| 中断CPU占用率 | LOS_GetAllIrqCpuUsage | 获取系统所有中断的历史CPU占用率 |
| 系统CPU占用率 | LOS_HistorySysCpuUsage | 获取系统历史CPU占用率 |
| 进程CPU占用率 | LOS_HistoryProcessCpuUsage | 获取指定进程历史CPU占用率 |
| 进程CPU占用率 | LOS_GetAllProcessCpuUsage | 获取系统所有进程的历史CPU占用率 |
| 任务CPU占用率 | LOS_HistoryTaskCpuUsage | 获取指定任务历史CPU占用率 |
| 中断CPU占用率 | LOS_GetAllIrqCpuUsage | 获取系统所有中断的历史CPU占用率 |
| 重置 | LOS_CpupReset | 重置CPU 占用率相关数据 |
### 开发流程
......@@ -102,48 +103,50 @@ CPU占用率的典型开发流程:
**示例代码**
本演示代码在 . kernel /liteos_a/testsuites /kernel /src /osTest.c中编译验证,在TestTaskEntry中调用验证入口函数CpupTest。
代码实现如下:
```
```c
#include "los_task.h"
#include "los_cpup.h"
#include "los_cpup.h"
#define MODE 4
UINT32 g_cpuTestTaskID;
VOID ExampleCpup(VOID)
{
printf("entry cpup test example\n");
while(1) {
usleep(100);
UINT32 g_cpuTestTaskID;
VOID ExampleCpup(VOID)
{
int i = 0;
dprintf("entry cpup test example\n");
for (i = 0; i < 10; i++) {
usleep(100); // 100: delay for 100ms
}
}
UINT32 ItCpupTest(VOID)
{
UINT32 CpupTest(VOID)
{
UINT32 ret;
UINT32 cpupUse;
TSK_INIT_PARAM_S cpupTestTask = { 0 };
TSK_INIT_PARAM_S cpupTestTask = {0};
memset(&cpupTestTask, 0, sizeof(TSK_INIT_PARAM_S));
cpupTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleCpup;
cpupTestTask.pcName = "TestCpupTsk";
cpupTestTask.uwStackSize = 0x800;
cpupTestTask.usTaskPrio = 5;
cpupTestTask.pcName = "TestCpupTsk";
cpupTestTask.uwStackSize = 0x800; // 0x800: cpup test task stack size
cpupTestTask.usTaskPrio = 5; // 5: cpup test task priority
ret = LOS_TaskCreate(&g_cpuTestTaskID, &cpupTestTask);
if(ret != LOS_OK) {
if (ret != LOS_OK) {
printf("cpupTestTask create failed .\n");
return LOS_NOK;
}
usleep(100);
usleep(100); // 100: delay for 100ms
/* 获取当前系统历史CPU占用率 */
cpupUse = LOS_HistorySysCpuUsage(CPU_LESS_THAN_1S);
printf("the history system cpu usage in all time:%u.%u\n",
cpupUse = LOS_HistorySysCpuUsage(CPUP_LAST_ONE_SECONDS);
dprintf("the history system cpu usage in all time:%u.%u\n",
cpupUse / LOS_CPUP_PRECISION_MULT, cpupUse % LOS_CPUP_PRECISION_MULT);
/* 获取指定任务的CPU占用率,该测试例程中指定的任务为以上创建的cpup测试任务 */
cpupUse = LOS_HistoryTaskCpuUsage(g_cpuTestTaskID, CPU_LESS_THAN_1S);
printf("cpu usage of the cpupTestTask in all time:\n TaskID: %d\n usage: %u.%u\n",
g_cpuTestTaskID, cpupUse / LOS_CPUP_PRECISION_MULT, cpupUse % LOS_CPUP_PRECISION_MULT);
return LOS_OK;
/* 获取指定任务的CPU占用率,该测试例程中指定的任务为以上创建的cpup测试任务 */
cpupUse = LOS_HistoryTaskCpuUsage(g_cpuTestTaskID, CPUP_LAST_ONE_SECONDS);
dprintf("cpu usage of the cpupTestTask in all time:\n TaskID: %d\n usage: %u.%u\n",
g_cpuTestTaskID, cpupUse / LOS_CPUP_PRECISION_MULT, cpupUse % LOS_CPUP_PRECISION_MULT);
return LOS_OK;
}
```
......@@ -151,9 +154,11 @@ UINT32 ItCpupTest(VOID)
编译运行得到的结果为:
```
entry cpup test example
the history system cpu usage in all time: 3.0
cpu usage of the cpupTestTask in all time: TaskID:10 usage: 0.0
根据实际运行环境,打印会有差异
```
......@@ -13,12 +13,12 @@ cpup [_mode_] [_taskID_]
## 参数说明
**表1** 参数说明
**表1** 参数说明
| 参数 | 参数说明 | 取值范围 |
| -------- | -------- | -------- |
| mode | -&nbsp;缺省:显示系统最近10s内的CPU占用率。<br/>-&nbsp;0:显示系统最近10s内的CPU占用率。<br/>-&nbsp;1:显示系统最近1s内的CPU占用率。<br/>-&nbsp;其他数字:显示系统启动至今总的CPU&nbsp;占用率。 | [0,0xFFFFFFFF] |
| taskID | 任务ID号 | [0,0xFFFFFFFF] |
| mode | -&nbsp;缺省:显示系统最近10s内的CPU占用率。<br/>-&nbsp;0:显示系统最近10s内的CPU占用率。<br/>-&nbsp;1:显示系统最近1s内的CPU占用率。<br/>-&nbsp;其他数字:显示系统启动至今总的CPU&nbsp;占用率。 | [0, 0xFFFFFFFF] |
| taskID | 任务ID号 | [0, 0xFFFFFFFF] |
## 使用指南
......@@ -37,8 +37,8 @@ cpup [_mode_] [_taskID_]
## 输出说明
**示例**:指令输出结果
**示例** 指令输出结果
```
OHOS # cpup 1 5pid 5
......
......@@ -19,13 +19,13 @@ date命令用于查询系统日期和时间。
## 参数说明
**表1** 参数说明
**表1** 参数说明
| 参数 | 参数说明 | 取值范围 |
| -------- | -------- | -------- |
| --help | 使用帮助。 | N/A |
| +Format | 根据Format格式打印日期和时间。 | --help中列出的占位符。 |
| -u | 显示UTC,而不是当前时区 | N/A |
| 参数 | 参数说明 | 取值范围 |
| ------- | ------------------------------ | ---------------------- |
| --help | 使用帮助。 | N/A |
| +Format | 根据Format格式打印日期和时间。 | --help中列出的占位符。 |
| -u | 显示UTC,而不是当前时区 | N/A |
## 使用指南
......@@ -36,6 +36,9 @@ date命令用于查询系统日期和时间。
- 目前命令不支持设置时间和日期。
## 特殊说明
date -u参数 shell端暂不支持。切换mksh版本可全支持,方法:cd bin; ./mksh。
## 使用实例
......@@ -46,8 +49,8 @@ date命令用于查询系统日期和时间。
示例:按指定格式打印系统日期
```
OHOS:/$ date +%Y--%m--%d
1970--01--01
```
```
\ No newline at end of file
......@@ -3,7 +3,7 @@
## 命令功能
dmesg命令用于显示系统启动过程和运行过程中的信息。
dmesg命令用于显示开机信息,以及系统启动过程和运行过程中的信息。
## 命令格式
......@@ -21,22 +21,22 @@ dmesg &gt; [_fileA_]
## 参数说明
**表1** 参数说明
**表1** 参数说明
| 参数 | 参数说明 | 取值范围 |
| -------- | -------- | -------- |
| -c | 打印缓存区内容并清空缓存区。 | N/A |
| -C | 清空缓存区。 | N/A |
| -D/-E | 关闭/开启控制台打印。 | N/A |
| -L/-U | 关闭/开启串口打印。 | N/A |
| -s&nbsp;size | 设置缓存区大小&nbsp;size是要设置的大小。 | N/A |
| -l&nbsp;level | 设置缓存等级。 | 0&nbsp;-&nbsp;5 |
| &gt;&nbsp;fileA | 将缓存区内容重定向写入文件。 | N/A |
| 参数 | 参数说明 | 取值范围 |
| --------------- | ---------------------------------------- | --------------- |
| -c | 打印缓存区内容并清空缓存区。 | N/A |
| -C | 清空缓存区。 | N/A |
| -D/-E | 关闭/开启控制台打印。 | N/A |
| -L/-U | 关闭/开启串口打印。 | N/A |
| -s&nbsp;size | 设置缓存区大小&nbsp;size是要设置的大小。 | N/A |
| -l&nbsp;level | 设置缓存等级。 | [0, 5] |
| &gt;&nbsp;fileA | 将缓存区内容重定向写入文件。 | N/A |
## 使用指南
- 该命令依赖于LOSCFG_SHELL_DMESG,使用时通过menuconfig在配置项中开启"Enable Shell dmesg":
- 该命令依赖于LOSCFG_SHELL_DMESG,在kernel/liteos_a中输入make menuconfig命令。此时会弹出配置项,找到Debug选项并进入,然后在配置项中开启"Enable Shell dmesg":
Debug ---&gt; Enable a Debug Version ---&gt; Enable Shell ---&gt; Enable Shell dmesg
- dmesg参数缺省时,默认打印缓存区内容。
......@@ -53,9 +53,9 @@ dmesg &gt; [_fileA_]
## 输出说明
**示例**dmesg重定向到文件
**示例** dmesg重定向到文件
```
OHOS # dmesg > dmesg.log
Dmesg write log to dmesg.log success
```
```
\ No newline at end of file
......@@ -3,7 +3,7 @@
## 命令功能
exec命令属于shell内置命令,目前实现最基础的执行用户态程序的功能。
exec命令属于shell内置命令,在exec执行命令时,不启用新的shell进程。目前实现最基础的执行用户态程序的功能
## 命令格式
......@@ -13,11 +13,11 @@ exec &lt;_executable-file_&gt;
## 参数说明
**表1** 参数说明
**表1** 参数说明
| 参数 | 参数说明 | 取值范围 |
| -------- | -------- | -------- |
| executable-file | 有效的可执行文件。 | N/A |
| 参数 | 参数说明 |
| --------------- | ------------------ |
| executable-file | 有效的可执行文件。 |
## 使用指南
......@@ -34,11 +34,11 @@ exec &lt;_executable-file_&gt;
## 输出说明
```
OHOS # exec helloworld
OHOS # hello world!
```
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 可执行文件执行后,先打印“OHOS \#”提示符原因:目前Shell “exec”命令执行均为后台执行,结果可能导致提示符提前打印。
> 可执行文件执行后,先打印“OHOS \#”提示符原因:目前Shell “exec”命令执行均为后台执行,结果可能导致提示符提前打印。
\ No newline at end of file
......@@ -13,17 +13,17 @@ free [_-b | -k | -m | -g | -t_]
## 参数说明
**表1** 参数说明
**表1** 参数说明
| 参数 | 参数说明 | 取值范围 |
| -------- | -------- | -------- |
| 无参数 | 以Byte为单位显示。 | N/A |
| --help/-h | 查看free命令支持的参数列表。 | N/A |
| -b | 以Byte为单位显示。 | N/A |
| -k | 以KiB为单位显示。 | N/A |
| -m | 以MiB为单位显示。 | N/A |
| -g | 以GiB为单位显示。 | N/A |
| -t | 以TiB为单位显示。 | N/A |
| 参数 | 参数说明 |
| -------- | -------- |
| 无参数 | 以Byte为单位显示。 |
| --help/-h | 查看free命令支持的参数列表。 |
| -b | 以Byte为单位显示。 |
| -k | 以KiB为单位显示。 |
| -m | 以MiB为单位显示。 |
| -g | 以GiB为单位显示。 |
| -t | 以TiB为单位显示。 |
## 使用指南
......@@ -57,7 +57,7 @@ Mem: 2 2 0 0 0
Swap: 0 0 0
```
**表2** 输出说明
**表2** 输出说明
| 输出 | 说明 |
| -------- | -------- |
......
......@@ -13,7 +13,7 @@ help
## 参数说明
## 使用指南
......
......@@ -13,7 +13,7 @@ hwi
## 参数说明
## 使用指南
......@@ -106,7 +106,7 @@ hwi
102: 0 0 0.0 0.0 0.0 normal SPI_HI35XX
```
**表1** 输出说明
**表1** 输出说明
| 输出 | 说明 |
| -------- | -------- |
......
......@@ -3,7 +3,7 @@
## 命令功能
命令用于发送特定信号给指定进程
kill命令用于发送特定信号给指定进程,让它去终结不正常的应用
## 命令格式
......@@ -13,31 +13,34 @@ kill [-l [_signo_] | _-s signo_ | _-signo_] _pid..._
## 参数说明
**表1** 参数说明
**表1** 参数说明
| 参数 | 参数说明 | 取值范围 |
| -------- | -------- | -------- |
| --help | 查看kill命令支持的参数列表 | N/A |
| -l | 列出信号名称和编号。 | N/A |
| -s | 发送信号 | N/A |
| signo | 信号ID。 | [1,30] |
| pid | 进程ID。 | [1,MAX_INT] |
| 参数 | 参数说明 | 取值范围 |
| ------ | -------------------------- | ----------- |
| --help | 查看kill命令支持的参数列表 | N/A |
| -l | 列出信号名称和编号。 | N/A |
| -s | 发送信号 | N/A |
| signo | 信号ID。 | [1, 30] |
| pid | 进程ID。 | [1, MAX_INT] |
> ![icon-notice.gif](public_sys-resources/icon-notice.gif) **须知:**
> signo有效范围为[0,64],建议取值范围为[1,30],其余为保留内容。
> signo有效范围为[0, 64],建议取值范围为[1, 30],其余为保留内容。
## 使用指南
- 必须指定发送的信号编号及进程号。
- 进程编号取值范围根据系统配置变化,例如系统最大支持pid为256,则取值范围缩小为[1-256]。
- 进程编号取值范围根据系统配置变化,例如系统最大支持pid为256,则取值范围缩小为[1, 256]。
## 特殊说明
kill命令以及参数 shell端暂不支持。切换mksh版本可全支持,方法:cd bin; ./mksh。
## 使用实例
- 查看当前进程列表,查看需要杀死的进程PID(42)。
```
OHOS:/$ ps
allCpu(%): 4.67 sys, 195.33 idle
......@@ -60,7 +63,7 @@ kill [-l [_signo_] | _-s signo_ | _-signo_] _pid..._
```
- 发送信号9(SIGKILL默认行为为立即终止进程)给42号进程test_demo(用户态进程):kill -s 9 42(kill -9 42效果相同),并查看当前进程列表,42号进程已终止。
```
OHOS:/$ kill -s 9 42
OHOS:/$
......@@ -91,9 +94,9 @@ kill [-l [_signo_] | _-s signo_ | _-signo_] _pid..._
发送成功或失败输出结果如下。
**示例 1** 发送信号给指定进程
**示例1** 发送信号给指定进程
```
OHOS:/$ kill -s 9 42
OHOS:/$
......@@ -102,12 +105,12 @@ OHOS:/$
信号发送成功会显示的提示进程已被杀死。
**示例 2** 信号发送失败
**示例2** 信号发送失败
```
OHOS:/$ kill -100 31
kill: Unknown signal '(null)'
```
信号发送失败,示例2所示原因为信号发送命令参数无效,请排查信号编号及进程编号是否有效。
信号发送失败,示例2所示原因为信号发送命令参数无效,请排查信号编号及进程编号是否有效。
\ No newline at end of file
......@@ -13,11 +13,11 @@ log level [_levelNum_]
## 参数说明
**表1** 参数说明
**表1** 参数说明
| 参数 | 参数说明 | 取值范围 |
| -------- | -------- | -------- |
| levelNum | 配置日志打印等级。 | [0,5] |
| levelNum | 配置日志打印等级。 | [0, 5] |
## 使用指南
......@@ -52,9 +52,9 @@ log level [_levelNum_]
## 输出说明
**示例**:设置当前日志打印级别为3
**示例** 设置当前日志打印级别为3
```
OHOS # log level 3
Set current log level WARN
......
......@@ -13,7 +13,7 @@ memcheck
## 参数说明
## 使用指南
......@@ -34,15 +34,15 @@ memcheck
## 输出说明
**示例1**当前没有内存越界
**示例1** 当前没有内存越界
```
OHOS # memcheck
system memcheck over, all passed!
```
**示例2**出现内存越界
**示例2** 出现内存越界
```
[L0S DLnkCheckMenl 349, memory check
stFreeNodeInfo.pstPrev:0x7e0d31f3 is out of legal mem range[0x80ba5f40, 0х83d00000]
......
......@@ -21,19 +21,20 @@ oom -h | --help
## 参数说明
**表1** 参数说明
**表1** 参数说明
| 参数 | 参数说明 | 取值范围 |
| -------- | -------- | -------- |
| -i&nbsp;[interval] | 设置oom线程任务检查的时间间隔。 | 100ms&nbsp;~&nbsp;10000ms |
| -m&nbsp;[mem&nbsp;byte] | 设置低内存阈值。 | 0MB&nbsp;~&nbsp;1MB,0MB表示不做低内存阈值检查。 |
| -r&nbsp;[mem&nbsp;byte] | 设置pagecache内存回收阈值。 | 低内存阈值&nbsp;~&nbsp;系统可用最大内存。 |
| -h&nbsp;\|&nbsp;--help | 使用帮助。 | N/A |
| 参数 | 参数说明 | 取值范围 |
| ----------------------- | ------------------------------- | ------------------------------------------------------------ |
| -i&nbsp;[interval] | 设置oom线程任务检查的时间间隔。 | [100, 10000] 单位: ms |
| -m&nbsp;[mem&nbsp;byte] | 设置低内存阈值。 | 0MB&nbsp;~&nbsp;1MB,0MB表示不做低内存阈值检查。 |
| -r&nbsp;[mem&nbsp;byte] | 设置pagecache内存回收阈值。 | 低内存阈值 ~ 系统可用最大内存,一个pagecache页一般为4KB,也有16 ~ 64KB的情况。 |
| -h&nbsp;\|&nbsp;--help | 使用帮助。 | N/A |
## 使用指南
参数缺省时,显示oom功能当前配置信息。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 当系统内存不足时,会打印出内存不足的提示信息。
......@@ -49,9 +50,9 @@ oom -h | --help
## 输出说明
**示例1:**oom缺省打印配置信息
**示例1** oom缺省打印配置信息
```
OHOS:/$ oom
[oom] oom loop task status: enabled
......@@ -62,7 +63,7 @@ OHOS:/$ oom
系统内存不足时打印提示信息
```
T:20 Enter:IT MEM 00M 001
[oom] OS is in low memory state
......@@ -109,22 +110,22 @@ traceback 5 -- 1r = 0x20c4df50 fp = 0хb0b0b0b 1r in /1ib/libc.so - -> 0x62f50
```
**示例2**:设置 oom 线程任务检查的时间间隔
**示例2** 设置 oom 线程任务检查的时间间隔
```
OHOS:/$ oom -i 100
[oom] set oom check interval (100)ms successful
```
**表2** 输出说明
**表2** 输出说明
| 输出 | 说明 |
| -------- | -------- |
| [oom]&nbsp;OS&nbsp;is&nbsp;in&nbsp;low&nbsp;memory&nbsp;state<br/>total&nbsp;physical&nbsp;memory:&nbsp;0x1bcf000(byte),&nbsp;used:&nbsp;0x1b50000(byte),&nbsp;free:&nbsp;0x7f000(byte),&nbsp;low&nbsp;memory&nbsp;threshold:&nbsp;0x80000(byte) | 操作系统处于低内存状态。<br/>整个系统可用物理内存为0x1bcf000&nbsp;byte,已经使用了&nbsp;0x1b50000&nbsp;byte,&nbsp;还剩0x7f000&nbsp;byte,当前设置的低内存阈值为0x80000&nbsp;byte。 |
| [oom]&nbsp;candidate&nbsp;victim&nbsp;process&nbsp;init&nbsp;pid:&nbsp;1,&nbsp;actual&nbsp;phy&nbsp;mem&nbsp;byte:&nbsp;82602 | 打印当前各个进程的内存使用情况,init进程实际占用物理内存82602byte。 |
| [oom]&nbsp;candidate&nbsp;victim&nbsp;process&nbsp;UserProcess12&nbsp;pid:&nbsp;12,&nbsp;actual&nbsp;phy&nbsp;mem&nbsp;byte:&nbsp;25951558 | UserProcess12进程实际使用25951558byte内存。 |
| [oom]&nbsp;max&nbsp;phy&nbsp;mem&nbsp;used&nbsp;process&nbsp;UserProcess12&nbsp;pid:&nbsp;12,&nbsp;actual&nbsp;phy&nbsp;mem:&nbsp;25951558 | 当前使用内存最多的进程是UserProcess12。 |
| excFrom:&nbsp;User! | 当系统处于低内存的情况下,UserProcess12进程再去申请内存时失败退出。 |
| 输出 | 说明 |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| [oom]&nbsp;OS&nbsp;is&nbsp;in&nbsp;low&nbsp;memory&nbsp;state<br/>total&nbsp;physical&nbsp;memory:&nbsp;0x1bcf000(byte),&nbsp;used:&nbsp;0x1b50000(byte),&nbsp;free:&nbsp;0x7f000(byte),&nbsp;low&nbsp;memory&nbsp;threshold:&nbsp;0x80000(byte) | 操作系统处于低内存状态。<br/>整个系统可用物理内存为0x1bcf000&nbsp;byte,已经使用了&nbsp;0x1b50000&nbsp;byte,&nbsp;还剩0x7f000&nbsp;byte,当前设置的低内存阈值为0x80000&nbsp;byte。 |
| [oom]&nbsp;candidate&nbsp;victim&nbsp;process&nbsp;init&nbsp;pid:&nbsp;1,&nbsp;actual&nbsp;phy&nbsp;mem&nbsp;byte:&nbsp;82602 | 打印当前各个进程的内存使用情况,init进程实际占用物理内存82602byte。 |
| [oom]&nbsp;candidate&nbsp;victim&nbsp;process&nbsp;UserProcess12&nbsp;pid:&nbsp;12,&nbsp;actual&nbsp;phy&nbsp;mem&nbsp;byte:&nbsp;25951558 | UserProcess12进程实际使用25951558byte内存。 |
| [oom]&nbsp;max&nbsp;phy&nbsp;mem&nbsp;used&nbsp;process&nbsp;UserProcess12&nbsp;pid:&nbsp;12,&nbsp;actual&nbsp;phy&nbsp;mem:&nbsp;25951558 | 当前使用内存最多的进程是UserProcess12。 |
| excFrom:&nbsp;User! | 当系统处于低内存的情况下,UserProcess12进程再去申请内存时失败退出。 |
\ No newline at end of file
......@@ -28,8 +28,8 @@ Debug版本才具备的命令。
## 输出说明
**示例:**查看物理页使用情况
**示例** 查看物理页使用情况
```
OHOS # pmm
phys_seg base size free_pages
......@@ -55,7 +55,7 @@ Vnode number = 67
Vnode memory size = 10720(B)
```
**表1** 输出说明
**表1** 输出说明
| 输出 | 说明 |
| -------- | -------- |
......
......@@ -13,7 +13,7 @@ reboot
## 参数说明
## 使用指南
......@@ -28,4 +28,4 @@ reboot
## 输出说明
......@@ -13,7 +13,7 @@ reset
## 参数说明
## 使用指南
......@@ -28,4 +28,4 @@ reset
## 输出说明
......@@ -13,7 +13,7 @@ sem [_ID__ / fulldata_]
## 参数说明
**表1** 参数说明
**表1** 参数说明
| 参数 | 参数说明 | 取值范围 |
| -------- | -------- | -------- |
......@@ -40,8 +40,8 @@ sem [_ID__ / fulldata_]
## 输出说明
**示例1**查询所有在用的信号量信息
**示例1** 查询所有在用的信号量信息
```
OHOS # sem
SemID Count
......@@ -67,7 +67,7 @@ OHOS # sem
0x00000006 0
```
**表2** 输出说明
**表2** 输出说明
| 输出 | 说明 |
| -------- | -------- |
......@@ -79,8 +79,8 @@ OHOS # sem
>
> ● sem命令的ID参数在[0, 1023]范围内时,返回对应ID的信号量的状态(如果对应ID的信号量未被使用则进行提示);其他取值时返回参数错误的提示。
**示例2:**查询所有在用的信号量信息
**示例2** 查询所有在用的信号量信息
```
OHOS # sem fulldata
Used Semaphore List:
......@@ -113,7 +113,7 @@ Used Semaphore List:
0x38 0x1 0x1 0x404978fc 0x395
```
**表3** 输出说明
**表3** 输出说明
| 输出 | 说明 |
| -------- | -------- |
......
......@@ -13,12 +13,12 @@ stack
## 参数说明
## 使用指南
## 使用实例
......@@ -28,8 +28,8 @@ stack
## 输出说明
**示例:**系统堆栈使用情况
**示例** 系统堆栈使用情况
```
OHOS # stack
stack name cpu id stack addr total size used size
......@@ -40,7 +40,7 @@ OHOS # stack
exc_stack 0 0x405c9000 0x1000 0x0
```
**表1** 输出说明
**表1** 输出说明
| 输出 | 说明 |
| -------- | -------- |
......
......@@ -13,12 +13,12 @@ su [_uid_] [_gid_]
## 参数说明
**表1** 参数说明
**表1** 参数说明
| 参数 | 参数说明 | 取值范围 |
| -------- | -------- | -------- |
| uid | 目标用户的用户id值。 | -&nbsp;为空。<br/>-&nbsp;[0,60000] |
| gid | 目标用户的群组id值。 | -&nbsp;为空。<br/>-&nbsp;[0,60000] |
| uid | 目标用户的用户id值。 | -&nbsp;为空。<br/>-&nbsp;[0, 60000] |
| gid | 目标用户的群组id值。 | -&nbsp;为空。<br/>-&nbsp;[0, 60000] |
## 使用指南
......@@ -37,8 +37,8 @@ su [_uid_] [_gid_]
## 输出说明
**示例:**切换到为uid为1000,gid为1000的用户
**示例** 切换到为uid为1000,gid为1000的用户
```
OHOS # ls
Directory /data/system/param:
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册