FreeRTOS 提供了多种特性来辅助跟踪调试栈相关的问题。
uxTaskGetStackHighWaterMark()函数
每个任务都独立维护自己的栈空间,栈空间总量在任务创建时进行设定。
uxTaskGetStackHighWaterMark() 主要用来查询指定任务的运行历史中,其栈空间还差
多少就要溢出。这个值被称为栈空间的”高水线(High Water Mark)”。
uxTaskGetStackHighWaterMark() API 函数原型如下:
unsigned portBASE_TYPE uxTaskGetStackHighWaterMark( xTaskHandle xTask);
参数名 | 描述 |
---|
xTask | 被查询任务的句柄——欲知如何获得任务句柄,详情请参见 API 函数xTaskCreate()的参数 pxCreatedTask。如果传入 NULL 句柄,则任务查询的是自身栈空间的高水线 |
返回值 | 任务栈空间的实际使用量会随着任务执行和中断处理过程上下浮动。uxTaskGetStackHighWaterMark()返回从任务启动执行开始的运行历史中,栈空间具有的最小剩余量。这个值即是栈空间使用达到最深时的剩下的未使用的栈空间。这个值越是接近 0,则这个任务就越是离栈溢出不远了。 |
运行时栈侦测
概述
FreeRTOS 包含两种运行时栈侦测机制,由 FreeRTOSConfig.h 中的配置常量
configCHECK_FOR_STACK_OVERFLOW 进行控制。这两种方式都会增加上下切换开销。
栈溢出钩子函数(或称回调函数)由内核在侦测到栈溢出时调用。要使用栈溢出钩子函数,需要进行以下配置:
- 在 FreeRTOSConfig.h 中把 configCHECK_FOR_STACK_OVERFLOW 设为 1 或 2。
- 提供钩子函数的具体实现,采用如下的函数名和函数原型。
void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed portCHAR *pcTaskName );
- 栈溢出钩子函数只是为了使跟踪调试栈空间错误更容易,而无法在栈溢出时对其进
行恢复。函数的入口参数传入了任务句柄和任务名,但任务名很可能在溢出时已经遭到
破坏。 - 栈溢出钩子函数还可以在中断的上下文中进行调用。
- 某些微控制器在检测到内存访问错误时会产生错误异常,很可能在内核调用栈溢出
钩子函数之前就触发了错误异常中断
运行时栈侦测方法
方法1
当 configCHECK_FOR_STACK_OVERFLOW 设置为 1 时选用方法 1。
任务被交换出去的时候,该任务的整个上下文被保存到它自己的栈空间中。这时任 务栈的使用应当达到了一个峰值。当configCHECK_FOR_STACK_OVERFLOW 设为 1时,内核会在任务上下文保存后检查栈指针是否还指向有效栈空间。 一旦检测到栈指 针的指向已经超出任务栈的有效范围,栈溢出钩子函数就会被调用。
方法 1 具有较快的执行速度,但栈溢出有可能发生在两次上下文保存之间,这种情
况不会被侦测到。
方法2
将 configCHECK_FOR_STACK_OVERFLOW 设为 2 就可以选用方法 2。方法 2
在方法 1 的基础上进行了一些补充。
当创建任务时,任务栈空间中就预置了一个标记。方法 2 会检查任务栈的最后 20 个字节,查看预置在这里的标记数据是否被覆盖。如果最后20 个字节的标记数据与预 设值不同,则栈溢出钩子函数就会被调用。
方法 2 没有方法 1 的执行速度快,但测试仅仅 20 个字节相对来说也是很快的。这
种方法应该可以侦测到任何时候发生的栈溢出,虽然理论上还是有可能漏掉一些情况,
但这些情况几乎是不可能发生的。
局限性
无论方法1、方法2都是在任务进行切换时执行,存在局限性:
- 任务执行的过程中出现过栈溢出,但任务切换前栈指针又恢复到了正常水平(方法1)
- 任务栈末尾的 16 个字节没有用到,即不会被修改,但是任务栈已经溢出了(方法2)
- 任务栈溢出后,把系统中的重要数据修改了导致系统直接进入Hardfault(方法1、2)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)