1、前言
后面的分析是以RISC-V架构为例,不同的架构在代码实现上有些许区别 RISC-V架构使用的满减栈
2、任务控制块介绍(TCB:task controller Block)
typedef struct tskTaskControlBlock
{
volatile StackType_t * pxTopOfStack;
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中的源码
# define taskCHECK_FOR_STACK_OVERFLOW ( ) \
{ \
\
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种纯软件、一种纯硬件、一种软硬件结合)》 ;