UCOSIII的任务管理详解

server/2024/10/18 18:28:12/

前言

对于操作系统而言,最重要的就是任务的创建、挂起、删除和调度等,简单的创建任务可能大家都会,但是做大型项目的话,任务多了就可能需要对UCOSIII的任务管理做更深层次的一些理解。

一、任务状态

UCOSIII是单核系统,一个时刻只有一个任务是运行状态,其它任务都是别的状态,状态一共分五种,分别为:休眠态、就绪态、运行态、等待态、中断服务态,下图是对于这几种状态的描述。

 

二、创建任务

代码如下:

void OSTaskCreate(OS_TCB         *p_tcb,       // 任务控制块CPU_CHAR       *p_name,       // 任务名称OS_TASK_PTR     p_task,       // 任务函数void           *p_arg,        // 传递给任务函数的参数OS_PRIO         prio,         // 任务优先级CPU_STK        *p_stk_base,   // 任务堆栈基地址CPU_STK_SIZE    stk_limit,    // 任务堆栈深度限位CPU_STK_SIZE    stk_size,     // 任务堆栈大小OS_MSG_QTY      q_size,       // 任务内部消息队列能够接收的最大消息数目OS_TICK         time_quanta,  // 当使能时间片轮转时的时间片长度void           *p_ext,        // 用户补充的存储区OS_OPT          opt,          // 任务选项OS_ERR         *p_err         // 存放该函数错误时的返回值
);

参数:

  1. p_tcb:指向任务控制块(Task Control Block, TCB)的指针。TCB是UCOSIII用于管理任务的一个数据结构,包含了任务的各种信息,如任务状态、优先级、堆栈指针等。

  2. p_name:指向任务名称的指针。每个任务都有一个唯一的名称,用于调试和日志记录。

  3. p_task:任务函数的指针。这是任务实际执行的代码入口点。任务函数通常具有void func(void *p_arg)的形式,其中p_arg是传递给任务函数的参数。

  4. p_arg:传递给任务函数的参数。这个参数在任务函数被调用时传递给它。如果任务函数不需要参数,这个值通常设置为NULL0

  5. prio:任务的优先级。UCOSIII支持多个任务优先级,优先级数值越小,任务越优先执行。但是,UCOSIII保留了一些优先级给系统内部任务使用,用户任务应避免使用这些优先级。

  6. p_stk_base:任务堆栈的基地址。这是堆栈空间的最低地址,用于初始化任务堆栈。在UCOSIII中,堆栈的增长方向可以是向上(从低地址向高地址)或向下(从高地址向低地址),具体取决于CPU的配置。

  7. stk_limit:任务堆栈的深度限位。这个值表示堆栈剩余空间的最低界限,当堆栈使用超过这个界限时,UCOSIII会报告堆栈溢出错误。但是,请注意,这个值在UCOSIII内部可能会根据堆栈的增长方向进行转换。

  8. stk_size:任务堆栈的大小。这是堆栈空间的总大小,以堆栈元素(通常是CPU的字大小)为单位。

  9. q_size:任务内部消息队列能够接收的最大消息数目。如果不需要内部消息队列,可以将此值设置为0

  10. time_quanta:当使能时间片轮转时的时间片长度。这个时间片长度决定了任务在执行完当前时间片后是否会被挂起,以便其他任务有机会执行。如果不需要时间片轮转,可以将此值设置为0

  11. p_ext:用户补充的存储区。这是一个指向用户定义的存储区的指针,可以用于存储任务相关的额外信息。如果不需要,可以设置为NULL

  12. opt:任务选项。这是一个位掩码,用于指定任务的特定选项,如是否检查堆栈、是否清除堆栈等。

  13. p_err:存放该函数错误时的返回值。如果函数调用成功,*p_err将被设置为OS_ERR_NONE;如果发生错误,则会被设置为相应的错误码。

三、任务堆栈

任务堆栈的作用

  1. 保存函数的执行路径信息:当任务中的函数调用发生时,堆栈用于保存函数的返回地址,以便在函数返回时能够正确地回到调用点。
  2. 保存CPU上下文:在任务切换时,堆栈用于保存当前任务的CPU寄存器状态(如程序计数器、状态寄存器等),以便在任务恢复时能够恢复到之前的状态。
  3. 传递参数:在函数调用时,参数通常通过堆栈传递给被调用的函数。
  4. 为临时变量分配空间:堆栈还用于存储任务执行过程中的临时变量。

任务堆栈的设定

  1. 堆栈大小:任务堆栈的大小取决于任务的需求,包括可能被调用的函数及其嵌套层数、相关局部变量的大小、中断服务程序所需要的空间等。设定堆栈大小时,需要考虑所有可能的情况,以确保堆栈不会溢出。
  2. 堆栈溢出检测:在有MMU(内存管理单元)或MPU(内存保护单元)的系统中,堆栈溢出的检测相对简单,因为这是MMU和MPU的必备功能之一。在没有这些硬件支持的系统中,UCOSIII提供了软件策略来检测堆栈溢出,如通过设置堆栈限制指针(StkLimitPtr)来监控堆栈的使用情况。
  3. 堆栈使用统计:UCOSIII提供了OSTaskStkChk()函数来统计任务堆栈的使用情况。这个函数可以帮助开发者了解每个任务的堆栈使用情况,以便在需要时调整堆栈大小。

注意事项

  1. 避免递归函数:在嵌入式系统中,由于资源有限,通常建议避免使用递归函数,因为它们可能会消耗大量的堆栈空间。
  2. 堆栈使用率:在程序设计调试阶段,最好谨慎地多查看任务堆栈的使用情况,以便在需要时做出调整。通常,堆栈使用率建议在50%到80%之间,太小会浪费空间,太大则可能不安全。
  3. 堆栈大小调整:如果任务堆栈的使用率接近或超过其限制,可能需要调整堆栈的大小。这可以通过修改任务创建时的stk_size参数来实现。

四、任务调度

UCOSIII(μC/OS-III)的任务调度是其内核的一个重要功能,它负责决定接下来哪个任务将获得CPU的控制权。以下是对UCOSIII任务调度的详细解释:

1. 调度器的类型

UCOSIII是一个抢占式的、基于优先级的内核。这意味着当有更高优先级的任务就绪时,当前正在执行的任务的CPU控制权将被剥夺,转交给更高优先级的任务。

2. 优先级就绪位图

UCOSIII使用就绪优先级位图来快速查找最高优先级的就绪任务。就绪优先级位图是一个按位表示的结构,每个位代表一个优先级。当某个优先级上有任务就绪时,相应位被置位。UCOSIII通过API如OS_PrioGetHighest()来获取最高优先级,并使用OS_PrioInsert()和OS_PrioRemove()来管理优先级位图,这些操作允许UCOSIII在O(1)时间复杂度内完成优先级任务的管理和调度。

3. 就绪队列

UCOSIII使用就绪队列来管理所有处于就绪状态的任务。每个优先级对应一个就绪队列,所有具有相同优先级的任务被链入该队列中。任务控制块(TCB)是任务管理的基本单位,包含了任务的所有状态信息。通过API如OS_RdyListInsert()、OS_RdyListInsertHead()、OS_RdyListInsertTail()等,可以将TCB插入到相应的就绪队列中,实现任务的调度。

4. 调度时机

任务调度通常发生在以下情况下:

  • 一个任务向另一个任务发送信号或信息,且接收方任务优先级高于当前任务。
  • 中断服务程序(ISR)向一个更高优先级的任务发布了消息或释放了信号量。
  • 任务调用时间延迟函数(如OSTimeDly())后超时。
  • 任务调用任务挂起函数(如OSTaskSuspend())挂起自身或其他任务,并因此改变了最高优先级任务的候选者。
  • 调用OSSched()显式请求任务调度。
  • 中断处理函数结束时,如果最高优先级的任务不是当前任务,则调用OSIntExit()进行任务切换。

5. 时间片轮转调度

当两个或多个任务拥有相同的优先级时,UCOSIII允许这些任务执行时间片轮转调度。每个时钟节拍到来时,当前运行任务的时间片减一,减到0则进行任务切换。如果一个任务不需要使用完整的时间片,它可以主动放弃CPU控制权,让给下一个同优先级的任务执行。

6. 调度策略

UCOSIII的调度策略基于优先级,并且允许用户在运行时动态调整任务的优先级和时间片大小。此外,用户还可以控制时间片轮转调度的开启和关闭。

UCOSIII通过就绪优先级位图和就绪队列的结合,实现了高效的任务调度机制。这种机制不仅保证了实时操作系统的高效性,还提供了灵活的任务管理方式。在实际应用中,通过优化任务优先级和队列管理,可以进一步提升系统性能,满足各种复杂的实时需求。

五、完整代码

下面是一个UCOSIII创建跑马灯任务的完整代码,可以参考:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "includes.h"//任务优先级
#define START_TASK_PRIO		3
//任务堆栈大小	
#define START_STK_SIZE 		512
//任务控制块
OS_TCB StartTaskTCB;
//任务堆栈	
CPU_STK START_TASK_STK[START_STK_SIZE];
//任务函数
void start_task(void *p_arg);//任务优先级
#define LED0_TASK_PRIO		4
//任务堆栈大小	
#define LED0_STK_SIZE 		128
//任务控制块
OS_TCB Led0TaskTCB;
//任务堆栈	
CPU_STK LED0_TASK_STK[LED0_STK_SIZE];
void led0_task(void *p_arg);//任务优先级
#define LED1_TASK_PRIO		5
//任务堆栈大小	
#define LED1_STK_SIZE       128
//任务控制块
OS_TCB Led1TaskTCB;
//任务堆栈	
CPU_STK LED1_TASK_STK[LED1_STK_SIZE];
//任务函数
void led1_task(void *p_arg);int main(void)
{OS_ERR err;CPU_SR_ALLOC();Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhz   HAL_Init();                     //初始化HAL库delay_init(180);                //初始化延时函数uart_init(115200);              //初始化USARTLED_Init();                     //初始化LED OSInit(&err);		//初始化UCOSIIIOS_CRITICAL_ENTER();//进入临界区//创建开始任务OSTaskCreate((OS_TCB 	* )&StartTaskTCB,		//任务控制块(CPU_CHAR	* )"start task", 		//任务名字(OS_TASK_PTR )start_task, 			//任务函数(void		* )0,					//传递给任务函数的参数(OS_PRIO	  )START_TASK_PRIO,     //任务优先级(CPU_STK   * )&START_TASK_STK[0],	//任务堆栈基地址(CPU_STK_SIZE)START_STK_SIZE/10,	//任务堆栈深度限位(CPU_STK_SIZE)START_STK_SIZE,		//任务堆栈大小(OS_MSG_QTY  )0,					//任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息(OS_TICK	  )0,					//当使能时间片轮转时的时间片长度,为0时为默认长度,(void   	* )0,					//用户补充的存储区(OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR|OS_OPT_TASK_SAVE_FP, //任务选项,为了保险起见,所有任务都保存浮点寄存器的值(OS_ERR 	* )&err);				//存放该函数错误时的返回值OS_CRITICAL_EXIT();	//退出临界区	 OSStart(&err);      //开启UCOSIIIwhile(1){} 
}//开始任务函数
void start_task(void *p_arg)
{OS_ERR err;CPU_SR_ALLOC();p_arg = p_arg;CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0uOSStatTaskCPUUsageInit(&err);  	//统计任务                
#endif#ifdef CPU_CFG_INT_DIS_MEAS_EN		//如果使能了测量中断关闭时间CPU_IntDisMeasMaxCurReset();	
#endif#if	OS_CFG_SCHED_ROUND_ROBIN_EN  //当使用时间片轮转的时候//使能时间片轮转调度功能,设置默认的时间片长度sOSSchedRoundRobinCfg(DEF_ENABLED,10,&err);  
#endif		OS_CRITICAL_ENTER();	//进入临界区//创建LED0任务OSTaskCreate((OS_TCB 	* )&Led0TaskTCB,		(CPU_CHAR	* )"led0 task", 		(OS_TASK_PTR )led0_task, 			(void		* )0,					(OS_PRIO	  )LED0_TASK_PRIO,     (CPU_STK   * )&LED0_TASK_STK[0],	(CPU_STK_SIZE)LED0_STK_SIZE/10,	(CPU_STK_SIZE)LED0_STK_SIZE,		(OS_MSG_QTY  )0,					(OS_TICK	  )0,					(void   	* )0,					(OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR|OS_OPT_TASK_SAVE_FP,(OS_ERR 	* )&err);				//创建LED1任务OSTaskCreate((OS_TCB 	* )&Led1TaskTCB,		(CPU_CHAR	* )"led1 task", 		(OS_TASK_PTR )led1_task, 			(void		* )0,					(OS_PRIO	  )LED1_TASK_PRIO,     	(CPU_STK   * )&LED1_TASK_STK[0],	(CPU_STK_SIZE)LED1_STK_SIZE/10,	(CPU_STK_SIZE)LED1_STK_SIZE,		(OS_MSG_QTY  )0,					(OS_TICK	  )0,					(void   	* )0,				(OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR|OS_OPT_TASK_SAVE_FP, (OS_ERR 	* )&err);OS_CRITICAL_EXIT();	//进入临界区				 OS_TaskSuspend((OS_TCB*)&StartTaskTCB,&err);		//挂起开始任务			 
}//led0任务函数
void led0_task(void *p_arg)
{OS_ERR err;p_arg = p_arg;while(1){LED0=0;    //LED0打开OSTimeDlyHMSM(0,0,0,200,OS_OPT_TIME_HMSM_STRICT,&err); //延时200msLED0=1;    //LED0关闭OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_HMSM_STRICT,&err); //延时500ms}
}//led1任务函数
void led1_task(void *p_arg)
{p_arg = p_arg;while(1){LED1=!LED1;delay_ms(500);//延时500ms}
}


http://www.ppmy.cn/server/98359.html

相关文章

猎码安卓APP开发IDE,amix STUDIO中文java,HTML5开发工具

【无爱也能发电】Xili 2024/8/2 10:41:20 猎码安卓APP开发IDE,amix java开发工具 我研发这些只有一小部分理由是为了赚钱,更多是想成就牛逼的技术产品。 目前的产品就够我赚钱的,我持续更新就好了,没必要继续研究。 IDE不赚钱,谁…

寻参算法之人工蜂群算法

人工蜂群算法(Artificial Bee Colony, ABC) 来历 人工蜂群算法(Artificial Bee Colony, ABC)由Dervis Karaboga在2005年提出。该算法受到蜜蜂觅食行为的启发,通过模拟蜜蜂在寻找食物过程中的探索和开发行为&#xff…

vue中scoped原理以及scoped的穿透用法

scoped原理: scoped:css样式只能用于当前vue组件。 scoped实现原理:通过postCss实现,给组件中所有的dom元素添加唯一的动态属性。给css选择器添加一个属性选择器。这样做是使样式只作用于该属性的dom元素。 scoped缺点&#xf…

Transformer代码

一、Embedding import torch from torch import nn import torch.nn.functional as F import math from torch import Tensor#将输入的词汇表索引转换成指定维度的Embedding向量 class TokenEmbedding(nn.Embedding):def __init__(self,vocab_size,d_model):super(TokenEmbedd…

golang判断某个文件内容是否是二进制文件方法, LimitReader, 获取文件大小,字符串0写入后的byte数据为48, byte零值

go语言中判断某个文件是否是二进制文件的方法, 通过LimitReader读取指定大小的数据后对数据进行判断, 这里有一个很有趣的知识点就是 字符串0在写入文件后,再通过io read读取后的byte数据他在内存中显示的可不是0 而是变成了 48, 十六进制 0x…

【微信小程序开发】——奶茶点餐小程序的制作(一)

👨‍💻个人主页:开发者-曼亿点 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 曼亿点 原创 👨‍💻 收录于专栏&#xff1a…

【Material-UI】按钮组:垂直按钮组详解

文章目录 一、按钮组概述1. 组件介绍2. 基本用法 二、垂直按钮组的应用场景1. 导航菜单2. 表单操作3. 选项切换 三、按钮组的样式定制1. 变体(Variants)2. 颜色(Colors) 四、垂直按钮组的优势1. 空间利用2. 可读性与易用性3. 视觉…

Babel 7入门基础知识 实践案例篇【2】推荐

Babel 是一个工具链,主要用于将采用 ECMAScript 2015 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。 我们前面回顾webpack入门基础知识的时候已经已经了解过了。 并且在Babel入门基础知识 实践案…