FreeRTOS堆栈溢出#

FreeRTOS中栈溢出检测配置#

在FreeRTOS中,通 define configCHECK_FOR_STACK_OVERFLOW 来配置栈溢出检测方式的,系统支持两种检测方式。

  1. #define configCHECK_FOR_STACK_OVERFLOW          1

  2. #define configCHECK_FOR_STACK_OVERFLOW          2

FreeRTOS何时进行栈溢出检测#

在每次 任务切换 时就会调用 taskCHECK_FOR_STACK_OVERFLOW 函数进行栈溢出检查。 [会触发任务切换的函数]

  • 栈溢出检测方式1源码:

    stack_macros.h#
    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源码:

    stack_macros.h#
     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 ) */
    
    该方式检查栈溢出存在的弊端:

    任务在超出任务栈入栈写入时正好写入值与初始化值相同,栈溢出检测会检测不出来。当然出现这样的概率非常小。

[会触发任务切换的函数]

:

  1. vTaskSuspend

  2. xPortStartScheduler

  3. xPortPendSVHandler

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();
}