1 IDE环境安装

目的:安装LiteOS IDE,并且是使用仿真方式运行。

1.1 IDE安装

HUAWEI LiteOS Studio安装 (gitee.io)

1.2 中文安装

HUAWEI LiteOS Studio扩展介绍 (gitee.io)

  • 选择通过VSIX文件安装:

  • 然后选择设置为中文:

HUAWEI LiteOS Studio安装 (gitee.io)

步骤 1 安装完成后,通过点击菜单栏中的View->Command Palette调出命令输入界面

步骤 2 在命令输入框中输入Configure Display Language,回车,选择需要切换的语言(enzh-cn等),弹出重启IDE完成配置的提示窗口,点击重启,即可完成语言切换

1.3 常用工具安装

HUAWEI LiteOS Studio安装 (gitee.io)

1.3.1 交叉编译工具链

kenneth/GNU-Arm-Embedded-Toolchain - 码云 - 开源中国 (gitee.com)

直接将压缩包解压缩,然后放到IDE的安装目录下:

1.3.2 Make工具

https://gitee.com/rtos_yuan/x-pack-windows-build-tools.git

直接将压缩包解压缩,然后放到IDE的安装目录下:

1.3.3 QEMU仿真器

QEMU for Windows – Installers (64 bit) (weilnetz.de)

直接双击安装包,然后一直Next即可。

1.4 编译运行

1.4.1 编译

首先配置目标板子为QEMU:

然后点击编译按钮:

编译完成状态,会输出一个Bin文件:

1.4.2 运行

STM32工程示例 (gitee.io)

以仿真方式运行,需要先配置烧录器为Simulator:

然后点击烧录:

2 基础学习

1、Windows下搭建LiteOS Studio IDE的仿真环境;

2、了解基本的编译系统;

3、添加用户自定义命令。

代码本地路径:D:\LiteOS\SVN\Code

LiteOS API: Thread (huawei.com)

2.1 编译系统

配置&编译框架简介_LiteOS_编译和开发工具_华为云 (huaweicloud.com)

Huawei LiteOS使用Kconfig文件配置系统,基于GCC/Makefile实现组件化编译。

不论是Linux下使用make menuconfig命令配置系统,还是Windows下使用Huawei LiteOS Studio进行图形化配置,Huawei LiteOS都会同时解析、展示根目录下的.config文件和tools/menuconfig/config.in文件(该文件包含了各个模块的Kconfig文件),同时在开发板的include文件夹下生成menuconfig.hconfig.in文件由Kconfig语言(一种菜单配置语言)编写而成。config.in文件决定了要展示的配置项,.config文件决定了各个配置项的默认值。

Huawei LiteOS通过在根目录下执行make命令完成自动化编译整个工程。对于根目录下的Makefile文件,其中包含了config.mkconfig.mk又包含了los_config.mk,而los_config.mk则包含了各个模块的Makefile.config文件,从而定义了对整个工程的编译链接规则。

Huawei LiteOS目前支持Windows及Linux平台的配置&编译。

  • 对于Windows平台,提供了Huawei LiteOS Studio图形化IDE,用户可直接在 Studio上完成配置&编译。
  • 对于Linux平台,通过menuconfig进行组件化配置及裁剪后,执行make命令完成编译。

2.1.1 menuconfig系统

简介:Huawei LiteOS使用Kconfig文件配置系统。所用的Kconfig语言是一种菜单配置语言,config.in和Kconfig都由该语言编写而成。Huawei LiteOS使用python kconfiglib来解析、展示Kconfig文件。解析Kconfig文件后,会在根目录下生成/更新.config文件,同时在开发板的include文件夹下生成menuconfig.h。使用menuconfig前,需要先安装python和kconfiglib代码。

代码结构:

  • 顶层Makefile中包括Makefile.kconfig文件。
include tools/menuconfig/Makefile.kconfig
  • Makefile.kconfig文件会根据不同的make目标,执行python脚本带不同的参数
.PHONY += menuconfig savemenuconfig
.PHONY += defconfig allyesconfig allnoconfig menuconfig: # 打开menuconfig配置菜单
python $(LITEOSTOPDIR)/tools/menuconfig/usr_config.py
-mv -f $(LITEOS_MENUCONFIG_H) $(LITEOS_PLATFORM_MENUCONFIG_H)
sh $(LITEOSTOPDIR)/components/download.sh savemenuconfig: # 保存当前菜单配置
python $(LITEOSTOPDIR)/tools/menuconfig/usr_config.py savemenuconfig
-mv -f $(LITEOS_MENUCONFIG_H) $(LITEOS_PLATFORM_MENUCONFIG_H) defconfig: # 根据kconfig文件,保存默认配置
python $(LITEOSTOPDIR)/tools/menuconfig/usr_config.py defconfig
-mv -f $(LITEOS_MENUCONFIG_H) $(LITEOS_PLATFORM_MENUCONFIG_H) allyesconfig: # 尽可能将多的配置项设置为'n'
python $(LITEOSTOPDIR)/tools/menuconfig/usr_config.py allyesconfig
-mv -f $(LITEOS_MENUCONFIG_H) $(LITEOS_PLATFORM_MENUCONFIG_H) allnoconfig: # 尽可能将多的配置项设置为'y'
python $(LITEOSTOPDIR)/tools/menuconfig/usr_config.py allnoconfig
-mv -f $(LITEOS_MENUCONFIG_H) $(LITEOS_PLATFORM_MENUCONFIG_H)

因此最终执行的是:tools\menuconfig\usr_config.py脚本,这个是根据python的kconfiglib库进行编写的,可以参考官方的说明:GitHub - ulfalizer/Kconfiglib: A flexible Python 2/3 Kconfig implementation and library

关于kconfig的语法可以参考:Kconfig Language — The Linux Kernel documentation

执行后的输出文件:menuconfig.h(配置头文件)、.config(Makefile使用的配置项选择文件)。

另外,关于python Kconfiglib库和kconfig语法可以参考我的博客:Python Kconfiglib初次学习 - zhengcixi - 博客园 (cnblogs.com)

  • 相关的文件

主要在tools\menuconfig目录以及公共模块目录下的kconfig文件

config.in:顶层kconfig配置文件,里面包含很多source配置项,用来包含其它模块的kconfig文件

Makefile.kconfig:makefile文件

usr_config.py:python kconfiglib库调用脚本

2.1.2 顶层Makefile

Makefile基础知识学习可参考:

https://www.gnu.org/software/make/manual/make.html (GNU make官方文档)

https://seisman.github.io/how-to-write-makefile/overview.html (跟我一起写Makefile 陈皓)

LiteOS_Lab Makefile分析-云社区-华为云 (huaweicloud.com)

编译输出文件目录out\realview-pbx-a9,由于QEMU使用的是realview-pbx-a9开发板进行仿真。

编译依赖部分文件清单

文件 作用
Makefile 顶层Makefile
config.mk 包含los_config.mk
build\mk\los_config.mk 包含了各个模块的Makefile.config文件,定义了对整个工程的编译链接规则
HIDE := @ 控制了编译打印是否输出
build\mk\liteos_tables_ldflags.mk 重要)添加系统命令或者用户自定义命令的命令项参数
build\mk\module.mk 编译各个模块具体的Makefile文件
build\mk\module_lib.mk 编译库的Makefile文件
targets\realview-pbx-a9\Makefile 以编译realview-pbx-a9目标板为例进行说明,这是配置编译该目录下的一些编译选项,具体的编译执行是module.mk文件。

一些配置说明:

build\mk\los_config.mk

# HIDE变量:用于进行输出抑制,如果用户想查看编译过程,可以进行打开;
HIDE := @ # 默认输出抑制,不打印编译过程中的字符串
HIDE := # 输出不抑制,打印编译过程的字符串 # MODULE变量:编译模块的Makefile文件路径
MODULE = $(MK_PATH)/module.mk # MODULE_LIB变量:编译输出库文件的Makefile路径
MODULE_LIB = $(MK_PATH)/module_lib.mk # OUT变量:编译输出文件路径
# BUILD变量:编译输出目标文件路径
OUT = $(LITEOSTOPDIR)/out/$(LITEOS_PLATFORM)
BUILD = $(OUT)/obj
MK_PATH = $(LITEOSTOPDIR)/build/mk

config.mk

# $(TOP_LD_PATH) = d:/LiteOS/SVN/Code 源码顶层路径
# $(LITEOS_MK_PATH) = d:/LiteOS/SVN/Code/build/mk 编译Makefile路径
# $(LITEOS_SUBDIRS) = arch/arm/cortex_a_r targets/bsp targets/realview-pbx-a9 kernel compat lib osdepends components demos shell 编译模块清单
# $(LIB_BIGODIR) = d:/LiteOS/SVN/Code/out/realview-pbx-a9/lib/obj 编译输出目标文件路径 TOP_LD_PATH = $(LITEOSTOPDIR)
LITEOS_MK_PATH = $(MK_PATH)
LITEOS_SUBDIRS = $(LIB_SUBDIRS)
LIB_BIGODIR = $(LITEOS_LIB_BIGODIR)

顶层Makefile执行的大致流程:

可以看出,需要编译以下的模块:

arch/arm/cortex_a_r targets/bsp targets/realview-pbx-a9 kernel compat lib osdepends components demos shell

2.1.3 模块Makefile

targets\realview-pbx-a9\Makefile为例进行说明:

# 编译的本地源文件目录,包括C文件和S文件
LOCAL_SRCS += $(ASSRCS) # 编译的本地头文件目录
LOCAL_INCLUDE += \
-I $(LITEOSTOPDIR)/targets/$(LITEOS_PLATFORM)/include \
-I $(LITEOSTOPDIR)/targets/$(LITEOS_PLATFORM)/include/hisoc \
-I $(LITEOSTOPDIR)/targets/$(LITEOS_PLATFORM)/include/asm \
-I $(LITEOSTOPDIR)/targets/$(LITEOS_PLATFORM)/include/pm \
-I $(LITEOSTOPDIR)/targets/$(LITEOS_PLATFORM)/uart \
-I $(LITEOSTOPDIR)/targets/$(LITEOS_PLATFORM) \
-I $(LITEOSTOPDIR)/include BOARD_DEF += $(C_DEFS) LOCAL_FLAGS := $(BOARD_DEF) $(LOCAL_INCLUDE) $(LITEOS_GCOV_OPTS) # $(MK_PATH) = d:/ESC225/LiteOS/SVN/Code/build/mk
# MODULE = $(MK_PATH)/module.mk
include $(MODULE)

最终就是使用build\mk\module.mk进行模块的编译。

因此,如果想增加编译的C文件,仅需要在变量LOCAL_SRCS变量中进行添加,想添加头文件,就在LOCAL_INCLUDE变量中添加,比如新增编译bsp/cmds目录下的文件:

ifeq ($(LOSCFG_LSW_SHELL), y)
LOCAL_SRCS += $(wildcard bsp/cmds/*.c)
LOCAL_INCLUDE += -I $(LITEOSTOPDIR)/targets/bsp/cmds
endif

2.2 启动流程

内核启动流程_LiteOS_内核_华为云 (huaweicloud.com)

2.3 Shell命令添加方法

概述_LiteOS_维测指南_Shell_华为云 (huaweicloud.com)

Huawei LiteOS的Shell模块为用户提供下面几个接口,接口详细信息可以查看API参考。

功能分类 接口名 描述
注册命令 SHELLCMD_ENTRY 静态注册命令
osCmdReg 动态注册命令

静态注册命令方式一般用于注册系统常用命令,动态注册命令方式一般用于注册用户命令。

静态注册命令有5个入参,动态注册命令有4个入参。下面除去第一个入参是静态注册独有的,剩余的四个入参两个注册命令是一致的。

/* 静态注册命令宏 */
#define SHELLCMD_ENTRY(l, cmdType, cmdKey, paraNum, cmdHook) \
CmdItem l LOS_HAL_TABLE_ENTRY(shellcmd) = { \
cmdType, \
cmdKey, \
paraNum, \
cmdHook \
} /* 动态注册命令函数 */
extern UINT32 osCmdReg(CmdType cmdType, CHAR *cmdKey, UINT32 paraNum, CmdCallBackFunc cmdProc);
  • 第一个入参:这个入参是静态注册独有的,动态注册中没有这个入参。命令变量名,用于设置链接选项(build/mk/liteos_tables_ldflags.mkLITEOS_TABLES_LDFLAGS变量)。

    例如变量名为ls_shellcmd,链接选项就应该设置为:LITEOS_TABLES_LDFLAGS += -uls_shellcmd

  • 第二个入参:命令类型,目前支持两种命令类型。

    • CMD_TYPE_EX:不支持标准命令参数输入,会把用户填写的命令关键字屏蔽掉。例如:输入ls /ramfs,传入给命令处理函数的参数只有/ramfs,对应于命令处理函数中的argv[0],而ls命令关键字并不会被传入。
    • CMD_TYPE_STD:支持的标准命令参数输入,所有输入的字符都会通过命令解析后被传入。例如:输入ls /ramfsls/ramfs都会被传入命令处理函数,分别对应于命令处理函数中的argv[0]argv[1]
  • 第三个入参:命令关键字,是命令处理函数在Shell中对应的名称。命令关键字必须唯一,即两个不同的命令项不能有相同的命令关键字,否则只会执行其中一个。Shell在执行用户命令时,如果存在多个命令关键字相同的命令,只会执行在help命令中排在最前面的那个。

  • 第四个入参:命令处理函数的入参最大个数。

    • 静态注册命令暂不支持设置。
    • 动态注册命令支持设置不超过32的入参最大个数,或者设置为XARGS(其在代码中被定义为0xffffffff)表示不限制参数个数。
  • 第五个入参:命令处理函数名,即在Shell中执行命令时被调用的函数。

2.3.1 新增命令开发流程

(1)定义Shell命令处理函数

Shell命令处理函数用于处理注册的命令。例如定义一个命令处理函数osShellCmdLs,处理ls命令,并在头文件中声明命令处理函数原型。

int osShellCmdLs(int argc, const char **argv);

命令处理函数的参数与C语言中main函数的参数类似,包括两个入参:

  • argc:Shell命令的参数个数。个数中是否包括命令关键字,和注册命令时的命令类型有关。
  • argv:为指针数组,每个元素指向一个字符串,该字符串就是执行shell命令时传入命令处理函数的参数。参数中是否包括命令关键字,和注册命令时的命令类型有关。

(2)注册命令:有静态注册命令和系统运行时动态注册命令两种注册方式。

(3)对于静态注册命令方式,在build/mk/liteos_tables_ldflags.mk中设置链接选项(LITEOS_TABLES_LDFLAGS变量)。

(4)通过make menuconfig使能Shell,详见配置项

(5)编译烧录系统后,可以执行新增的Shell命令。

2.3.2 静态注册编程实例

本实例演示如何使用静态注册命令方式新增一个名为test的Shell命令,步骤如下:

  1. 定义一个新增命令所要调用的命令处理函数cmd_test
  2. 使用SHELLCMD_ENTRY函数添加新增命令项。
  3. liteos_tables_ldflags.mk中添加链接该新增命令项参数。
  4. 通过make menuconfig使能Shell
  5. 重新编译代码后运行。

1、定义命令所要调用的命令处理函数cmd_test

#include "shell.h"
#include "shcmd.h" int cmd_test(int argc, const char **argv)
{
int i = 0; printf("hello everybody!\n");
printf("argc = %d\n", argc);
for (i = 0; i < argc; i++) {
printf("args[%u] = %s\n", i, argv[i]);
} return 0;
}

2、添加新增命令项

SHELLCMD_ENTRY(test_shellcmd, CMD_TYPE_EX, "test", 0, (CMD_CBK_FUNC)cmd_test);

3、在链接选项中添加链接该新增命令项参数,这里新增加了一个变量LITEOS_TABLES_LSW_LDFLAGS

build/mk/liteos_tables_ldflags.mkLITEOS_TABLES_LDFLAGS项下添加-utest_shellcmd

LITEOS_TABLES_LSW_LDFLAGS := \
-utest_shellcmd LITEOS_TABLES_LDFLAGS := \
... ... \
$(LITEOS_TABLES_LSW_LDFLAGS)

4、通过make menuconfig使能Shell,即设置LOSCFG_SHELL=y

5、重新编译代码,烧录执行,可以看出第一个字符串test没有当作参数。

Huawei LiteOS # test 0 1
hello everybody!
argc = 2
args[0] = 0
args[1] = 1

2.3.3 动态注册编程实例

本实例演示如何使用动态注册命令方式新增一个名为test的Shell命令。

  1. 定义一个新增命令所要调用的命令处理函数cmd_test
  2. 使用osCmdReg函数添加新增命令项。
  3. 通过make menuconfig使能Shell。
  4. 重新编译代码后运行。

1、定义命令所要调用的命令处理函数cmd_test

#include "shell.h"
#include "shcmd.h" int cmd_test(int argc, const char **argv)
{
int i = 0; printf("hello everybody!\n");
printf("argc = %d\n", argc);
for (i = 0; i < argc; i++) {
printf("args[%u] = %s\n", i, argv[i]);
} return 0;
}

2、app_init函数中调用osCmdReg函数动态注册命令,必须放在DemoEntry函数之后:

__attribute__((weak)) VOID app_init(VOID)
{
printf("app init!!!!\n");
(VOID)DemoEntry(); osCmdReg(CMD_TYPE_EX, "test", 0, (CMD_CBK_FUNC)cmd_test); }

3、通过make menuconfig使能Shell,即设置LOSCFG_SHELL=y

4、重新编译代码、烧录、执行:

Huawei LiteOS # test 1 2 3 4 5
hello everybody!
argc = 5
args[0] = 1
args[1] = 2
args[2] = 3
args[3] = 4
args[4] = 5

2.3.4 系统命令

LiteOS自带了一些系统命令,可以参考:

使能系统命令_LiteOS_维测指南_Shell_系统命令参考_华为云 (huaweicloud.com)

一些常用的命令:

  • help命令:用于显示当前操作系统内所有的Shell命令。
  • date命令:用于查询及设置系统时间。
  • uname命令:用于显示操作系统的名称,系统编译时间,版本信息等。
  • task命令:用于查询系统任务信息。
  • free命令:可显示系统内存的使用情况,同时显示系统的text段、data段、rodata段、bss段大小。
  • cpup命令:用于查询系统CPU的占用率,并以百分比显示占用率。

2.4 任务

2.4.1 简介

概述_LiteOS_内核_任务_华为云 (huaweicloud.com)

Huawei LiteOS的任务模块具有如下特性:

  • 支持多任务。
  • 一个任务表示一个线程。
  • 抢占式调度机制,高优先级的任务可打断低优先级任务,低优先级任务必须在高优先级任务阻塞或结束后才能得到调度。
  • 相同优先级任务支持时间片轮转调度方式。
  • 共有32个优先级[0-31],最高优先级为0,最低优先级为31。

Huawei LiteOS 的任务管理模块提供下面几种功能,接口详细信息可以查看API参考。

功能分类 描述 接口名
创建和删除任务 创建任务,并使该任务进入suspend状态,不对该任务进行调度。如果需要调度,可以调用LOS_TaskResume使该任务进入ready状态 LOS_TaskCreateOnly
创建任务,并使该任务进入ready状态,如果就绪队列中没有更高优先级的任务,则运行该任务 LOS_TaskCreate
创建任务,任务栈由用户传入,并使该任务进入suspend状态,不对该任务进行调度。如果需要调度,可以调用LOS_TaskResume使该任务进入ready状态 LOS_TaskCreateOnlyStatic
创建任务,任务栈由用户传入,并使该任务进入ready状态,如果就绪队列中没有更高优先级的任务,则运行该任务 LOS_TaskCreateStatic
删除指定的任务 LOS_TaskDelete
控制任务状态 恢复挂起的任务,使该任务进入ready状态 LOS_TaskResume
挂起指定的任务,然后切换任务 LOS_TaskSuspend
任务延时等待,释放CPU,等待时间到期后该任务会重新进入ready状态 LOS_TaskDelay
当前任务释放CPU,并将其移到具有相同优先级的就绪任务队列的末尾 LOS_TaskYield
控制任务调度 锁任务调度,但任务仍可被中断打断 LOS_TaskLock
解锁任务调度 LOS_TaskUnlock
控制任务优先级 设置当前任务的优先级 LOS_CurTaskPriSet
设置指定任务的优先级 LOS_TaskPriSet
获取指定任务的优先级 LOS_TaskPriGet
设置任务亲和性 设置指定任务的运行cpu集合(该函数仅在SMP模式下支持) LOS_TaskCpuAffiSet
回收任务栈资源 回收所有待回收的任务栈资源 LOS_TaskResRecycle
获取任务信息 获取当前任务的ID LOS_CurTaskIDGet
获取指定任务的信息,包括任务状态、优先级、任务栈大小、栈顶指针SP、任务入口函数、已使用的任务栈大小等 LOS_TaskInfoGet
获取指定任务的运行cpu集合(该函数仅在SMP模式下支持) LOS_TaskCpuAffiGet
任务信息维测 注册任务上下文切换的钩子函数。只有开启LOSCFG_BASE_CORE_TSK_MONITOR宏开关后,这个钩子函数才会在任务发生上下文切换时被调用 LOS_TaskSwitchHookReg
任务空闲处理回调 注册空闲任务钩子函数,当系统空闲时调用 LOS_IdleHandlerHookReg

2.4.2 编程示例

描述:本实例介绍基本的任务操作方法,包含2个不同优先级任务的创建、任务延时、任务锁与解锁调度、挂起和恢复等操作,阐述任务优先级调度的机制以及各接口的应用。

UINT32 g_taskHiId;
UINT32 g_taskLoId;
#define TSK_PRIOR_HI 4
#define TSK_PRIOR_LO 5 UINT32 Example_TaskHi(VOID)
{
UINT32 ret; printf("Enter TaskHi Handler.\r\n"); /* 延时2个Tick,延时后该任务会挂起,执行剩余任务中最高优先级的任务(g_taskLoId任务) */
ret = LOS_TaskDelay(2);
if (ret != LOS_OK) {
printf("Delay Task Failed.\r\n");
return LOS_NOK;
} /* 2个Tick时间到了后,该任务恢复,继续执行 */
printf("TaskHi LOS_TaskDelay Done.\r\n"); /* 挂起自身任务 */
ret = LOS_TaskSuspend(g_taskHiId);
if (ret != LOS_OK) {
printf("Suspend TaskHi Failed.\r\n");
return LOS_NOK;
}
printf("TaskHi LOS_TaskResume Success.\r\n"); return ret;
} /* 低优先级任务入口函数 */
UINT32 Example_TaskLo(VOID)
{
UINT32 ret; printf("Enter TaskLo Handler.\r\n"); /* 延时2个Tick,延时后该任务会挂起,执行剩余任务中最高优先级的任务(背景任务) */
ret = LOS_TaskDelay(2);
if (ret != LOS_OK) {
printf("Delay TaskLo Failed.\r\n");
return LOS_NOK;
} printf("TaskHi LOS_TaskSuspend Success.\r\n"); /* 恢复被挂起的任务g_taskHiId */
ret = LOS_TaskResume(g_taskHiId);
if (ret != LOS_OK) {
printf("Resume TaskHi Failed.\r\n");
return LOS_NOK;
} printf("TaskHi LOS_TaskDelete Success.\r\n"); return ret;
} /* 任务测试入口函数,创建两个不同优先级的任务 */
UINT32 Example_TskCaseEntry(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S initParam; /* 锁任务调度,防止新创建的任务比本任务高而发生调度 */
LOS_TaskLock(); printf("LOS_TaskLock() Success!\r\n"); initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_TaskHi;
initParam.usTaskPrio = TSK_PRIOR_HI;
initParam.pcName = "TaskHi";
initParam.uwStackSize = LOSCFG_TASK_MIN_STACK_SIZE;
initParam.uwResved = LOS_TASK_STATUS_DETACHED;
/* 创建高优先级任务,由于锁任务调度,任务创建成功后不会马上执行 */
ret = LOS_TaskCreate(&g_taskHiId, &initParam);
if (ret != LOS_OK) {
LOS_TaskUnlock(); printf("Example_TaskHi create Failed!\r\n");
return LOS_NOK;
} printf("Example_TaskHi create Success!\r\n"); initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_TaskLo;
initParam.usTaskPrio = TSK_PRIOR_LO;
initParam.pcName = "TaskLo";
initParam.uwStackSize = LOSCFG_TASK_MIN_STACK_SIZE;
initParam.uwResved = LOS_TASK_STATUS_DETACHED; /* 创建低优先级任务,由于锁任务调度,任务创建成功后不会马上执行 */
ret = LOS_TaskCreate(&g_taskLoId, &initParam);
if (ret != LOS_OK) {
LOS_TaskUnlock(); printf("Example_TaskLo create Failed!\r\n");
return LOS_NOK;
} printf("Example_TaskLo create Success!\r\n"); /* 解锁任务调度,此时会发生任务调度,执行就绪队列中最高优先级任务 */
LOS_TaskUnlock(); return LOS_OK;
}

运行:

Huawei LiteOS # test-cmd
LOS_TaskLock() Success!
Example_TaskHi create Success!
Enter TaskHi Handler.
Example_TaskLo create Success!
Enter TaskLo Handler. Huawei LiteOS # TaskHi LOS_TaskDelay Done.
TaskHi LOS_TaskSuspend Success.
TaskHi LOS_TaskResume Success.
TaskHi LOS_TaskDelete Success.

2.5 互斥锁

2.5.1 简介

概述_LiteOS_内核_互斥锁_华为云 (huaweicloud.com)

互斥锁又称互斥型信号量,是一种特殊的二值性信号量,用于实现对临界资源的独占式处理。另外,互斥锁可以解决信号量存在的优先级翻转问题。

任意时刻互斥锁只有两种状态,开锁或闭锁。当任务持有时,这个任务获得该互斥锁的所有权,互斥锁处于闭锁状态。当该任务释放锁后,任务失去该互斥锁的所有权,互斥锁处于开锁状态。当一个任务持有互斥锁时,其他任务不能再对该互斥锁进行开锁或持有。

用互斥锁处理临界资源的同步访问时,如果有任务访问该资源,则互斥锁为加锁状态。此时其他任务如果想访问这个临界资源则会被阻塞,直到互斥锁被持有该锁的任务释放后,其他任务才能重新访问该公共资源,此时互斥锁再次上锁,如此确保同一时刻只有一个任务正在访问这个临界资源,保证了临界资源操作的完整性。

Huawei LiteOS的互斥锁模块为用户提供下面几种功能,接口详细信息可以查看API参考。

功能分类 接口名 描述
创建/删除互斥锁 LOS_MuxCreate 创建互斥锁
LOS_MuxDelete 删除指定互斥锁
申请/释放互斥锁 LOS_MuxPend 申请指定互斥锁
LOS_MuxPost 释放指定互斥锁

申请互斥锁有三种模式:无阻塞模式、永久阻塞模式、定时阻塞模式。

  • 无阻塞模式:即任务申请互斥锁时,入参timeout等于0。若当前没有任务持有该互斥锁,或者持有该互斥锁的任务和申请该互斥锁的任务为同一个任务,则申请成功,否则立即返回申请失败。
  • 永久阻塞模式:即任务申请互斥锁时,入参timeout等于0xFFFFFFFF。若当前没有任务持有该互斥锁,则申请成功。否则,任务进入阻塞态,系统切换到就绪任务中优先级最高者继续执行。任务进入阻塞态后,直到有其他任务释放该互斥锁,阻塞任务才会重新得以执行。
  • 定时阻塞模式:即任务申请互斥锁时,0<timeout<0xFFFFFFFF。若当前没有任务持有该互斥锁,则申请成功。否则该任务进入阻塞态,系统切换到就绪任务中优先级最高者继续执行。任务进入阻塞态后,超时前如果有其他任务释放该互斥锁,则该任务可成功获取互斥锁继续执行,若超时前未获取到该互斥锁,接口将返回超时错误码。

释放互斥锁:

  • 如果有任务阻塞于该互斥锁,则唤醒被阻塞任务中优先级最高的,该任务进入就绪态,并进行任务调度。
  • 如果没有任务阻塞于该互斥锁,则互斥锁释放成功。

2.5.2 编程示例

本实例实现如下流程:

  1. 任务Example_TaskEntry创建一个互斥锁,锁任务调度,创建两个任务Example_MutexTask1Example_MutexTask2Example_MutexTask2优先级高于Example_MutexTask1,解锁任务调度,然后Example_TaskEntry任务休眠300Tick
  2. Example_MutexTask2被调度,以永久阻塞模式申请互斥锁,并成功获取到该互斥锁,然后任务休眠100TickExample_MutexTask2挂起,Example_MutexTask1被唤醒。
  3. Example_MutexTask1以定时阻塞模式申请互斥锁,等待时间为10Tick,因互斥锁仍被Example_MutexTask2持有,Example_MutexTask1挂起。10Tick超时时间到达后,Example_MutexTask1被唤醒,以永久阻塞模式申请互斥锁,因互斥锁仍被Example_MutexTask2持有,Example_MutexTask1挂起。
  4. 100Tick休眠时间到达后,Example_MutexTask2被唤醒,释放互斥锁,唤醒Example_MutexTask1Example_MutexTask1成功获取到互斥锁后,释放锁。
  5. 300Tick休眠时间到达后,任务Example_TaskEntry被调度运行,删除互斥锁,删除两个任务。

代码实现:

#include "los_typedef.h"
#include "los_mux.h" /* 互斥锁句柄id */
UINT32 g_testMux;
/* 任务ID */
UINT32 g_testTaskId01;
UINT32 g_testTaskId02; VOID Example_MutexTask1(VOID)
{
UINT32 ret; printf("task1 try to get mutex, wait 10 ticks.\n");
/* 申请互斥锁 */
ret = LOS_MuxPend(g_testMux, 10);
if (ret == LOS_OK) {
printf("task1 get mutex g_testMux.\n");
/* 释放互斥锁 */
LOS_MuxPost(g_testMux);
return;
} else if (ret == LOS_ERRNO_MUX_TIMEOUT ) {
printf("task1 timeout and try to get mutex, wait forever.\n");
/* 申请互斥锁 */
ret = LOS_MuxPend(g_testMux, LOS_WAIT_FOREVER);
if (ret == LOS_OK) {
printf("task1 wait forever, get mutex g_testMux.\n");
/* 释放互斥锁 */
LOS_MuxPost(g_testMux);
return;
}
}
return;
} VOID Example_MutexTask2(VOID)
{
printf("task2 try to get mutex, wait forever.\n");
/* 申请互斥锁 */
(VOID)LOS_MuxPend(g_testMux, LOS_WAIT_FOREVER); printf("task2 get mutex g_testMux and suspend 100 ticks.\n"); /* 任务休眠100Ticks */
LOS_TaskDelay(100); printf("task2 resumed and post the g_testMux\n");
/* 释放互斥锁 */
LOS_MuxPost(g_testMux);
return;
} UINT32 Example_TaskEntry(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S task1;
TSK_INIT_PARAM_S task2; /* 创建互斥锁 */
LOS_MuxCreate(&g_testMux); /* 锁任务调度 */
LOS_TaskLock(); /* 创建任务1 */
memset(&task1, 0, sizeof(TSK_INIT_PARAM_S));
task1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_MutexTask1;
task1.pcName = "MutexTsk1";
task1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task1.usTaskPrio = 15;
ret = LOS_TaskCreate(&g_testTaskId01, &task1);
if (ret != LOS_OK) {
printf("task1 create failed.\n");
return LOS_NOK;
} /* 创建任务2 */
memset(&task2, 0, sizeof(TSK_INIT_PARAM_S));
task2.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_MutexTask2;
task2.pcName = "MutexTsk2";
task2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task2.usTaskPrio = 4;
ret = LOS_TaskCreate(&g_testTaskId02, &task2);
if (ret != LOS_OK) {
printf("task2 create failed.\n");
return LOS_NOK;
} /* 解锁任务调度 */
LOS_TaskUnlock();
/* 休眠300Ticks */
LOS_TaskDelay(300); /* 删除互斥锁 */
LOS_MuxDelete(g_testMux); /* 删除任务1 */
ret = LOS_TaskDelete(g_testTaskId01);
if (ret != LOS_OK) {
printf("task1 delete failed .\n");
return LOS_NOK;
}
/* 删除任务2 */
ret = LOS_TaskDelete(g_testTaskId02);
if (ret != LOS_OK) {
printf("task2 delete failed .\n");
return LOS_NOK;
} return LOS_OK;
}

运行测试:

Huawei LiteOS # test
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. Huawei LiteOS # test
task1 try to get mutex, wait 10 ticks.
task2 try to get mutex, wait forever.
task1 get mutex g_testMux.
task2 get mutex g_testMux and suspend 100 ticks.
task2 resumed and post the g_testMux

可以看出,任务1和任务2谁先运行是随机的,不是官网说的那样。

LiteOS基础学习的更多相关文章

  1. salesforce 零基础学习(五十二)Trigger使用篇(二)

    第十七篇的Trigger用法为通过Handler方式实现Trigger的封装,此种好处是一个Handler对应一个sObject,使本该在Trigger中写的代码分到Handler中,代码更加清晰. ...

  2. 如何从零基础学习VR

    转载请声明转载地址:http://www.cnblogs.com/Rodolfo/,违者必究. 近期很多搞技术的朋友问我,如何步入VR的圈子?如何从零基础系统性的学习VR技术? 本人将于2017年1月 ...

  3. IOS基础学习-2: UIButton

    IOS基础学习-2: UIButton   UIButton是一个标准的UIControl控件,UIKit提供了一组控件:UISwitch开关.UIButton按钮.UISegmentedContro ...

  4. HTML5零基础学习Web前端需要知道哪些?

    HTML零基础学习Web前端网页制作,首先是要掌握一些常用标签的使用和他们的各个属性,常用的标签我总结了一下有以下这些: html:页面的根元素. head:页面的头部标签,是所有头部元素的容器. b ...

  5. python入门到精通[三]:基础学习(2)

    摘要:Python基础学习:列表.元组.字典.函数.序列化.正则.模块. 上一节学习了字符串.流程控制.文件及目录操作,这节介绍下列表.元组.字典.函数.序列化.正则.模块. 1.列表 python中 ...

  6. python入门到精通[二]:基础学习(1)

    摘要:Python基础学习: 注释.字符串操作.用户交互.流程控制.导入模块.文件操作.目录操作. 上一节讲了分别在windows下和linux下的环境配置,这节以linux为例学习基本语法.代码部分 ...

  7. CSS零基础学习笔记.

    酸菜记 之 CSS的零基础. 这篇是我自己从零基础学习CSS的笔记加理解总结归纳的,如有不对的地方,请留言指教, 学前了解: CSS中字母是不分大小写的; CSS文件可以使用在各种程序文件中(如:PH ...

  8. Yaf零基础学习总结5-Yaf类的自动加载

    Yaf零基础学习总结5-Yaf类的自动加载 框架的一个重要功能就是类的自动加载了,在第一个demo的时候我们就约定自己的项目的目录结构,框架就基于这个目录结构来自动加载需要的类文件. Yaf在自启动的 ...

  9. Yaf零基础学习总结4-Yaf的配置文件

    在上一节的hello yaf当中我们已经接触过了yaf的配置文件了, Yaf和用户共用一个配置空间, 也就是在Yaf_Application初始化时刻给出的配置文件中的配置. 作为区别, Yaf的配置 ...

  10. qml基础学习 Canvas画笔

    一.画布元素 自qt4.7发布qml以来,qml也在一直不断的完善中,在qt4时代使用qml时如果需要异形图,那我们只能让设计师来切图,这样的感觉是很不爽的,总感觉开发没有那么犀利.但是到了qt5这一 ...

随机推荐

  1. VulnHub-Jangow-01-1.0.1打靶记录

    知识点 NMAP参数 -sV 获取系统信息 -sT TCP扫描可能会留下日志记录 -sC 使用默认脚本(在-A模式下不需要) -p1-xxx 扫描端口号 -p- ==>等价于 -p1-65535 ...

  2. hibernate4升级5带来的一些参数变化

    public String hqlToHibernate5(String hql) { String[] tmp = hql.split(" "); String hqlTmp = ...

  3. spring-jdbc5新特性,一个配置文件解决临时修改数据库的问题

    import java.sql.SQLException; import java.util.List; import java.util.Map; import javax.sql.DataSour ...

  4. Unity性能优化——资源优化(一)

    实际项目中发现的许多问题都是源自无心之过:临时的"测试"更改和疲惫不堪的开发人员的误点击可能会暗地里添加性能不良的资源或更改现有资源的导入设置. 对于任何大规模的项目,最好是将防止 ...

  5. 力扣1050(MySQL)-合作过至少三次的演员和导演(简单)

    题目: ActorDirector 表: 写一条SQL查询语句获取合作过至少三次的演员和导演的 id 对 (actor_id, director_id) 示例:  建表语句: 1 create tab ...

  6. 平安保险基于 SPI 机制的 RocketMQ 定制化应用

    ​简介:本文讲讲述平安保险为何选择 RocketMQ,以及在确定使用消息中间件后,又是如何去选择哪款消息中间件的. 作者:孙园园|平安人寿资深开发 为什么选用 RocketMQ 首先跟大家聊聊我们为什 ...

  7. Nacos 2.0 性能提升十倍,贡献者 80% 以上来自阿里之外

    简介: 3 月 20 日,Nacos 2.0 正式发布.Nacos 是阿里巴巴在 2018 年开源的一个更易于构建云原生应用的动态服务发现.配置管理和服务管理平台,也可以理解为微服务的注册中心 + 配 ...

  8. 浅谈 Linux 高负载的系统化分析

    ​简介: 浅谈 Linux 高负载的系统化分析,阿里云系统组工程师杨勇通过对线上各种问题的系统化分析. 讲解 Linux Load 高如何排查的话题属于老生常谈了,但多数文章只是聚焦了几个点,缺少整体 ...

  9. dotnet C# 通过 Vortice 使用 Direct2D 特效入门

    本文将告诉大家如何通过 Vortice 使用 D2D 的特效 本文属于 DirectX 系列博客,更多 DirectX 和 D2D 以及 Vortice 库的博客,请参阅我的 博客导航 上一篇: Di ...

  10. 2019-10-7-WPF-will-break-when-an-exception-be-throw-in-the-StylusPlugIn

    title author date CreateTime categories WPF will break when an exception be throw in the StylusPlugI ...