UCOSIII的任务管理详解

news2024/11/13 9:35:03

前言

对于操作系统而言,最重要的就是任务的创建、挂起、删除和调度等,简单的创建任务可能大家都会,但是做大型项目的话,任务多了就可能需要对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);              //初始化USART
    LED_Init();                     //初始化LED 
	OSInit(&err);		//初始化UCOSIII
	OS_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);      //开启UCOSIII
    while(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 > 0u
   OSStatTaskCPUUsageInit(&err);  	//统计任务                
#endif
	
#ifdef CPU_CFG_INT_DIS_MEAS_EN		//如果使能了测量中断关闭时间
    CPU_IntDisMeasMaxCurReset();	
#endif

#if	OS_CFG_SCHED_ROUND_ROBIN_EN  //当使用时间片轮转的时候
	 //使能时间片轮转调度功能,设置默认的时间片长度s
	OSSchedRoundRobinCfg(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); //延时200ms
		LED0=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.coloradmin.cn/o/1994835.html

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

相关文章

【网络】协议,OSI参考模型,局域网通信,跨网络通信

1.协议 1.1.什么是协议/协议的由来? 这个就要回到我们说的阿帕网了!! 在阿帕网(ARPA)产生运作之初,通过接口信号处理机实现互联的电脑并不多,大部分电脑相互之间不兼容。 在一台电脑上完成的工…

贪心/前后缀优化dp,CF 575F - Bulbo

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 575F - Bulbo 二、解题报告 1、思路分析 F1 前后缀分离优化dp 点 的值域很大,但是线段数目很少,只有5000量级 也就是说最多有10000 个 不同的点 我们将所有点从小到大排序得到点集…

【python】OpenCV—Greedy Snake

文章目录 1、代码实现2、涉及到的库——cv2.setWindowProperty 1、代码实现 import cv2 import numpy as np from random import choiceclass SnakePart:def __init__(self, front, x, y):self.front frontself.x xself.y ydef move(self):# 跟随它前面的部分移动self.x s…

数据结构:链表经典算法OJ题

目录 前言 一、移除链表元素 二、反转链表 三、合并两个有序链表 四、链表的中间节点 五、环形链表的约瑟夫问题 前言 在了解了链表的相关知识后,我们还需要一些题目进行练习加深对链表这方面知识的理解,也可以用来检测链表这块学的的怎么样&#…

【手撕数据结构】二叉树和堆

目录 树的概念树的相关概念二叉树二叉树的概念满二叉树和完全二叉树 堆的概念与结构堆的向上调整算法思路分析代码详细解说 堆的向下调整算法算法图解分析代码详解分析 堆的各个接口堆的定义及声明堆的初始化堆的销毁堆的插入堆的删除取堆顶数据堆的数据个数堆的判空 树的概念 …

【CVE-2024-38077】核弹级Windows RCE漏洞如何自检并修复该漏洞(附批量漏洞检测工具及分析伪代码)

代码详细分析点击此处 # 伪代码分析链接 工具为官方工具,师傅可自行测试 深信服CVE-2024-38077漏洞扫描工具.exe Algorithm : SHA1Hash : 85ECBDB053950A20B9748E867586D059AAA19115Algorithm : SHA256Hash : 1BF3A372F95C4F5B2D776C6ABB1E9BCA51933C3…

机器学习·L3W2-协同过滤

推荐算法 推荐算法可以预测用户评分,并根据评分推荐数据 推荐算法与其他预测算法的区别在于:推荐算法中的数据大多都不完整,用户只对几个电影评分;而预测算法则要求数据完整,便于拟合和预测 协同过滤 评分矩阵Y&#x…

Install pytorch 使用 torch 的例子

如果不知道怎么开始和安装软件 从这里开始 如果需要GPU版本,请选择CUDA,而不是CPU PyTorchhttps://pytorch.org/ Python 3.8.13 | packaged by conda-forge | (default, Mar 25 2022, 06:04:10) [GCC 10.3.0] on linux Type "help", &quo…

k8s环境使用cronjob对mysql8进行备份

一、configmap 数据库备份脚本写入k8s环境的configmap文件,并生成config。 # cat mysql-back-configmap.yaml apiVersion: v1 kind: ConfigMap metadata:name: mysql8backnamespace: crontabs data:mysql8back.sh: |#!/bin/bashDUMPDIR/tmp#backupbackupDatedate…

【专题】2023-2024跨境旅游消费趋势研究报告合集PDF分享(附原数据表)

原文链接:https://tecdat.cn/?p37306 近日,“世界旅游联盟中欧旅游对话”在匈牙利布达佩斯举办,发布《2023 - 2024 跨境旅游消费趋势研究报告》。 报告显示,2023 - 2024 年全球旅游业复苏,跨境旅游人数和支出显著增加…

【Material-UI】Checkbox组件:Indeterminate状态详解

文章目录 一、什么是Indeterminate状态?二、Indeterminate状态的实现1. 基本用法示例2. 代码解析3. Indeterminate状态的应用场景 三、Indeterminate状态的UI与可访问性1. 无障碍设计2. 用户体验优化 四、Indeterminate状态的最佳实践1. 状态同步2. 优化性能3. 提供…

SQL Zoo 10.Window functions

以下数据均来自SQL Zoo 1.Show the lastName, party and votes for the constituency S14000024 in 2017.(显示2017年选区“S14000024”的姓氏、政党和选票) SELECT lastName, party, votesFROM geWHERE constituency S14000024 AND yr 2017 ORDER BY…

多个IT系统数据同步方案-操作标识

这是一个很普遍的方案,实现多个IT系统数据同步: 从上游发出的数据标识,就可以完整的控制数据,当然对上游的要求也多一些,就是打数据标识前一定要读取一下现有的数据。

Spring Boot 基于 SCRAM 认证集成 Kafka 的详解

一、说明 在现代微服务架构中,Kafka 作为消息中间件被广泛使用,而安全性则是其中的一个关键因素。在本篇文章中,我们将探讨如何在 Spring Boot 应用中集成 Kafka 并使用 SCRAM 认证机制进行安全连接;并实现动态创建账号、ACL 权限…

Android Studio生成系统签名platform.jks

准备工作: 系统工程师需要提供以下文件并且在同一个目录文件夹下面 1、platform.pk8 2、platform.x509.pem 3、signapk.jar(通用的) 按照以下顺序执行 1、platform.pk8和platform.x509.pem 生成 .jks 提供platform.pk8、platform.x509.pem 、signapk.jar&…

低代码革命:重塑开发效率与质量的未来

1. 引言 随着信息技术的快速发展,企业对应用程序的需求日益增长。然而,传统的软件开发模式面临着开发周期长、成本高、人才短缺等问题。近年来,“低代码”开发平台如雨后春笋般涌现,承诺让非专业人士也能快速构建应用程序。这种新…

Netty原理及高性能

1. Netty原理 Netty是一个基于Java的异步事件驱动的网络应用框架,他用于快速开发高性能、高可靠性的网络服务器和客户端应用。Netty的原理涉及多个方面,包括 Reactor模式、核心组件、编解码、线程模型以及TCP粘包和拆包处理等。 1.1 Reactor模式 Reactor…

玻璃存储还没整明白,陶瓷纳米存储又来了!

关注我们 - 数字罗塞塔计划 - 在信息爆炸的当下,我们每天产生的数据比以往任何时候都多。其实很多数据都是存储后很少被访问,但仍需要长期保存的“冷数据”。磁带、硬磁盘、光盘等传统存储介质难以提供冷数据存储所需的超长寿命、超大容量和持续可访问性…

Docker Compse单机编排

一.Docker Compse 介绍 Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,你可以使用 YAML 文件来配置应用程序的服务、网络和卷,然后使用单个命令创建和启动所有服务。这使得在开发、测试和部署过程中管理多容器应用程…

精彩分享|暴雨亮相第二十届智能计算国际会议(ICIC 2024)

8月6日至8日,第二十届智能计算国际会议(ICIC 2024)在天津盛大召开,这场由宁波东方理工大学(暂名)主办,天津科技大学承办,中国矿业大学(北京)、中国矿业大学和…