FreeRTOS 原理 --- 最少剩余可用任务栈 与 任务栈溢出检查

最少剩余可用任务栈

函数 uxTaskGetStackHighWaterMark() 可用于获取任务的最少剩余可用栈

#if ( INCLUDE_uxTaskGetStackHighWaterMark == 1 )

    UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask )
    {
    TCB_t *pxTCB;
    uint8_t *pucEndOfStack;
    UBaseType_t uxReturn;

        pxTCB = prvGetTCBFromHandle( xTask );

        #if portSTACK_GROWTH < 0 // 对于 M3/M4 内核,为-1,表示栈是从高地址向低地址方向增长
        {
            pucEndOfStack = ( uint8_t * ) pxTCB->pxStack;
        }
        #else
        {
            pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack;
        }
        #endif

        uxReturn = ( UBaseType_t ) prvTaskCheckFreeStackSpace( pucEndOfStack ); // pucEndOfStack 是栈底

        return uxReturn;
    }

#endif /* INCLUDE_uxTaskGetStackHighWaterMark */

 任务创建时,把任务栈的值都初始化为 tskSTACK_FILL_BYTE

#define tskSTACK_FILL_BYTE    ( 0xa5U )
    /* Avoid dependency on memset() if it is not required. */
    #if( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 )
    {
        /* Fill the stack with a known value to assist debugging. */
        ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
    }
    #endif /* tskSET_NEW_STACKS_TO_KNOWN_VALUE */

函数 prvTaskCheckFreeStackSpace() 从栈底往栈顶一个字节一个字节读数据,直到读出的值不等于 tskSTACK_FILL_BYTE,如果栈被使用,其值不等于 tskSTACK_FILL_BYTE

    static uint16_t prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte )
    {
    uint32_t ulCount = 0U;

        while( *pucStackByte == ( uint8_t ) tskSTACK_FILL_BYTE )
        {
            pucStackByte -= portSTACK_GROWTH; // 栈底是最后被使用的栈内存,从栈底往栈顶延申
            ulCount++; // 退出 while 后,ulCount 等于栈未被使用的字节数
        }

        ulCount /= ( uint32_t ) sizeof( StackType_t ); /*lint !e961 Casting is not redundant on smaller architectures. */

        return ( uint16_t ) ulCount;
    }

函数 vTaskGetInfo() 也可以获取栈使用情况在内的其他信息。

任务栈溢出检查

检查点

PendSV 中断里,获取要执行的任务的函数 vTaskSwitchContext()

检查原理1 - 基于栈指针的检测

在任务切换时,FreeRTOS会检查当前任务的栈指针是否超出了为该任务分配的栈空间范围。具体来说,它会比较栈顶指针(pxTopOfStack)与栈的起始地址(pxStack)或结束地址(取决于栈的生长方向)。如果栈指针超出了合法范围,则认为发生了栈溢出。

#if( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH < 0 ) )

    /* Only the current stack state is to be checked. */
    #define taskCHECK_FOR_STACK_OVERFLOW()                                                                \
    {                                                                                                    \
        /* Is the currently saved stack pointer within the stack limit? */                                \
        if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack )                                        \
        {                                                                                                \
            vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName );    \
        }                                                                                                \
    }

#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */

检查原理2 - 基于栈填充值的检测

在任务创建时,FreeRTOS会将任务的栈空间填充为一个特定的值(如0xA5)。在任务切换时,它会检查栈空间的最后几个字节(通常是16个字节)是否仍然保持为这个特定的值。如果这些字节被修改了,则认为发生了栈溢出。

#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH < 0 ) )

    #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 );    \
        }                                                                                                \
    }

#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */

 

posted @ 2023-11-16 23:15  流水灯  阅读(985)  评论(0)    收藏  举报