FreeRTOS 任务栈溢出检测
栈生长方向从高地址向低地址生长(M3、M4)

(1) 上图标识 1 的位置是 RTOS 的某个任务调用了函数 test()前的 SP 栈指针位置。
(2) 上图标识 2 的位置是调用了函数 test 需要保存返回地址到栈空间。这一步不是必须的,对于 M3 和 M4 内核是先将其保存到 LR 寄存器中,如果 LR 寄存器中有保存上一级函数的返回地址,需要将 LR 寄存器中的内容先入栈。
(3) 上图标识 3 的位置是局部变量 int i 和 int array[10]占用的栈空间,但申请了栈空间后已经越界了。这个就是所谓的栈溢出了。如果用户在函数 test 中通过数组 array 修改了这部分越界区的数据且这部分越界的栈空间暂时没有用到或者数据不是很重要,情况还不算严重,但是如果存储的是关键数据,会直接导致系统崩溃。
(4) 上图标识 4 的位置是局部变量申请了栈空间后,栈指针向下偏移(返回地址+变量 i+10 个数组元 素)*4 =48 个字节。
(5) 上图标识 5 的位置可能是其它任务的栈空间,也可能是全局变量或者其它用途的存储区,如果 test 函数在使用中还有用到栈的地方就会从这里申请,这部分越界的空间暂时没有用到或者数据不是很重要,情况还不算严重,但是如果存储的是关键数据,会直接导致系统崩溃。
钩子函数
钩子函数的主要作用就是对原有函数的功能进行扩展,用户可以根据自己的需要往里面添加相关的测试代码,可以在 FreeRTOS 工程中检索这个钩子函数 vApplicationStackOverflowHook 所在的位置。
FreeRTOS 的栈溢出检测机制
FreeRTOS 提供了两种栈溢出检测机制,这两种检测都是在任务切换时才会进行:
◆ 方法一 在任务切换时检测任务栈指针是否过界了,如果过界了,在任务切换的时候会触发栈溢出钩子函数
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName );
用户可以在钩子函数里面做一些处理。这种方法不能保证所有的栈溢出都能检测到。
比如任务在执行的过程中出现过栈溢出。任务切换前栈指针又恢复到了正常水平,这种情况在任务切换的时候是检测不到的。
又比如任务栈溢出后,把这部分栈区的数据修改了,这部分栈区的数据不重要或者暂时没有用到还好,但如果是重要数据被修改将直接导致系统进入硬件异常,这种情况下,栈溢出检测功能也是检测不到的。
使用方法一需要用户在 FreeRTOSConfig.h 文件中配置如下宏定义:
#define configCHECK_FOR_STACK_OVERFLOW 1
◆ 方法二 任务创建的时候将任务栈所有数据初始化为 0xa5,任务切换时进行任务栈检测的时候会检测末尾的 16 个字节是否都是 0xa5,通过这种方式来检测任务栈是否溢出了。
相比方法一,这种方法的速度稍慢些,但是这样就有效地避免了方法一里面的部分情况。不过依然不能保证所有的栈溢出都能检测到。
比如任务栈末尾的 16 个字节没有用到,即没有被修改,但是任务栈已经溢出了,这种情况是检测不到的。
另外任务栈溢出后,任务栈末尾的 16 个字节没有修改,但是溢出部分的栈区数据被修改了,这部分栈区的数据不重要或者暂时没有用到还好,但如果是重要数据被修改将直接导致系统进 入硬件异常,这种情况下,栈溢出检测功能也是检测不到的。 使用方法二需要用户在 FreeRTOSConfig.h 文件中配置如下宏定义:
#define configCHECK_FOR_STACK_OVERFLOW 2