【学习日记】【FreeRTOS】任务句柄、任务控制块TCB、任务栈、任务、就绪表详解

news2024/9/24 3:21:41

写在前面

本文是对FreeRTOS中任务句柄、任务控制块TCB、任务栈、任务、就绪表详解。

一、裸机和RTOS中函数存储位置详解

  • 左图为裸机开发时 RAM 的使用情况,右图是使用了 FreeRTOS 后 RAM 的使用情况(图片来自野火)。
    在这里插入图片描述
  • 无论是裸机开发还是FreeRTOS,程序都需要存放在RAM中以便执行。不过,在裸机开发环境下,程序员需要手动管理和分配内存,而在FreeRTOS中,操作系统会自动管理内存。

二、什么是任务句柄

任务句柄(Task Handle)是在 FreeRTOS 中用于标识和引用任务的数据类型。每个创建的任务都会分配一个唯一的任务句柄,通过该句柄可以对任务进行操作和管理。

任务句柄是一个指向任务控制块(Task Control Block,TCB)的指针。任务控制块是 FreeRTOS 中用于描述和管理任务的数据结构,包含了任务的状态、优先级、堆栈等信息。

使用任务句柄,可以通过 FreeRTOS 提供的 API 函数对任务进行操作,例如挂起(suspend)、恢复(resume)、删除(delete)任务,或者查询任务的状态等。另外,任务句柄还可以用于任务通信和同步的机制,例如向任务发送信号量或消息。

在创建任务时,通过调用 FreeRTOS 提供的任务创建函数(例如 xTaskCreate())可以获取到相应任务的句柄。你可以将该句柄保存在一个变量中,以便后续对该任务进行操作或引用。

例如,以下示例演示了如何创建一个任务并获取其句柄:

// 创建任务
TaskHandle_t xTaskHandle;
xTaskCreate(taskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, &xTaskHandle);

// 使用任务句柄进行操作
vTaskSuspend(xTaskHandle);  // 挂起任务
vTaskResume(xTaskHandle);   // 恢复任务
vTaskDelete(xTaskHandle);   // 删除任务

在上述示例中,xTaskCreate() 函数创建了一个名为 “Task” 的任务,并将该任务的任务句柄保存在 xTaskHandle 变量中。然后,我们可以使用任务句柄对任务进行挂起、恢复和删除操作。

任务句柄提供了一种有效的方式来管理和操作 FreeRTOS 中的任务。通过使用任务句柄,可以方便地对任务进行控制和监视。

三、概念图解

在这里插入图片描述

四、函数详解

1.任务创建 static void prvInitialiseNewTask(…)

  • 这个函数用于创建新的任务,其中的 “prv” 表示该函数是一个私有函数,只用于内部处理和初始化新任务的操作。对于外部使用者来说,应该使用公开的 API 函数来创建和管理任务,而不是直接调用 “prvInitialiseNewTask”。
  • 这个函数被 TaskHandle_t xTaskCreateStatic() 函数调用
  • 函数源代码如下:
static void prvInitialiseNewTask( 	TaskFunction_t pxTaskCode,              /* 任务入口 */
									const char * const pcName,              /* 任务名称,字符串形式 */
									const uint32_t ulStackDepth,            /* 任务栈大小,单位为字 */
									void * const pvParameters,              /* 任务形参 */
									TaskHandle_t * const pxCreatedTask,     /* 任务句柄 */
									TCB_t *pxNewTCB )                       /* 任务控制块指针 */

{
	StackType_t *pxTopOfStack;
	UBaseType_t x;	
	
	/* 获取栈顶地址 */
	pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
	//pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
	/* 向下做8字节对齐 */
	pxTopOfStack = ( StackType_t * ) ( ( ( uint32_t ) pxTopOfStack ) & ( ~( ( uint32_t ) 0x0007 ) ) );	

	/* 将任务的名字存储在TCB中 */
	for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
	{
		pxNewTCB->pcTaskName[ x ] = pcName[ x ];

		if( pcName[ x ] == 0x00 )
		{
			break;
		}
	}
	/* 任务名字的长度不能超过configMAX_TASK_NAME_LEN */
	pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';

    /* 初始化TCB中的xStateListItem节点 */
    vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
    /* 设置xStateListItem节点的拥有者 */
	listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
    
    
    /* 初始化任务栈 */
	pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );   


	/* 让任务句柄指向任务控制块 */
    if( ( void * ) pxCreatedTask != NULL )
	{		
		*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
	}
}
  • 获取栈顶地址:
pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );

栈顶 = 栈起始地址 + 栈大小 -1

  • 获取到的栈顶地址需要做 8 字节对齐
pxTopOfStack = ( StackType_t * ) ( ( ( uint32_t ) pxTopOfStack ) & ( ~( ( uint32_t ) 0x0007 ) ) );

~ 是按位取反运算符。它会反转操作数的每一位,将所有的0变为1,将所有的1变为0。

在给定的代码中,~ 运算符用于创建一个掩码,该掩码在对齐操作中用于清除特定位的值。

( ~( ( uint32_t ) 0x0007 ) )

在这里,0x0007 是一个表示二进制数 0000 0111 的十六进制数,它具有最低的3位都是1,其他位都是0。通过 ~ 运算符对 0x0007 进行按位取反,得到的掩码就是所有最低的3位都是0,其他位都是1。

这样,当掩码与 pxTopOfStack 进行按位与操作时,最低的3位将被清零,而其他位将保持不变,pxTopOfStack 变量就被更新为按照8字节对齐的地址。

通常,在某些特定的编程环境中,需要按照特定的内存对齐要求来访问数据。这段代码将 pxTopOfStack 指针变量按照8字节对齐,以满足特定的对齐要求。

2.初始化任务栈 StackType_t *pxPortInitialiseStack(…)

通过栈顶指针对整个栈进行初始化,分为自动加载内容和手动加载内容。

  • 代码如下:
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
    /* 异常发生时,自动加载到CPU寄存器的内容 */
	pxTopOfStack--;
	*pxTopOfStack = portINITIAL_XPSR;	                                    /* xPSR的bit24必须置1 */
	pxTopOfStack--;
	*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;	/* PC,即任务入口函数 */
	pxTopOfStack--;
	*pxTopOfStack = ( StackType_t ) prvTaskExitError;	                    /* LR,函数返回地址 */
	pxTopOfStack -= 5;	/* R12, R3, R2 and R1 默认初始化为0 */
	*pxTopOfStack = ( StackType_t ) pvParameters;	                        /* R0,任务形参 */
    
    /* 异常发生时,手动加载到CPU寄存器的内容 */    
	pxTopOfStack -= 8;	/* R11, R10, R9, R8, R7, R6, R5 and R4默认初始化为0 */

	/* 返回栈顶指针,此时pxTopOfStack指向空闲栈 */
    return pxTopOfStack;
}

在这里插入图片描述

3.初始化任务就绪列表

任务就绪列表的定义

任务就绪列表(Task Ready List)是用于存储当前准备就绪状态的任务的数据结构。

任务就绪列表是一个由多个优先级队列组成的数据结构,其中每个优先级队列维护了相同优先级的就绪任务。通过任务就绪列表,操作系统可以快速找到具有最高优先级的就绪任务,并将其调度到正在运行的任务。

当一个任务变为就绪状态时,它将被插入到适当的就绪列表中,而当一个任务被调度执行时,它将从就绪列表中被移除。

每个列表中存储相同优先级的任务,最大支持256个优先级,也就是最大有256个列表。

  • 定义5个优先级的任务就绪列表的代码:
#define configMAX_PRIORITIES		            ( 5 )	//最大列表数量

/* 任务就绪列表 */
List_t pxReadyTasksLists[ configMAX_PRIORITIES ];	//定义了5个任务就绪列表

任务就绪列表的初始化

循环调用列表初始化函数 vListInitialise() 进行初始化即可。

  • 代码如下:
/* 初始化任务相关的列表 */
void prvInitialiseTaskLists( void )
{
    UBaseType_t uxPriority;
    
    
    for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )
	{
		vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );	//初始化每个就绪列表
	}
}

五、任务创建与初始化方法

1.定义任务栈大小,并定义任务栈存放任务上下文

//定义任务栈
#define TASK1_STACK_SIZE                    20
StackType_t Task1Stack[TASK1_STACK_SIZE];

2.定义任务控制块TCB

//定义任务控制块
TCB_t Task1TCB;

3.定义任务句柄(用于指向TCB)

//定义任务句柄
TaskHandle_t Task1_Handle;

4.定义任务函数并声明

void Task1_Entry( void *p_arg );

//定义任务函数(无限循环不返回)
void Task1_Entry(void *p_arg)
{
	
	for(;;){
		//此处书写任务代码
		
	}
}

5.在main函数中,初始化所有的任务就绪列表

prvInitialiseTaskLists();	//初始化所有的任务就绪列表

6.在main函数中,创建任务,并使任务句柄指向TCB

//任务创建函数的函数原型:
TaskHandle_t xTaskCreateStatic(	TaskFunction_t pxTaskCode,           /* 任务入口 */
					            const char * const pcName,           /* 任务名称,字符串形式 */
					            const uint32_t ulStackDepth,         /* 任务栈大小,单位为字 */
					            void * const pvParameters,           /* 任务形参 */
					            StackType_t * const puxStackBuffer,  /* 任务栈起始地址 */
					            TCB_t * const pxTaskBuffer );         /* 任务控制块指针 */

//创建任务,并使任务句柄指向TCB
Task1_Handle = xTaskCreateStatic(Task1_Entry,
						"Task1",
						TASK1_STACK_SIZE,
						NULL,
						Task1Stack,
						&Task1TCB);

7.将任务控制块中的任务项插入一个就绪列表中

vListInsert(&pxReadyTasksLists[1], &Task1TCB.xStateListItem);

后记

如果您觉得本文写得不错,可以点个赞激励一下作者!
如果您发现本文的问题,欢迎在评论区或者私信共同探讨!
共勉!

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

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

相关文章

每天一道leetcode:剑指 Offer 26. 树的子结构(中等递归BFS广度优先遍历树)

今日份题目&#xff1a; 输入两棵二叉树A和B&#xff0c;判断B是不是A的子结构。(约定空树不是任意一个树的子结构) B是A的子结构&#xff0c; 即A中有出现和B相同的结构和节点值。 例如: 给定的树 A: 3 ​/ \ ​4 5 ​/ \ ​1 2 给定的树 B&#xff1a; 4 ​/ ​1 返…

[Axios]在Axios中,怎么实现监听上传数据进度、监听接口返回数据进、如何终止网络请求。实现上传文件实时进度条以及下载文件实时进度条。

1. Axios的本质 Axios的本质是XHR的promise封装&#xff0c;所以XHR的一些函数对它也同样适用。2. 如何监听上传数据的实时进度 // 涉及函数onUploadProgress // 形参: event // 函数作用&#xff1a;请求接口上传的过程中会不停的调用onUploadProgress函数 axiox({url: api/…

自然语言处理: 第七章GPT的搭建

理论基础 在以transformer架构为框架的大模型遍地开花后&#xff0c;大模型的方向基本分成了三类分别是: decoder-only架构 , 其中以GPT系列为代表encoder-only架构&#xff0c;其中以BERT系列为代表encoder-decoder架构&#xff0c;标准的transformer架构以BART和T5为代表 大…

spring 面试题

一、Spring面试题 专题部分 1.1、什么是spring? Spring是一个轻量级Java开发框架&#xff0c;最早有Rod Johnson创建&#xff0c;目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack&#xff08;一站式&#xff09;轻量…

计算机视觉一 —— 介绍与环境安装

傲不可长 欲不可纵 乐不可极 志不可满 一、介绍 研究理论和应用 - 研究如何使机器“看”的科学 - 让计算机具有人类视觉的所有功能 - 让计算机从图像中&#xff0c;提取有用的信息&#xff0c;并解释 - 重构人眼&#xff1b;重构视觉皮层&#xff1b;重构大脑剩余部分 计…

RabbitMQ 发布确认机制

发布确认模式是避免消息由生产者到RabbitMQ消息丢失的一种手段 发布确认模式 原理说明实现方式开启confirm&#xff08;确认&#xff09;模式阻塞确认异步确认 总结 原理说明 生产者通过调用channel.confirmSelect方法将信道设置为confirm模式&#xff0c;之后RabbitMQ会返回Co…

使用postman做接口测试

1.接口测试&#xff1a;针对软件对外提供服务的接口的输入输出进行测试&#xff0c;以及接口间相互逻辑的测试&#xff0c;验证接口功能与接口描述文档的一致性 2.接口测试流程&#xff1a; 1&#xff09;获取接口信息&#xff1a;通过接口文档或抓包来获取接口的基本调用方式和…

【脚踢数据结构】内核链表

(꒪ꇴ꒪ )&#xff0c;Hello我是祐言QAQ我的博客主页&#xff1a;C/C语言,Linux基础,ARM开发板&#xff0c;软件配置等领域博主&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff0c;让我们成为一个强大的攻城狮&#xff01;送给自己和读者的一句鸡汤&#x1f914;&…

【iOS安全】开启任意app的WebView远程调试

参考&#xff1a;https://mp.weixin.qq.com/s/bNKxQaVrPaXsZ5BPbsXy7w &#xff08;来自周智老师的公众号&#xff09; 概述 Safari 有一个内置的前端调试器&#xff0c; 在iPhone通过局域网或者USB连接MacBook 并启用Safari 远程调试之后&#xff0c;前端调试器默认情况下对…

【机器学习1】什么是机器学习机器学习的重要性

什么是机器学习? 简而言之&#xff0c;机器学习就是训练机器去学习。 机器学习作为人工智能(Artificial Intelligence,AI)的一个分支&#xff0c;以其最基本的形式来使用算法通过从数据中获取知识来进行预测。 不同于人类通过分析大量数据手动推导规则和模型&#xff0c;机…

释放AI创作潜能:从大模型训练到高产力应用

文章目录 每日一句正能量前言什么是人工智能生成内容&#xff08;AIGC&#xff09;人工智能生成内容&#xff08;AIGC&#xff09;能做什么为什么要用人工智能生成内容&#xff08;AIGC&#xff09;创作成果用Java实现冒泡排序算法学生信息收集系统学生请假管理系统需求分析教务…

SpringBoot 依赖管理

Spring Boot 依赖管理 1. 父项目做依赖管理 无需关注版本号&#xff0c;自动版本仲裁机制 <!-- 依赖管理 --> <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version&g…

利用 PHP 特性绕 WAF 测试

在测试绕过 WAF 执行远程代码之前&#xff0c;首先构造一个简单的、易受攻击的远程代码执行脚本&#xff0c;内容如图&#xff1a; 第 6 行是一个比较明显的命令执行代码&#xff0c;第 3 行尝试拦截 system、exec 或 passthru 等函数&#xff08;PHP 中有许多其他函数可以执行…

CTF REVERSE练习之脱壳分析

今天要介绍脱壳分析的实验。壳&#xff0c;在自然界中&#xff0c;植物用壳来保护种子&#xff0c;动物用壳来保护身体等。同样&#xff0c;在一些计算机软件里也有一段专门负责保护软件不被非法修改或反编译的程序。他们附加在原程序上通过Windows加载器载入内存后&#xff0c…

FreeRTOS(任务管理的创建、删除、挂起、恢复)

目录 一、任务的基本概念 二、任务状态的概念 1、Running—运行态&#xff1a; 2、Ready—就绪态 3、Blocked—阻塞态 4、Suspended—挂起态 三、任务状态的切换 四、系统启动 1、vTaskStartScheduler()函数 1.1 作用 1.2 启动函数介绍 2、空闲任务 2.1 空闲任务的作…

mac安装vscode 配置git

1、安装vscode 官网地址 下载mac稳定版安装很慢的解决办法 (转自) mac电脑如何解决下载vscode慢的问题 选择谷歌浏览器右上角的3个点&#xff0c;选择下载内容&#xff0c;右键选择复制链接地址&#xff0c;在新窗口粘贴地址&#xff0c; 把地址中的一段替换成下面的cscode.sd…

新的里程碑!纪念正月十六工作室博客总访问量突破两百万

时值盛夏&#xff0c;清风徐徐&#xff0c;不觉间我们的博客访问量又迈入了新的里程碑——访问量突破两百万&#xff01; 总访问量突破百万&#xff1a; 个人成就&#xff1a; 记得上次突破重大里程碑还是去年夏天&#xff0c;那时我们重修岳阳楼&#xff0c;追往忆&#…

小程序商品如何设置阶梯价?

阶梯价在电商小程序中是一种常见的销售策略&#xff0c;可以吸引更多的消费者并提高销售额。下面将介绍一些怎么设置小程序产品的阶梯价的方法。 1. 添加/修改商品的时候&#xff0c;点击阶梯价&#xff0c;会弹出阶梯价设置界面。 2. 设置阶梯价规则。例如&#xff0c;当消费者…

http相关知识点

文章目录 长链接http周边会话保持方案1方案2 基本工具postmanFiddlerFiddler的原理 长链接 一张网页实际上可能会有多种元素组成&#xff0c;这也就说明了网页需要多次的http请求。可由于http是基于TCP的&#xff0c;而TCP创建链接是有代价的&#xff0c;因此频繁的创建链接会…

gSpan算法执行步骤详解示例

目录 1. 问题描述2. gSpan算法步骤2.1 数据预处理2.2 深度递归挖掘频繁子图2.2.1 获取所有的频繁边2.2.2 深度递归挖掘频繁子图 参考文献 1. 问题描述 gSpan 是一款图规则挖掘算法&#xff0c;目标是从现有的图集中挖掘频繁子图。如下图中包含三个图&#xff1a; 其中圆圈为顶…