FreeRTOS中任务控制块中关于堆栈的三个变量pxTopOfStack、pxStack、pxEndOfStack的分析

2023-05-16

这里写自定义目录标题

FreeRTOS中任务控制块中关于堆栈的定义

typedef struct tskTaskControlBlock
{
	volatile StackType_t	*pxTopOfStack;	/*< Points to the location of the last item placed on the tasks stack.  THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */
	//指向放置在任务堆栈上的最后一个项目的位置,对于向下增长的栈,这个总是指向最后一个入栈的项目。

	#if ( portUSING_MPU_WRAPPERS == 1 )
		xMPU_SETTINGS	xMPUSettings;		/*< The MPU settings are defined as part of the port layer.  THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */
	#endif

	ListItem_t			xStateListItem;	/*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */
	ListItem_t			xEventListItem;		/*< Used to reference a task from an event list. */
	UBaseType_t			uxPriority;			/*< The priority of the task.  0 is the lowest priority. */
	StackType_t			*pxStack;			/*< Points to the start of the stack. */
	//位置是固定的,随着任务的运行,堆栈可能溢出,堆栈往下增长的系统中,这个变量就可以判断堆栈是否溢出,对于堆栈往上增长的系统,想确定堆栈是否溢出,还需要pxEndOfStack这个变量。
	char				pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created.  Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */

	#if ( portSTACK_GROWTH > 0 )
		StackType_t		*pxEndOfStack;		/*< Points to the end of the stack on architectures where the stack grows up from low memory. *///如果堆栈向上生长(portSTACK_GROWTH > 0),指针pxEndOfStack指向堆栈尾部,用于检验堆栈是否溢出。
	#endif
	...
}

xTaskCreate()函数中对栈的申请的过程

BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,				//函数指针,指向任务函数的入口
							const char * const pcName,             	//任务描述,主要用于调试
							const uint16_t usStackDepth,			//指定任务堆栈大小
							void * const pvParameters,				//任务函数所需要的参数
							UBaseType_t uxPriority,					//任务优先级
							TaskHandle_t * const pxCreatedTask )	//任务句柄,创建任务后可以使用这个句柄引用任务;
        /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
	{
	TCB_t *pxNewTCB;             //暂时新建一个任务控制块指针,下面用内存申请函数为之创建一个内存空间
	BaseType_t xReturn;

		/* If the stack grows down then allocate the stack then the TCB so the stack
		does not grow into the TCB.  Likewise if the stack grows up then allocate
		the TCB then the stack. */
        //如果堆栈增长方式是往低地址增加,则分配堆栈然后分配TCB,以便堆栈不会增长到TCB。 同样,如果堆栈增长方式是从低地址向高地址增加,则分配TCB然后分配堆栈。
		#if( portSTACK_GROWTH > 0 )
		{
			/* Allocate space for the TCB.  Where the memory comes from depends on
			the implementation of the port malloc function and whether or not static
			allocation is being used. */
			pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

			if( pxNewTCB != NULL )
			{
				/* Allocate space for the stack used by the task being created.
				The base of the stack memory stored in the TCB so the task can
				be deleted later if required. */
				pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */

				if( pxNewTCB->pxStack == NULL )
				{
					/* Could not allocate the stack.  Delete the allocated TCB. */
					vPortFree( pxNewTCB );
					pxNewTCB = NULL;
				}
			}
		}
		#else /* portSTACK_GROWTH */
		{
		StackType_t *pxStack;

			/* Allocate space for the stack used by the task being created. */
			pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */

			if( pxStack != NULL )
			{
				/* Allocate space for the TCB. */
				pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e961 MISRA exception as the casts are only redundant for some paths. */

				if( pxNewTCB != NULL )
				{
					/* Store the stack location in the TCB. */
					pxNewTCB->pxStack = pxStack;
				}
				else
				{
					/* The stack cannot be used as the TCB was not created.  Free
					it again. */
					vPortFree( pxStack );
				}
			}
			else
			{
				pxNewTCB = NULL;
			}
		}
		#endif /* portSTACK_GROWTH */
         //确定任务控制块的pxStack的数值,栈的起始位置

可以看到在创建任务的开始阶段就进行了有关freertos任务的两个内存申请,一个是任务堆栈,一个是任务控制块所需占用空间;
这其中根据portSTACK_GROWTH这个宏对申请的顺序进行了调整,先看一下这个宏的定义和区别。
这个是用户根据实际使用的芯片的堆栈增长方式来设定的,如果portSTACK_GROWTH大于0,那么堆栈就是向上增长的,否则就是向下增长的。

在这里插入图片描述上图显示了堆栈 向上增长和向下增长的区别。
如果堆栈是向下增长,也就是从高地址向低地址增长,那么在任务刚开始创建后,堆栈是空的。如图中例子,栈顶在为TaskStk[0][511],栈底为在TaskStk[0][0]。
相反,如果堆栈是向上增长的,栈顶在为TaskStk[0][0],栈底为在TaskStk[0][511]。
那么,如果我们向堆栈中压入数据,例如推入0x0012ff78后,堆栈变化如下图:
在这里插入图片描述如图,压栈后,若堆栈向下增长,在原来栈顶位置插入数据0x0012ff78,然后栈顶位置向低地址方向移4个字节,指向TaskStk[0][510]。若堆栈向上增长,在原来栈顶位置压入0x0012ff78,栈顶变为TaskStk[0][1]。
堆栈中向上增长----低地址向高地址方向增长。

向下增长----高地址向低地址方向增长。

堆栈中数据插入、删除遵循 后进先出的规则,因此插入数据向上增长和向下增长需要注意插入的方向。

申请过程中为什么要区别栈的增长方式,因为两个数据区,一个是任务栈一个是任务控制块TCB,TCB的大小是固定的,任务栈的大小存在变动。创建任务时候的申请是在堆上面进行的,堆上先申请的地址空间是低的,后申请的空间是高的。申请后任务栈以堆栈的方式进行访问。
如果栈往下增长,也就是栈顶的地址高,栈底的地址低,在这种情况下如果先申请的是TCB的空间后申请任务栈的空间,TCB地址<任务栈地址,在这种情况下,使用任务栈的时候,栈顶指针往低地址走,如果事先申请的任务栈空间不够,那么就会触碰到TCB的地址空间。而如果先申请任务栈再申请TCB,任务栈地址<TCB地址,这种情况下,随着程序运行,任务栈栈顶地址往低地址方向走,即使任务栈空间不足,也不会触碰到TCB区域,这样相对安全一些。
栈往高地址增长的情况也照此分析即可。

到此为止其实就是确定了任务控制块pxStack的具体数据,pxStack是申请到的任务栈的起始地址,但是这个地址是不是栈顶地址是不确定的,因为如果栈的增长是往低地址增长的,此时pxStack的数值就不是栈顶地址;如果栈的增长方式是从低地址往高地址的,那么此时pxStack的数值就是栈顶地址。
在这里插入图片描述
到此为止任务控制块中pxStack的含义就明白了。
上述xTaskCreate函数的完成之处也就是设置任务控制块中pxStack的数值。

接下来xTaskCreate函数将调用prvInitialiseNewTask函数,这个函数就是初始化xTaskCreate中申请的任务控制块中的其他数据,包括另外两个关于堆栈地址的变量pxEndOfStack、pxTopOfStack。
这两个根据名字也知道,一个是栈顶地址,一个是栈底地址,栈顶地址将会随着任务的进行而发生变化(入栈和出栈操作)。
接下来是prvInitialiseNewTask函数的代码段

/* Calculate the top of stack address.  This depends on whether the stack
	grows from high memory to low (as per the 80x86) or vice versa.
	portSTACK_GROWTH is used to make the result positive or negative as required
	by the port. */
    //计算栈顶地址,这个取决于堆栈是往高地址增长还是往低地址增长(x86)的,确定栈顶位置
	#if( portSTACK_GROWTH < 0 )   //如果栈是往低地址增长的
	{
		pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
		pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 MISRA exception.  Avoiding casts between pointers and integers is not practical.  Size differences accounted for using portPOINTER_SIZE_TYPE type. */

		/* Check the alignment of the calculated top of stack is correct. 其实就是字节对齐*/
		configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
	}
	#else /* portSTACK_GROWTH */  //如果栈是往高地址增长的
	{
		pxTopOfStack = pxNewTCB->pxStack;//前面申请的任务栈内存起始地址就是栈顶地址

		/* Check the alignment of the stack buffer is correct. */
		configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );

		/* The other extreme of the stack space is required if stack checking is
		performed. */
		pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );   //此时需要一个栈尾地址(和栈顶地址)相对应
	}
	#endif /* portSTACK_GROWTH */

根据前面的分析,如果portSTACK_GROWTH<0,那么栈是往低地址增长的,此时栈顶的地址其实不是pxStack,而是pxStack+任务栈总大小的位置,pxStack其实就是栈底地址;如果portSTACK_GROWTH>0,那么栈是往高地址增长的,此时栈顶地址就是pxStack,这时候,我们并不知道栈底地址了,所以此时需要pxEndOfStack这个变量来保存一下栈底地址。

为什么需要栈底地址,就是需要判断任务栈再任务运行过程中是否出现了栈溢出,什么时候发生栈溢出,栈顶地址和栈尾地址重合了,接下来栈顶地址继续走就会溢出了,所以栈底地址就是为了判断任务运行过程中是否会溢出。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS中任务控制块中关于堆栈的三个变量pxTopOfStack、pxStack、pxEndOfStack的分析 的相关文章

  • 基于HAL库的FREERTOS----------二.任务API函数

    任务API函数览概 CUBEMX对 做了API的封装 很多 的函数没有封装到位 可以用原函数调用 任务API函数分别介绍 1 uxTaskPriorityGet 此函数用来获取指定任务的优先级 要使用此函数的话宏 INCLUDE uxTas
  • 【FreeRtos学习笔记】STM32 CubeMx——Timers(定时器)

    目录 1 软件定时器 2 示例程序 2 1 例程功能 2 2 步骤 2 3 实验结果 2 4 函数讲解 1 软件定时器 定时器是MCU常用的外设 我们在学习各种单片机时必然会学习它的硬件定时器 但是 MCU自带的硬件定时器资源是有限的 而且
  • 【FreeRTOS 信号量】互斥信号量

    互斥信号量与二值信号量类似 但是互斥信号量可以解决二值信号量出现的优先级翻转问题 解决办法就是优先级继承 普通互斥信号量创建及运行 参阅安富莱电子demo 互斥信号量句柄 static SemaphoreHandle t xMutex NU
  • FreeRTOS 软件定时器的使用

    FreeRTOS中加入了软件定时器这个功能组件 是一个可选的 不属于freeRTOS内核的功能 由定时器服务任务 其实就是一个定时器任务 来提供 软件定时器是当设定一个定时时间 当达到设定的时间之后就会执行指定的功能函数 而这个功能函数就叫
  • FreeRTOS学习笔记 6 - 互斥量

    目录 1 创建 2 获取 3 释放 4 测试 FreeRTOS不支持调度方式的设置 所以下面2个宏定义可以随意设置值 define RTOS IPC FLAG FIFO 0x00 define RTOS IPC FLAG PRIO 0x01
  • FreeRTOS系列

    1 RTOS简介 RTOS全称为 Real Time Operation System 即实时操作系统 RTOS强调的是实时性 又分为硬实时和软实时 硬实时要求在规定的时间内必须完成操作 不允许超时 而软实时里对处理过程超时的要求则没有很严
  • ZYNQ中FreeRTOS中使用定时器

    使用普通的Timer中断方式时 Timer中断可以正常运行 但是UDP通信进程无法启动 其中TimerIntrHandler是中断服务程序 打印程序运行时间与从BRAM中读取的数据 void SetupInterruptSystem XSc
  • 【FreeRTOS(三)】任务状态

    文章目录 任务状态 任务挂起 vTaskSuspend 取消任务挂起 vTaskResume 挂起任务调度器 vTaskSuspendAll 取消挂起任务调度器 xTaskResumeAll 代码示例 任务挂起 取消任务挂起 代码示例 挂起
  • freeRTOS使用uxTaskGetStackHighWaterMark函数查看任务堆栈空间的使用情况

    摘要 每个任务都有自己的堆栈 堆栈的总大小在创建任务的时候就确定了 此函数用于检查任务从创建好到现在的历史剩余最小值 这个值越小说明任务堆栈溢出的可能性就越大 FreeRTOS 把这个历史剩余最小值叫做 高水位线 此函数相对来说会多耗费一点
  • 【FreeRTOS】任务通知的使用

    作者主页 凉开水白菜 作者简介 共同学习 互相监督 热于分享 多加讨论 一起进步 专栏资料 https pan baidu com s 1nc1rfyLiMyw6ZhxiZ1Cumg pwd free 点赞 收藏 再看 养成习惯 订阅的粉丝
  • FreeRTOS笔记(一)简介

    这个笔记主要依据韦东山freertos快速入门系列记录 感谢韦东山老师的总结 什么是实时操作系统 操作系统是一个控制程序 负责协调分配计算资源和内存资源给不同的应用程序使用 并防止系统出现故障 操作系统通过一个调度算法和内存管理算法尽可能把
  • [FreeRTOS入门学习笔记]定时器

    定时器的使用步骤 1 定义一个handle xTimerCreate创建 2 启动定时器 在Task1中调用 通过队列通知守护任务来执行定时器任务 要再config头文件中定义守护任务相关配置 虽然定时器是在task1中启动 但是定时器的任
  • FreeRTOS轻量级同步--任务通知

    1 简介 在FreeRTOS的配置参数中的configUSE TASK NOTIFICATIONS宏打开 一般RTOS会默认打开 如图1所示 图1 notify宏开关 RTOS在创建任务时 会创建一个32位的通知值ulNotifiedVal
  • 单片机通信数据延迟问题排查

    1 问题说明 笔者在最近的项目中 发现系统的响应延迟较高 经过排查 排除了单片机运行卡死的问题 2 原因分析 具体排查过程这里就不细致说明了 直接给出排查后原因 任务执行周期规划不合理 导致freertos队列发送接收到的命令有延迟 为了便
  • FreeRTOS之系统配置

    1 FreeRTOS的系统配置文件为FreeRTOSConfig h 在此配置文件中可以完成FreeRTOS的裁剪和配置 在官方的demo中 每个工程都有一个该文件 2 先说一下 INCLUDE 开始的宏 使用 INCLUDE 开头的宏用来
  • 如何更改 FreeRTOS 中任务的最大可用堆大小?

    我通过以下方式在任务中创建元素列表 l dllist pvPortMalloc sizeof dllist dlllist 有 32 字节大 我的嵌入式系统有 60kB SRAM 所以我希望系统可以轻松处理我的 200 个元素列表 我发现在
  • FreeRTOS 匈牙利表示法 [重复]

    这个问题在这里已经有答案了 我是 RTOS 和 C 编程的新手 而且我仍在习惯 C 的良好实践 因此 我打开了一个使用 FreeRTOS 的项目 我注意到操作系统文件使用匈牙利表示法 我知道一点符号 但面临一些新的 标准 FreeRTOS
  • C++ freeRTOS任务,非静态成员函数的无效使用

    哪里有问题 void MyClass task void pvParameter while 1 this gt update void MyClass startTask xTaskCreate this gt task Task 204
  • 小型 ARM 微控制器的 RTOS 内核之间的可量化差异 [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 有许多不同的 RTOS 可用于微控制器 我专门寻找支持 ARM Cortex M 处理器的 RTOS 另外 我对闭源解决方案不感兴趣 试图从网站
  • 哪些变量类型/大小在 STM32 微控制器上是原子的?

    以下是 STM32 微控制器上的数据类型 http www keil com support man docs armcc armcc chr1359125009502 htm http www keil com support man d

随机推荐

  • 八皇后详解

    历史 八皇后问题是一个古老而著名的问题 xff0c 是回溯算法的典型例题 该问题是十九世纪著名的数学家高斯1850年提出 xff1a 在8X8格的国际象棋上摆放八个皇后 xff0c 使其不能互相攻击 xff0c 即任意两个皇后都不能处于同一
  • KVM虚拟机创建功能详细讲解

    KVM虚拟机创建功能详细讲解 一 KVM虚拟机创建的用户操作 对于用户或者管理员来说 xff0c 虚拟机的创建有着很多的方法 xff0c 例如 xff1a kvm自带命令行工 具 使用virsh命令来创建 使用具有图形界面的virt man
  • JS中堆和栈

    什么是栈和堆 栈和堆的主要作用存储变量 xff0c 根据变量值得不同存储的位置也不同 在JS中 xff0c 栈主要是存储基本类型的变量 xff0c 包括String Number Boolean Undefined Null 和 对象类型的
  • 实例:Python调用c++文件(参数为指针和数组指针)

    本文作为Python调用c 43 43 的进阶实现 xff0c 简单的实现参考文章Python调用c 43 43 高级 xff08 swig xff09 1 一个小小的工作目标 通过Python调用c 43 43 文件 xff0c 生成一个
  • RT-Thread学习笔记——信号量

    前言 本文讲RT Thread的线程间同步之信号量 xff0c 包括为什么要进行线程间同步 信号量创建与删除 信号量获取与释放以及基于STM32的二值信号量示例和计算型信号量示例 xff0c 采用RTT amp 正点原子联合出品潘多拉开发板
  • ssh Connection closed by remote host解决

    描述 最近某台服务器隔一段时间就会有ssh连接失败的告警 xff0c 但是上去排查的时候会发现ssh并没有连接失败的错误 后来有在ssh的地方添加日志 xff0c 打印错误的输出 xff0c 最终得到的报错Stderr ssh exchan
  • linux-2.6.32.6源码目录结构分析及其driver所占比重

    find maxdepth 1 type d name exec du sh 未编译个目录文件大小 xff1a 423M linux 2 6 32 6 5 8M firmware 1 3M lib 140K init 144K virt 2
  • Formatter(格式化)和 Converter(格式化)

    功能 xff1a Spring框架的功能 xff0c Formatter和Converter均可以将一种对象类型转换成另一种对象类型 区别 xff1a Converter是通用元件 xff0c 可以在应用程序的任意层中使用 Formatte
  • 牛人 就是这样锤炼出来的,中兴资深工程师,嵌入式专家韦东山是这样炼成的!

    导读 xff1a 还在大学的你 xff0c 不知道未来何去何从 xff1f 都说迷茫是大事做不了 xff0c 小事不想做 还在做程序袁的你 xff0c 是不是也想创业 xff1f 程序袁好创业吗 xff1f Linux好学吗 xff1f 学
  • Windows下编译PX4源码并连接simulink

    目录 一 安装UAV支持PX4飞行器支持包二 下载工具链对于2019a对于2021a 三 克隆PX4源码四 构建PX4固件固件的选择编译指令报错的处理 五 MATLAB的Test Connection 一 安装UAV支持PX4飞行器支持包
  • ubuntu树莓派3/4B+的GPIO硬件串口通信

    目录 一 概要二 树莓派硬件常识三 交换硬件串口和mini串口映射关系3 1 查看原始映射3 2 编辑config文件 xff0c 置换硬件映射3 3 禁用蓝牙 四 串口功能配置4 1 编辑cmdline文件4 2 禁用蓝牙4 3 串口功能
  • JS运行原理,宏任务微任务的任务loop驱动模式

    要拎清运行原理 xff0c 就要知道js有什么无理限制和主要解决什么问题 首先 xff0c js是单线程的 xff0c 不能开辟多线程 这决定了它支持并发 xff0c 不支持并行 并发 强调的是可以一起 出 发 xff0c 并行 强调的是可
  • 关于CPU C-States 省电模式,你需要知道的事情

    为了在CPU空闲的时候降低功耗 xff0c CPU可以被命令进入low power模式 每个CPU都有几种power模式 xff0c 这些模式被统称为C states或者C modes lower power模式最早在486DX4处理器上被
  • nginx如何部署多个web应用(vue)

    服务器资源与域名资源都是有限的 xff0c 如何在同一个域名下部署多个vue项目 xff08 nginx反向代理 xff09 xff0c 通过域名后面的不同的URI来访问不同的应用 xff0c 来解决资源不足的问题 xff0c 在此期间遇到
  • 【博客8】缤果PyQt5串口调试助手V1.1(初级篇)

    超级好用的Python QT GUI串口调试助手 目录 前言 一 软件概要 xff1a 二 软件界面 xff1a 1 App动态演示 2 其他扩展展示 三 main py源码 xff1a 1 PyQt5 Serial Debug Assis
  • 主机ubuntu通过SSH访问TX2

    因为最近实验室搭建好了装有激光雷达的ros小车 xff0c 笔者想通过主机对TX2的文件进行管理和下载 但由于笔者之前使用TX2都是直接连接在显示屏上使用 xff0c 并未使用过远程访问 xff0c 为此笔者查了较多资料走了不少弯路 xff
  • IDEA快捷创建Servlet项目

    IDEA快捷创建Servlet项目 1 右键单击 要创建Servlet项目 所在的包 注 xff1a 若没有Servlet选项 打开File gt project structure 进入如下页面 xff0c 选择你使用的工程 xff0c
  • 【基础教程】Python转义字符及用法

    ASCII 编码为每个字符都分配了唯一的编号 xff0c 称为编码值 在 Python 中 xff0c 一个 ASCII 字符除了可以用它的实体 xff08 也就是真正的字符 xff09 表示 xff0c 还可以用它的编码值表示 这种使用编
  • SAP 实施新的金融工具 IFRS17规则解析

    在实施新的金融工具 IFRS 规则的过程中 xff0c 保险公司现在看到了保险负债的新标准 经过多年的长期讨论 xff0c IASB 于 2016 年 11 月承诺在 2021 年 1 月 1 日生效 xff0c 并明确表示不会考虑进一步推
  • FreeRTOS中任务控制块中关于堆栈的三个变量pxTopOfStack、pxStack、pxEndOfStack的分析

    这里写自定义目录标题 FreeRTOS中任务控制块中关于堆栈的定义 typedef struct tskTaskControlBlock volatile StackType t pxTopOfStack lt Points to the