freeRTOS的栈溢出检测机制

发布时间:2024年01月05日

1、前言

  • 后面的分析是以RISC-V架构为例,不同的架构在代码实现上有些许区别
  • RISC-V架构使用的满减栈

2、任务控制块介绍(TCB:task controller Block)

typedef struct tskTaskControlBlock   
{
    volatile StackType_t * pxTopOfStack; 	//当前任务使用的栈空间地址,也就是SP指向的地址
    ListItem_t xStateListItem;         
    ListItem_t xEventListItem;               
    UBaseType_t uxPriority;                   
    StackType_t * pxStack;  //记录分配给任务的栈空间的最低地址                    
    char pcTaskName[ configMAX_TASK_NAME_LEN ];
}

3、第一种:切换任务时检查当前栈指针是否溢出

3.1、检查栈溢出的逻辑

在这里插入图片描述

  • TCB->pxStack指向任务栈空间的最低地址
  • TCB->pxTopOfStack可以理解成当前使用到的栈地址
  • 栈从高地址向低地址使用,最后一个可用的栈空间地址就是TCB->pxStack,其中软件可以限制保留栈空间最后一小段空间,即[TCB->pxStack, TCB->pxStack+portSTACK_LIMIT_PADDING]这段区间的栈空间不可用
  • 在切换任务时,检查当前使用的栈地址是否小于TCB->pxStack+portSTACK_LIMIT_PADDING,如果小于,则说明栈空间已经溢出。
  • TCB->pxTopOfStack落在[TCB->pxStack, TCB->pxStack+portSTACK_LIMIT_PADDING]区间还不是真正意义上的栈溢出,只是软件上保留了部分栈空间,还可以挽救,可以理解成警告当前栈空间不足
  • 如果TCB->pxTopOfStack < TCB->pxStack,则真正发生了栈溢出,已经发生内存踩踏

3.2、FreeRTOS中的源码

//vApplicationStackOverflowHook:用于处理栈溢出的函数
#define taskCHECK_FOR_STACK_OVERFLOW()                                                            \
{                                                                                                 \
    /* Is the currently saved stack pointer within the stack limit? */                            \
    if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack + portSTACK_LIMIT_PADDING )           \
    {                                                                                             \
        vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \
    }                                                                                             \
}

3.3、性能分析

  • 每次在任务切换时都会检查,需要消耗部分性能,但是判断逻辑简单
  • 切换任务时的检查,只能判断当前是否发生栈溢出。
  • 无法检测任务执行时是否发生栈溢出。如果在任务执行时发生栈溢出,但是在切换任务时,栈空间已经被释放,在切换时的栈指针又指向栈空间,这是检测不出来的。

4、第二种:切换任务时检查栈空间最后16个字节是否曾被使用

4.1、检查栈溢出的逻辑

在这里插入图片描述

  • 在创建任务时,把任务的栈空间赋值成特殊值
  • 在切换任务时,检查栈空间的最后一小段是否被改写过
  • 如果不是特殊值则说明该段栈空间被使用过,则认为任务栈空间不足,曾经发生过栈溢出

4.2、FreeRTOS中的源码

 #define taskCHECK_FOR_STACK_OVERFLOW()                                                            \
 {                                                                                                 \
     const uint32_t * const pulStack = ( uint32_t * ) pxCurrentTCB->pxStack;                       \
     const uint32_t ulCheckValue = ( uint32_t ) 0xa5a5a5a5;                                        \
                                                                                                   \
     if( ( pulStack[ 0 ] != ulCheckValue ) ||                                                      \
         ( pulStack[ 1 ] != ulCheckValue ) ||                                                      \
         ( pulStack[ 2 ] != ulCheckValue ) ||                                                      \
         ( pulStack[ 3 ] != ulCheckValue ) )                                                       \
     {                                                                                             \
         vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \
     }                                                                                             \
 }

4.3、性能分析

  • 每次切换时都要判断一小段栈空间的值,会带来不小的性能开销
  • 能够检查出发生过栈空间溢出,但是不能定位到是何时何地发生过栈空间溢出
  • 栈空间的特殊值被修改,不一定是任务本身发生栈空间,也可能是其他任务发生了内存越界,修改了本任务的栈空间内容

5、总结

  • 上面介绍的两种栈空间溢出检测机制,基本只能检测出是否发生过栈空间溢出,不好定位是何时何地发生栈空间溢出
  • 更多是用来判断当前分配给任务的栈空间是否足够,如果发生异常则给任务多分配点栈空间
  • 两种任务检查机制都会消耗性能,可以在DEBUG版本程序进行栈空间溢出检测,在RELEASE版本程序关闭栈空间溢出检测机制,以节省性能开销
  • 还有硬件的异常检测机制,参考博客:《四种“栈溢出检测方法”实现分析(2种纯软件、一种纯硬件、一种软硬件结合)》
文章来源:https://blog.csdn.net/weixin_42031299/article/details/135372135
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。