freeRTOS的栈溢出检测机制

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种纯软件、一种纯硬件、一种软硬件结合)》
  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

正在起飞的蜗牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值