鸿蒙Hi3861学习五-Huawei LiteOS-M(任务管理)

news2024/11/16 9:23:01

一、任务简介

        关于任务的相关介绍,之前文章有比较详细的介绍,这里不做过多解释,可以参考如下文章:FreeRTOS学习二(任务)_t_guest的博客-CSDN博客

         而LiteOS的主要特性可以总结为如下几点:

  1. LiteOS的任务模块可以给用户提供多个任务,实现了任务之间的切换和通信,帮助用户管理业务程序流程。
  2. LiteOS中的任务是抢占式调度机制高优先级的任务可以打断低优先级任务,低优先级任务必须在高优先级任务阻塞或结束后才能得到调度,同时支持时间片轮转调度方式
  3. LiteOS的任务默认有32个优先级(0-31),最高优先级为0,最低优先级为31.

二、任务

任务状态        

        任务状态通常分为一下四种:

  • 就绪(Ready):该任务在就绪列表中,只等待CPU
  • 运行(Running):该任务正在执行
  • 阻塞(Blocked):该任务不在就绪列表中。包含任务被挂起、任务被延时、任务正在等待信号量、读写队列或等待读写事件等。
  • 退出态(Dead):任务运行结束等待系统回收资源

名词介绍

任务ID:在任务创建时通过参数返回给用户,作为任务的一个非常重要的标识

任务优先级:优先级标识任务执行的优先顺序

任务入口函数:每个新任务得到调度后将执行的函数

任务控制块TCB:每个任务都含有一个任务控制快(TCB-Task Control Block)。TCB包含了任务上下文栈指针(stack pointer)、任务状态、任务优先级、任务ID、任务名、任务栈大小等信息。TCB可以反映出每个任务的运行情况

任务栈:每个任务都拥有一个独立的栈空间,称为任务栈。

任务上下文:任务在运行过程中使用到的一些资源,如寄存器等,我们称为任务上下文。LitsOS在任务挂起的时候会将本任务的任务上下文信息,保存在自己的任务栈里面,以便任务恢复后,从栈空间中恢复挂起时的上下文信息,从而继续执行被挂起时被打断的代码。

任务切换:任务切换包含获取就绪列表中最高优先级任务、切出任务上下文保存、切入任务上下文恢复等动作。

任务状态迁移

就绪态->运行态:任务创建后进入就绪态,发生任务切换时,就绪列表中最高优先级的任务被执行,从而进入运行态,但此刻该任务依旧在就绪列表中。

运行态->阻塞态:任务运行因挂起、读信号量等待等,在就绪列表中被删除进入阻塞态。

阻塞态->就绪态(阻塞态->运行态):阻塞的任务被恢复后(任务恢复、延时时间超时、读信号量超时或读到信号量等),此时被恢复的任务就会被加入就绪列表,从而由阻塞态变成就绪态。此时如果被恢复任务的优先级高于正在运行任务的优先级,则会发生任务切换,将该任务由就绪态变成运行态。

就绪态->阻塞态:任务在就绪态被挂起,进而进入阻塞态。

运行态->就绪态:有更高优先级任务创建或恢复后,发生任务切换而进入就绪列表。

运行态->退出态:任务运行结束,内核自动将此任务删除,此时由运行态变为退出态。

阻塞态->退出态:阻塞的任务调用删除接口,任务状态由阻塞态变成退出态。

三、 实操

        LiteOS-m的内核代码是从CMSIS-RTOS2接口中封装而来。

1.什么是CMSIS-RTOS2接口

        CMSIS是Cortex微控制器软件接口标准(Cortex Microcontroller Software Interface Standard)是ARM和一些编译器厂家以及半导体厂家共同遵循的一套标准,是由ARM专门针对Cortex-M系列提出的标准。在该标准的约定下,ARM和芯片厂商会提供一些通用的API接口来访问Cortex内核以及一些专用外设,以减少更换芯片以及开发工具等移植工作所带来的金钱以及时间上的消耗。

        CMSIS-RTOS2(CMSIS-RTOS API Version 2)是Arm® Cortex®-M 处理器的通用的RTOS接口。为需要RTOS功能的软件组件提供了标准化的API。

        CMSIS-RTOS2是一个通用的API,它与底层的RTOS内核无关,写应用程序的程序员在用户代码中调用CMSIS-RTOS2 API函数,可以更方便地将应用程序从一个RTOS到另一个RTOS,使用CMSIS-RTOS2 API的中间件也可以避免很多不必要的移植工作。

        官方API参考:Main Page

         SDK中内核的源码文件在kernel/liteos_m/components/cmsis中。

2.常用函数

        osThreadNew

       函数功能

        创建一个新的任务。

        函数原型

osThreadId_t osThreadNew(osThreadFunc_t func, void *argument, const osThreadAttr_t *attr)

        参数

        func:线程的回调函数

        argument:作为启动参数传递给线程函数的指针。一般为NULL

        attr:线程属性。线程的相关属性都在这里设置,包括线程堆栈大小,优先级等等。主要看osThreadAttr_t数据类型。

typedef struct {
  /** Thread name */
  const char                   *name;
  /** Thread attribute bits */
  uint32_t                 attr_bits;
  /** Memory for the thread control block */
  void                      *cb_mem;
  /** Size of the memory for the thread control block */
  uint32_t                   cb_size;
  /** Memory for the thread stack */
  void                   *stack_mem;
  /** Size of the thread stack */
  uint32_t                stack_size;
  /** Thread priority */
  osPriority_t              priority;
  /** TrustZone module of the thread */
  TZ_ModuleId_t            tz_module;
  /** Reserved */
  uint32_t                  reserved;
} osThreadAttr_t;
name

线程的名称

指向具有线程对象的可读字符串

默认值为:NULL

attr_bits

属性位,可以设置线程对象的选项。

osThreadDetached(0):在分离模式下创建线程(默认)

osThreadJoinable(1):在可连接模式下创建线程

cb_mem

内存控制块位置

指向线程控制块对象的内存位置。静态内存分配时使用

默认值:NULL(动态内存分配)

cb_size

为控制块提供的内存大小

内存块的大小与cb_mem一起传递。必须大于或等于线程控制块的大小。(静态内存分配时使用)

stack_mem

内存的堆栈位置

指向线程堆栈的内存位置的指针,必须64字节对齐。静态内存分配时使用。

默认值:NULL(动态内存分配)

stack_size

堆栈大小

由stack_mem指定的堆栈大小。即给创建的线程分配的堆栈大小

priority        

线程优先级。

默认值:osPriorityNormal(24)

注:这里的优先级与liteos的优先级不同。Liteos优先级是31最低,0最高。这里0最低,38最高

tz_module

TrustZone模块标识符

线程上下文管理标识符为线程分配上下文内存。以非安全状态运行的RTOS内核调用由头文件TZ_context.h定义的接口函数。对于根本不使用安全调用的线程,可以安全地设置为零。

reserved        

保留

默认值:0

        返回值

        线程ID,可以供其他函数调用。

        实例

    attr.name = "thread1";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 1024 * 1;
    attr.priority = 25; 

    g_thread1_id  = osThreadNew((osThreadFunc_t)thread1, NULL, &attr);
    if (g_thread1_id == NULL)
    {
        LOG_E("Falied to create thread1!");
    }

        osThreadYield

        函数功能

        将控制权交给下一个具有相同优先级并处于 READY 状态的线程。如果在状态 READY 中没有其他具有相同优先级的线程,则当前线程继续执行并且不发生线程切换。osThreadYield 不会将线程设置为状态 BLOCKED 。因此,即使状态为 READY 的线程可用,也不会调度具有较低优先级的线程。该函数不能从中断服务程序调用

        函数原型

osStatus_t osThreadYield(void)

        参数

        无

        返回值

        osOK:成功

        其他值:失败

        实例

osThreadYield();

        osThreadGetId

        函数功能

        获取线程ID

        函数原型

osThreadGetId

        参数

        无

        返回值

        线程ID

        实例

osThreadId_t temp_t2_id = osThreadGetId();

        osThreadGetName

        函数功能

        获取线程的名字。名字是线程在创建是设置的

        函数原型

const char *osThreadGetName(osThreadId_t thread_id)

        参数

        thread_id:线程ID。通过osThreadGetId或osThreadNew获得

        返回值

        线程的ID。错误时返回NULL

        实例

osThreadId_t temp_t2_id = osThreadGetId();
const char *temp_name = osThreadGetName(temp_t2_id);

        osThreadGetStackSize

        函数功能

        获取线程总栈大小

        函数原型

uint32_t osThreadGetStackSize(osThreadId_t thread_id)

        参数

        线程ID。通过osThreadGetId或osThreadNew获得

        返回值

        线程总栈大小

        实例

osThreadId_t temp_t2_id = osThreadGetId();
osThreadGetStackSize(temp_t2_id);

        osThreadGetStackSpace

        函数功能

        获取线程剩余栈大小

        函数原型

uint32_t osThreadGetStackSpace(osThreadId_t thread_id)

        参数

        线程ID。通过osThreadGetId或osThreadNew获得

        返回值

        线程剩余栈大小

        实例

osThreadId_t temp_t2_id = osThreadGetId();
osThreadGetStackSpace(temp_t2_id);

        osKernelGetTickCount

        函数功能

        获取系统时钟的Tick数。通常Tick数周期为1ms

        函数原型

uint32_t osKernelGetTickCount(void)

        参数

        无

        返回值

        Tick数

        实例

osKernelGetTickCount()

        osDelay

        函数功能

        线程挂起时间。

        函数原型

osStatus_t osDelay(uint32_t ticks)

        参数

        挂起ticks数。真正的挂起时间为ticks*10ms

        返回值

        osOK:正常

        osError:异常

        实例

osDelay(200);

        osDelayUntil

        函数功能

        线程挂起,直到ticks数为止。

        注:调用该函数后,线程会挂起,直到系统的ticks数,到达设置的ticks数为止。例如这里设置ticks数为1000,那么线程运行到osDelayUntil函数后会查询当前的系统ticks数,如果小于1000则挂起等待,直到系统ticks数等于1000后,才开始继续往下执行。

        函数原型

osStatus_t osDelayUntil(uint32_t ticks)

        参数

        ticks数

        返回值

        osOK:正常

        osError:异常

        实例

osDelayUntil(1000);

        osThreadTerminate

        函数功能

        删除线程。线程终止后,所有的资源都会返回到系统

        注:该函数不能在中断服务中调用

        函数原型

osStatus_t osThreadTerminate(osThreadId_t thread_id)

        参数

        线程ID

        返回值

        osOK:成功

        其他值:异常。含义参考如下:

typedef enum {
  /** Operation completed successfully */
  osOK                      =  0,
  /** Unspecified error */
  osError                   = -1,
  /** Timeout */
  osErrorTimeout            = -2,
  /** Resource error */
  osErrorResource           = -3,
  /** Incorrect parameter */
  osErrorParameter          = -4,
  /** Insufficient memory */
  osErrorNoMemory           = -5,
  /** Service interruption */
  osErrorISR                = -6,
  /** Reserved. It is used to prevent the compiler from optimizing enumerations. */
  osStatusReserved          = 0x7FFFFFFF
} osStatus_t;

        实例

osThreadId_t temp_t2_id = osThreadGetId();
osStatus_t ret = osThreadTerminate(temp_t2_id);

        

四、综合实例

        这里我们创建两个线程,并且分别打印两个线程的栈大小、名字、剩余栈大小。且任务一使用创建时osThreadNew返回的任务ID,而任务2使用osThreadGetId获取的任务ID。看看效果是否相同。

#define LOG_I(fmt, args...)   printf("<%8ld> - [APP]:"fmt"\r\n",osKernelGetTickCount(),##args);

#define LOG_E(fmt, args...)   printf("<%8ld>-[APP_ERR]>>>>>>>>>>>>:"fmt"\r\n",osKernelGetTickCount(), ##args);

osThreadId_t g_thread1_id = NULL;
osThreadId_t g_thread2_id = NULL;

/*****任务一*****/
void thread1(void)
{
    LOG_I("thread 1 start");
    const char *temp_name = osThreadGetName(g_thread1_id);
    int sum = 0;
    osDelayUntil(1000);

    while (1)
    {
        LOG_I("this is Thread 1,name:[%s],thread stack size:[%ld],left stack:[%ld],sum:%d", temp_name,osThreadGetStackSize(g_thread1_id),osThreadGetStackSpace(g_thread1_id),sum);
        osDelay(100);
        if(sum++ > 10)
            break;
    }
    LOG_I("thread 1 break");
    osThreadTerminate(g_thread1_id);
}


/*****任务二*****/
void thread2(void)
{
    LOG_I("thread 2 start");
    osThreadId_t temp_t2_id = osThreadGetId();
    const char *temp_name = osThreadGetName(temp_t2_id);
    while (1)
    {
         LOG_I("this is Thread 2,name:[%s],thread stack size:[%ld],left stack:[%ld]", temp_name,osThreadGetStackSize(temp_t2_id),osThreadGetStackSpace(temp_t2_id));
        osDelay(100);
    }
    LOG_I("thread 2 end");
}


void Hello_World(void)
{
    osThreadAttr_t attr;

    LOG_I("hello!!!!!!!!!!!!!!!!!!!!!!!!!");

    attr.name = "thread1";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 1024 * 1;
    attr.priority = 25; 

    g_thread1_id  = osThreadNew((osThreadFunc_t)thread1, NULL, &attr);
    if (g_thread1_id == NULL)
    {
        LOG_E("Falied to create thread1!");
    }
    else
    {
        LOG_I("thread1 id:0x%.8x",*(uint32_t *)(g_thread1_id));
    }

    attr.name = "thread2";
    attr.stack_size = 1024 * 2;

    g_thread2_id = osThreadNew((osThreadFunc_t)thread2, NULL, &attr);
    if (g_thread2_id == NULL)
    {
        LOG_E("Falied to create thread2!");
    }
    else
    {
        LOG_I("thread2 id:0x%.8x",*(uint32_t *)(g_thread2_id));
    }  
}

SYS_RUN(Hello_World);

        从结果可以看到如下几点信息:

  1. 线程1在开始运行后,遇到了osDelayUntil,直接挂起。等到系统ticks到达1000后才继续往下运行。
  2. 线程1中使用的线程ID是创建线程时osThreadNew返回的。而线程2中使用的线程ID是通过osThreadGetId函数获取的。都能达到预期的效果。
  3. osThreadGetStackSize和osThreadGetStackSpace会分别打印出栈总大小和剩余栈大小。
  4. 线程1在运行10次后,就删除了自身,并且释放了所有的资源。

参考链接:

CMSIS-RTOS2 文档翻译 之 参考(CMSIS-RTOS2 API 之 线程管理) - 爱码网

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/504308.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

〖数据挖掘〗weka3.8.6的安装与使用

目录 背景 一、安装 二、使用explorer 1. 介绍 2.打开自带的数据集(Preprocess) 1.打开步骤 2.查看属性和数据编辑 3.classify 4.Cluster 5.Associate 6.Select attributes 7.Visualize 待补充 背景 Weka的全名是怀卡托智能分析环境&#xff08;Waikato Environme…

低代码平台解读—如何不写代码创建表单和维护表单

工作表新建与修改——敲敲云 新建工作表的流程包含 新建工作表/编辑公祖表为工作表添加字段&#xff0c;例如“员工档案”表中有姓名、性别、年龄等字段为字段设置属性工作表布局工作表预览、保存、关闭 1、新建工作表/修改工作表 新建工作表 修改工作表 2、为工作表添加字段 …

c#笔记-定义类

声明类 类可以使用帮助你管理一组相互依赖的数据&#xff0c;来完成某些职责。 类使用class关键字定义&#xff0c;并且必须在所有顶级语句之下。 类的成员只能有声明语句&#xff0c;不能有执行语句。 class Player1 {int Hp;int MaxHp;int Atk;int Def;int Overflow(){if (…

算法记录 | Day55 动态规划

392.判断子序列 思路&#xff1a; 1.确定dp数组&#xff08;dp table&#xff09;以及下标的含义: dp[i][j] 表示以下标i-1为结尾的字符串s&#xff0c;和以下标j-1为结尾的字符串t&#xff0c;相同子序列的长度为dp[i][j]。 2.确定递推公式&#xff1a; if (s[i - 1] t[…

线程同步、生产者消费模型和POSIX信号量

gitee仓库&#xff1a; 1.阻塞队列代码&#xff1a;https://gitee.com/WangZihao64/linux/tree/master/BlockQueue 2.环形队列代码&#xff1a;https://gitee.com/WangZihao64/linux/tree/master/ringqueue 条件变量 概念 概念&#xff1a; 利用线程间共享的全局变量进行同…

单片机c51中断 — 开关状态监测

项目文件 文件 关于项目的内容知识点可以见专栏单片机原理及应用 的第五章&#xff0c;中断 图中 P2.0引脚处接有一个发光二极管 D1&#xff0c;P3.2引脚处接有一个按键。要求分别采用一般方式和中断方式编程实现按键压下一次&#xff0c;D1 的发光状态反转一次的功能。 查询…

从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象

目录 1. 构造函数的初始化列表 1.1 初始化列表概念 1.2 初始化列表注意事项 2. 构造函数的explicit关键字 2.1 C语言的隐式类型转换 2.2 explicit 关键字使用 3. static成员 3.1 static的概念 3.2 static成员特性 3.3 static成员使用场景 4. 友元&#xff08;frien…

【Java 基础】类和对象 方法重载详解

《Java 零基础入门到精通》专栏持续更新中。通过本专栏你将学习到 Java 从入门到进阶再到实战的全套完整内容,所有内容均将集中于此专栏。无论是初学者还是有经验的开发人员,都可从本专栏获益。 订阅专栏后添加我微信或者进交流群,进群可找我领取 前端/Java/大数据/Python/低…

Linux 常用命令(1)

文章目录 Linux 常用命令格式 clear 清屏清屏获取当前目录的路径 pwd目录切换命令 cd进入上一级目录进入当前目录的文件夹 ta中(假设这里有一个文件夹ta)进入主目录进入根目录 显示目录内容 ls显示详细信息&#xff0c;包含文件属性显示全部内容&#xff0c;包含隐藏文件&#…

tiechui_lesson07_中断级和自旋锁

一、中断级IRQL 高级别可以打断低级别的调用&#xff0c;同级别不能打断同级别的调用。 中断级在软件层面分为三级&#xff0c;再高的级别是硬件发送的中断。 - 0 pass_level- 1 apc_level- 2 dpc_level 只有硬件中断能打断 1.获取中断级 DbgPrint("当前执行中断级为 %…

无法防范的网络攻击-DDOS

DDoS攻击&#xff08;Distributed Denial of Service Attack&#xff09;是一种网络攻击方式&#xff0c;攻击者通过利用大量的计算机或者网络设备向目标服务器发送大量的请求&#xff0c;使得目标服务器无法正常响应合法用户的请求&#xff0c;从而导致服务不可用或者服务质量…

M302H-YS-Hi3798MV300H/MV310-当贝纯净桌面卡刷固件包

M302H-YS-Hi3798MV300H&#xff0f;MV310-当贝纯净桌面卡刷固件包-内有教程及短接点提示 特点&#xff1a; 1、适用于对应型号的电视盒子刷机&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、修改dns&#xff0c;三网通用&#xff1b; 4、大量精简…

LicheePi4A尝鲜开箱笔记

开发板介绍 LicheePi4A是以 TH1520 主控核心&#xff0c;搭载 4TOPSint8 AI 算力的 NPU&#xff0c;支持双屏 4K 显示输出&#xff0c;支持 4K 摄像头接入&#xff0c;双千兆 POE 网口和多个 USB 接口&#xff0c;音频由 C906 核心处理。 LicheePi4A详细介绍可以在https://wi…

SpringCloud 微服务系列——Spring Cloud Alibaba 微服务工具集

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

FreeRTOS内核:详解Task各状态(GPT4帮写)

FreeRTOS内核&#xff1a;详解Task各状态&#xff08;GPT4帮写&#xff09; 1. 背景2. Task顶层状态区分3. 运行状态&#xff08;Running&#xff09;4. 非运行状态4.1 阻塞态&#xff08;Blocked&#xff09;&#xff1a;4.2 挂起态&#xff08;Suspended&#xff09;4.3 就绪…

K8s基础8——svc基础使用、应用暴露、iptables代理、ipvs代理

文章目录 一、Service基本了解二、Service定义与创建2.1 相关命令2.2 yaml文件参数大全2.3 创建svc2.3.1 两种创建方式类比2.3.2 验证集群内A应用访问B应用2.3.3 将集群外服务定义为K8s的svc2.3.4 分配多个端口 2.4 常用三种类型2.4.1 ClusterIP&#xff08;集群内部访问&#…

如何解决Redis的双写一致性

目录 1.更新策略2.问题场景3.解决方案 1.更新策略 Redis和MySQL的默认的更新策略是旁路缓存策略&#xff0c;旁路缓存策略又有写策略和读策略 写策略&#xff1a;更新时&#xff0c;先更新数据库&#xff0c;再更新缓存 读策略&#xff1a;读取数据时&#xff0c;如果命中缓…

自动驾驶——Smooth Local Planning

7.1参数曲线 在本模块中&#xff0c;我们将讨论分层运动规划器的最低级别&#xff0c;即局部规划器。作为提醒&#xff0c;局部规划器是分层规划器的一部分&#xff0c;它以无碰撞、高效和舒适的方式执行行为规划器所要求的机动。这导致轨迹&#xff0c;即在给定时间空间中的一…

【C++入门】auto关键字(C++11) + 指针空值nullptr(C++11)

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…

【谷粒商城之ThreadLocal用户身份鉴别】

本笔记内容为尚硅谷谷粒商城购物车ThreadLocal用户身份鉴别部分 目录 ThreadLocal 1.导入依赖 2.编写配置 3.配置Session 4.cookie中的user-key说明 5.编写To与常量 6.编写拦截器 7.添加拦截器的WebConfig配置类 8.Debug测试UserInfoTo中是否有数据 ThreadLocal T…