FreeRTOS:TCB_t结构体解读(转载)

TCB_t:任务控制块

TCB_t的全称为Task Control Block,也就是任务控制块,这个结构体包含了一个任务所有的信息,它的定义以及相关变量的解释如下:

html 复制代码
typedef struct tskTaskControlBlock             
    {
        // 这里栈顶指针必须位于TCB第一项是为了便于上下文切换操作,详见xPortPendSVHandler中任务切换的操作。
        volatile StackType_t    *pxTopOfStack;    

        // MPU相关暂时不讨论
        #if ( portUSING_MPU_WRAPPERS == 1 )
/*< MPU设置被定义为端口层的一部分。它必须是TCB结构的第二个成员。 */
            xMPU_SETTINGS    xMPUSettings;        
        #endif

        // 表示任务状态,不同的状态会挂接在不同的状态链表下
        // 由于configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES = 0 ,此链表共5个成员
        ListItem_t            xStateListItem;    
        // 事件链表项,会挂接到不同事件链表下
        ListItem_t            xEventListItem;        
        // 任务优先级,数值越大优先级越高
        UBaseType_t            uxPriority;            
        // 指向堆栈起始位置,这只是单纯的一个分配空间的地址,可以用来检测堆栈是否溢出
        StackType_t            *pxStack;            
        // 任务名
        char                pcTaskName[ configMAX_TASK_NAME_LEN ];

        // 指向栈尾,可以用来检测堆栈是否溢出
        #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
            StackType_t        *pxEndOfStack;        
        #endif

        // 记录临界段的嵌套层数
        #if ( portCRITICAL_NESTING_IN_TCB == 1 )
            UBaseType_t        uxCriticalNesting;    
        #endif

        // 跟踪调试用的变量
        #if ( configUSE_TRACE_FACILITY == 1 )
            UBaseType_t        uxTCBNumber;        
            UBaseType_t        uxTaskNumber;        
        #endif

        // 任务优先级被临时提高时,保存任务原本的优先级
        #if ( configUSE_MUTEXES == 1 )
            UBaseType_t        uxBasePriority;        
            UBaseType_t        uxMutexesHeld;
        #endif

        // 任务的一个标签值,可以由用户自定义它的意义,例如可以传入一个函数指针可以用来做Hook    函数调用
        #if ( configUSE_APPLICATION_TASK_TAG == 1 )
            TaskHookFunction_t pxTaskTag;
        #endif

        // 任务的线程本地存储指针,可以理解为这个任务私有的存储空间
        #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
            void            *pvThreadLocalStoragePointers[     configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
        #endif

        // 运行时间变量
        #if( configGENERATE_RUN_TIME_STATS == 1 )
            uint32_t        ulRunTimeCounter;    
        #endif

        // 支持NEWLIB的一个变量
        #if ( configUSE_NEWLIB_REENTRANT == 1 )
            struct    _reent xNewLib_reent;
        #endif

        // 任务通知功能需要用到的变量
        #if( configUSE_TASK_NOTIFICATIONS == 1 )
            // 任务通知的值 
            volatile uint32_t ulNotifiedValue;
            // 任务通知的状态
            volatile uint8_t ucNotifyState;
        #endif

        // 用来标记这个任务的栈是不是静态分配的
        #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) 
            uint8_t    ucStaticallyAllocated;         
        #endif

        // 延时是否被打断
        #if( INCLUDE_xTaskAbortDelay == 1 )
            uint8_t ucDelayAborted;
        #endif

        // 错误标识
        #if( configUSE_POSIX_ERRNO == 1 )
            int iTaskErrno;
        #endif

    } tskTCB;
    typedef tskTCB TCB_t;

在TCB_t结构体的定义中可以看到根据栈的生长方式的不同,其将具有不同的成员变量pxEndOfStack,在这里说明一下栈的生长方式是如何定义的,以及为何生长方式会存在pxEndOfStack这一变量的差异。

栈的生长方式可以分为两种,一种是向下生长,一种是向上生长,FreeRTOS中用portSTACK_GROWTH来区分这两种生长方式,portSTACK_GROWTH大于0为向上生长,小于零为向下生长。两种生长方式的区别可以简单概括如下

  • 向上生长:入栈时栈顶指针增加,出栈时栈顶指针减小。

  • 向下生长:入栈时栈顶指针减小,出栈时栈顶指针增加。

    为什么会有这两种出入栈方式呢?为何不将所有芯片统一成一种生长方式?这一点应该是芯片设计的实际需要,具体原因无法解答。

    有了上图栈的生长方式为什么会影响成员变量的个数很好理解了,pxStack是指向栈内存分配的起始地址(低地址),pxEndOfStack是指向栈的尾部的,当栈是向下生长时,pxStack和pxEndOfStack值是一致的,再定义pxEndOfStack浪费了内存,而栈是向上生长时pxStack与pxEndOfStack的值不一致,如果想知道栈的结束地址,必须要定义一个变量pxEndOfStack来存储,以用于后续的栈溢出检测等操作。

    状态链表

FreeRTOS中的任务一共有四种状态分别是运行状态(Running State),就绪状态(Ready State),阻塞状态(Blocked State),挂起状态(Suspended State),其含义可以简单理解为

  • 运行状态:正在执行的任务。

  • 就绪状态:等待获得执行权的任务。

  • 阻塞状态:直到某些条件达成才会重新进入就绪态等待获得执行权,否则不会执行的任务。

  • 挂起状态:除非被主动恢复,否则永远不会执行。

这四种链表分别对应着pxCurrentTCB,pxReadyTasksLists,pxDelayedTaskList,xSuspendedTaskList这四个变量。除运行状态外,任务处于其它状态时,都是通过将任务TCB中的xStateListItem挂到相应的链表下来表示的。

相关推荐
C++ 老炮儿的技术栈2 小时前
VSCode -配置为中文界面
大数据·c语言·c++·ide·vscode·算法·编辑器
刃神太酷啦2 小时前
聚焦 string:C++ 文本处理的核心利器--《Hello C++ Wrold!》(10)--(C/C++)
java·c语言·c++·qt·算法·leetcode·github
物联网嵌入式小冉学长3 小时前
10.C S编程错误分析
c语言·stm32·单片机·算法·嵌入式
不过四级不改名6771 天前
用c语言实现简易c语言扫雷游戏
c语言·算法·游戏
我命由我123451 天前
嵌入式 STM32 开发问题:烧录 STM32CubeMX 创建的 Keil 程序没有反应
c语言·开发语言·c++·stm32·单片机·嵌入式硬件·嵌入式
C++ 老炮儿的技术栈1 天前
手动实现strcpy
c语言·开发语言·c++·算法·visual studio
xtmatao1 天前
正整数的正向分解
c语言
whoarethenext1 天前
使用C/C++的OpenCV 构建人脸识别并自动抓拍系统
c语言·c++·opencv
Navigator_Z1 天前
LeetCode //C - 757. Set Intersection Size At Least Two
c语言·算法·leetcode
几道之旅2 天前
零基础RT-thread第二节:按键控制
c语言·stm32