解决方案

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

seo靠我 2023-09-23 11:27:58

一、任务简介

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

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

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

二、任务

任务状态        

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

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

名词介绍

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

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

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

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

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

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

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

任务状态迁移

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

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

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

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

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

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

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

三、 实操

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

1.什么是CMSIS-RTOS2接口

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

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

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

        官方API参考:Main Page

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

2.常用函数

        osThreadNew

       函数功能

创建一个SEO靠我新的任务。

        函数原型

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

        SEO靠我

        func:线程的回调函数

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

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

typedef struct {/** Thread name */const char *name;/** Thread attribute bits */uint32_t attrSEO靠我_bits;/** Memory for the thread control block */void *cb_mem;/** Size of the memory for the thread cSEO靠我ontrol block */uint32_t cb_size;/** Memory for the thread stack */void *stack_mem;/** Size of the thSEO靠我read stack */uint32_t stack_size;/** Thread priority */osPriority_t priority;/** TrustZone module ofSEO靠我 the thread */TZ_ModuleId_t tz_module;/** Reserved */uint32_t reserved; } osThreadAttr_t; SEO靠我 name

线程的名称

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

默认值为:NULL

attr_bits

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

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

osTSEO靠我hreadJoinable(1):在可连接模式下创建线程

cb_mem

内存控制块位置

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

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

cb_size

为控制块提供的内存大小

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

stack_mem

内存的堆栈位置

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

默认值:NUSEO靠我LL(动态内存分配)

stack_size

堆栈大小

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

priority        

线程优先级。

默认值:osPriorityNormal(24)

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

tz_module

TrustZone模块标识符

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

reserved        

保留

默认值:0

        返回值

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

        实例

attr.naSEO靠我me = "thread1";attr.attr_bits = 0U;attr.cb_mem = NULL;attr.cb_size = 0U;attr.stack_mem = NULL;attr.sSEO靠我tack_size = 1024 * 1;attr.priority = 25; g_thread1_id = osThreadNew((osThreadFunc_t)thread1, NULL, &SEO靠我attr);if (g_thread1_id == NULL){LOG_E("Falied to create thread1!");}

        osThreadGetId

        函数功能

        获取线程ID

        函数原型

osTSEO靠我hreadGetId

        参数

        无

        返回值

        线程ID

        实例

osThreadId_t temp_t2_id = osThreadGetId();

        osThreadGetName

        函数功能

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

        函数原型

const char *osThreadGetName(osThreadId_t thread_id)

        参数

        thread_id:线程ID。通过osThreadGetId或osSEO靠我ThreadNew获得

        返回值

        线程的ID。错误时返回NULL

        实例

osThreadId_t temp_t2_id = osThreadGetId(); const char *temSEO靠我p_name = osThreadGetName(temp_t2_id);

        osThreadGetStackSize

        函数功能

        获取线程总栈大小

        函数原型

uint32_t osThreadGetStackSEO靠我Size(osThreadId_t thread_id)

        参数

        线程ID。通过osThreadGetId或osThreadNew获得

        返回值

        线程总栈大小

        实例

osThreadId_t temp_t2_SEO靠我id = osThreadGetId(); osThreadGetStackSize(temp_t2_id);

        osThreadGetStackSpace

        函数功能

        获取线程剩余栈大小

        函数SEO靠我原型

uint32_t osThreadGetStackSpace(osThreadId_t thread_id)

        参数

        线程ID。通过osThreadGetId或osThreadNew获得

        返回值

线SEO靠我程剩余栈大小

        实例

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

osKernSEO靠我elGetTickCount

        函数功能

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

        函数原型

uint32_t osKernelGetTickCount(void)

        参数

        无

        返回值

        Tick数

        SEO靠我

osKernelGetTickCount()

        osDelay

        函数功能

        线程挂起时间。

        函数原型

osStatus_t osDelay(uint32_t ticks)

        参数

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

        返回值

        osOK:正常

        osError:异常

        实例

osDelay(200);

        osDelayUntil

        函数功能

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

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

        函数原型

osStatus_t osDelayUntil(uint32_t ticks)

        参数

        ticks数

        返回值

        osOK:正常

        osError:异常

        实例

osDSEO靠我elayUntil(1000);

        osThreadTerminate

        函数功能

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

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

        函数原型

osStatus_t osThreaSEO靠我dTerminate(osThreadId_t thread_id)

        参数

        线程ID

        返回值

        osOK:成功

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

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

        实例

osThreadId_t temp_t2_id = osThreadGetId(); osStatus_t ret = osThreadTerminate(SEO靠我temp_t2_id);

四、综合实例

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

#define LOG_I(fmt, args...) printf("<%8ld> - [APP]:"fmt"\r\n",osKernelGetTickCount(SEO靠我),##args);#define LOG_E(fmt, args...) printf("<%8ld>-[APP_ERR]>>>>>>>>>>>>:"fmt"\r\n",osKernelGetTicSEO靠我kCount(), ##args);osThreadId_t g_thread1_id = NULL; osThreadId_t g_thread2_id = NULL;/*****任SEO靠我务一*****/ void thread1(void) {LOG_I("thread 1 start");const char *temp_name = osThreaSEO靠我dGetName(g_thread1_id);int sum = 0;osDelayUntil(1000);while (1){LOG_I("this is Thread 1,name:[%s],thSEO靠我read stack size:[%ld],left stack:[%ld],sum:%d", temp_name,osThreadGetStackSize(g_thread1_id),osThreaSEO靠我dGetStackSpace(g_thread1_id),sum);osDelay(100);if(sum++ > 10)break;}LOG_I("thread 1 break");osThreadSEO靠我Terminate(g_thread1_id); }/*****任务二*****/ void thread2(void) {LOG_I("thread SEO靠我2 start");osThreadId_t temp_t2_id = osThreadGetId();const char *temp_name = osThreadGetName(temp_t2_SEO靠我id);while (1){LOG_I("this is Thread 2,name:[%s],thread stack size:[%ld],left stack:[%ld]", temp_nameSEO靠我,osThreadGetStackSize(temp_t2_id),osThreadGetStackSpace(temp_t2_id));osDelay(100);}LOG_I("thread 2 eSEO靠我nd"); }void Hello_World(void) {osThreadAttr_t attr;LOG_I("hello!!!!!!!!!!!!!!!!!!!!!SEO靠我!!!!");attr.name = "thread1";attr.attr_bits = 0U;attr.cb_mem = NULL;attr.cb_size = 0U;attr.stack_memSEO靠我 = NULL;attr.stack_size = 1024 * 1;attr.priority = 25; g_thread1_id = osThreadNew((osThreadFunc_t)thSEO靠我read1, NULL, &attr);if (g_thread1_id == NULL){LOG_E("Falied to create thread1!");}else{LOG_I("threadSEO靠我1 id:0x%.8x",*(uint32_t *)(g_thread1_id));}attr.name = "thread2";attr.stack_size = 1024 * 2;g_threadSEO靠我2_id = osThreadNew((osThreadFunc_t)thread2, NULL, &attr);if (g_thread2_id == NULL){LOG_E("Falied to SEO靠我create thread2!");}else{LOG_I("thread2 id:0x%.8x",*(uint32_t *)(g_thread2_id));} }SYS_RUN(HeSEO靠我llo_World);

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

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

参考链接:

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

“SEO靠我”的新闻页面文章、图片、音频、视频等稿件均为自媒体人、第三方机构发布或转载。如稿件涉及版权等问题,请与 我们联系删除或处理,客服邮箱:html5sh@163.com,稿件内容仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同 其观点或证实其内容的真实性。

网站备案号:浙ICP备17034767号-2