FreeRTOS堆栈溢出#
FreeRTOS中栈溢出检测配置#
在FreeRTOS中,通 define configCHECK_FOR_STACK_OVERFLOW
来配置栈溢出检测方式的,系统支持两种检测方式。
#define configCHECK_FOR_STACK_OVERFLOW 1
#define configCHECK_FOR_STACK_OVERFLOW 2
第1种方式,只检测当前栈顶指针是否小于任务栈的最大值 栈溢出检测方式1源码
第2种方式,检查任务栈的最后4个字内容是否为初始化写入的
0xa5a5a5a5
栈溢出检测方式2源码
FreeRTOS何时进行栈溢出检测#
在每次 任务切换 时就会调用 taskCHECK_FOR_STACK_OVERFLOW
函数进行栈溢出检查。
[会触发任务切换的函数]
栈溢出检测方式1源码:
56#if ( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH < 0 ) ) 57 58/* Only the current stack state is to be checked. */ 59 #define taskCHECK_FOR_STACK_OVERFLOW() \ 60 { \ 61 /* Is the currently saved stack pointer within the stack limit? */ \ 62 if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack + portSTACK_LIMIT_PADDING ) \ 63 { \ 64 vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ 65 } \ 66 } 67 68#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */
- 该方式检查栈溢出存在的弊端:
当任务在执行可程中超过了该任务的栈顶后又出栈恢复到任务栈内,此时任务栈以外的数据已被篡改。但栈溢出无法检测到
栈溢出检测方式2源码:
87#if ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH < 0 ) ) 88 89 #define taskCHECK_FOR_STACK_OVERFLOW() \ 90 { \ 91 const uint32_t * const pulStack = ( uint32_t * ) pxCurrentTCB->pxStack; \ 92 const uint32_t ulCheckValue = ( uint32_t ) 0xa5a5a5a5; \ 93 \ 94 if( ( pulStack[ 0 ] != ulCheckValue ) || \ 95 ( pulStack[ 1 ] != ulCheckValue ) || \ 96 ( pulStack[ 2 ] != ulCheckValue ) || \ 97 ( pulStack[ 3 ] != ulCheckValue ) ) \ 98 { \ 99 vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ 100 } \ 101 } 102 103 #endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */
- 该方式检查栈溢出存在的弊端:
任务在超出任务栈入栈写入时正好写入值与初始化值相同,栈溢出检测会检测不出来。当然出现这样的概率非常小。
FreeRTOS中如何处理栈溢出#
当检测到栈溢出后会调用 vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName )
函数,
我们可以自定义该函数并在该函数中对栈溢出做相应的处理
- 栈溢出未安设想运行:
本想在发生栈溢出时打印栈溢出日志,并关闭当前任务,代码如下:
void vApplicationStackOverflowHook (osThreadId_t xTask, char *pcTaskName) { log_a("Thread %s[%x]",pcTaskName,(uint32_t)xTask); osThreadExit(); }
运行时,当主任务发生栈溢出时。却没有打印栈溢出信息,程序停在了
osThreadExit
的for循环处。分析其原因:日志异步打印任务优先级低于当前主任务,当前任务又被阻塞在 osThreadExit 处所以日志无法打印出来
处理方法:先关闭异步打印,再打印栈溢出信息
void vApplicationStackOverflowHook(osThreadId_t xTask, char *pcTaskName) { #ifdef ELOG_ASSERT_ENABLE elog_async_enabled(false); #endif log_e("%s[%0#x] stack over flowed!",pcTaskName,xTask); osThreadExit(); }