freertos的核心---线程与调度

FreeRTOS线程与调度详解
本文深入介绍了FreeRTOS中的线程(任务)结构,包括任务控制块(TCB)的详细组成,任务的创建过程,以及任务如何被添加到就绪列表中。文章强调线程是FreeRTOS管理的基本单元,探讨了任务的生命周期和优先级,并提供了任务创建函数的内部实现,展示了任务栈的初始化和任务状态列表项的作用。此外,还提到了FreeRTOS中的全局列表变量,如就绪、延迟和挂起任务列表,为理解FreeRTOS的任务调度打下基础。

一.

划重点,划重点,划重点.

线程就是freertos运行管理的最小单位.一个线程有自己的生命周期,可以是一段时间也可以是forever.

具体看开发人员对于线程的规划,几个线程,每个线程处理什么事情.

先来看看线程长啥样?(任务是线程的别名,为了方便以后都称任务)

typedef struct tskTaskControlBlock             /* The old naming convention is used to prevent breaking kernel aware debuggers. */
{
    volatile StackType_t    *pxTopOfStack;    /*< 任务的桟顶. */

    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 ( configUSE_MUTEXES == 1 )
        UBaseType_t        uxBasePriority;        /*< 如果使用锁,记录任务优先级,当优先级反转复原后,恢复之前的优先级. */
        UBaseType_t        uxMutexesHeld;      
    #endif

    #if( configUSE_TASK_NOTIFICATIONS == 1 )
        volatile uint32_t ulNotifiedValue;            /*< 任务的通知数值和状态. */
        volatile uint8_t ucNotifyState;
    #endif

} tskTCB;
typedef tskTCB TCB_t;

哇塞,内容有点多.不要慌,先...自行脑补

 

二.任务的创建.

任务可以静态或者动态创建,静态和动态是内存管理的事情,后面默认都是动态创建.对于主线的理解没有任何影响.

BaseType_t xTaskCreate(    TaskFunction_t pxTaskCode,
                            const char * const pcName,        /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                            const configSTACK_DEPTH_TYPE usStackDepth,
                            void * const pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t * const pxCreatedTask )
    {
    TCB_t *pxNewTCB;
    BaseType_t xReturn;

      /*判断堆栈的增长方向,一般都是向下增长*/
        #if( portSTACK_GROWTH > 0 )
        {
           
            pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

            if( pxNewTCB != NULL )
            {
               
                pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); 

                if( pxNewTCB->pxStack == NULL )
                {
               
                    vPortFree( pxNewTCB );
                    pxNewTCB = NULL;
                }
            }
        }
        #else 
        {
        StackType_t *pxStack;

           
            pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); 

            if( pxStack != NULL )
            {
                
                pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); 

                if( pxNewTCB != NULL )
                {
                    /*这里申请成功,赋值*/
                    pxNewTCB->pxStack = pxStack;
                }
                else
                {
                   
                    vPortFree( pxStack );
                }
            }
            else
            {
                pxNewTCB = NULL;
            }
        }
        #endif /* portSTACK_GROWTH */

        if( pxNewTCB != NULL )
        {

           /*任务成员的初始化*/

            prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );

           /*然后把任务添加到就绪列表*/
            prvAddNewTaskToReadyList( pxNewTCB );
            xReturn = pdPASS;
        }
        else
        {
            xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
        }

        return xReturn;
    }

看看怎么成员初始化

 

static void prvInitialiseNewTask(     TaskFunction_t pxTaskCode,
                                    const char * const pcName,        /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                                    const uint32_t ulStackDepth,
                                    void * const pvParameters,
                                    UBaseType_t uxPriority,
                                    TaskHandle_t * const pxCreatedTask,
                                    TCB_t *pxNewTCB,
                                    const MemoryRegion_t * const xRegions )
{
StackType_t *pxTopOfStack;
UBaseType_t x;

    /*根据堆栈增长方向的不同,不同的处理方式,*/
    #if( portSTACK_GROWTH < 0 )
    {

       /*桟顶是高字节,向下增长,即是向低字节增长,通过桟指针和栈顶指针,判断桟的使用情况*/
        pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] );
        pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
    }
    #else /* portSTACK_GROWTH */
    {

        /*桟顶是低字节,向上增长,即向高字节增长,必要额外记录栈顶指针*/
        pxTopOfStack = pxNewTCB->pxStack;
        pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
    }
    #endif /* portSTACK_GROWTH */

    /* 记录任务的名字. */
    if( pcName != NULL )
    {
        for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
        {
            pxNewTCB->pcTaskName[ x ] = pcName[ x ];
            if( pcName[ x ] == ( char ) 0x00 )
            {
                break;
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
    }

   /*记录优先级*/
    if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
    {
        uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
    }

    pxNewTCB->uxPriority = uxPriority;

    /*使用锁,记录优先级,防止优先级反转*/
    #if ( configUSE_MUTEXES == 1 )
    {
        pxNewTCB->uxBasePriority = uxPriority;
        pxNewTCB->uxMutexesHeld = 0;
    }
    #endif /* configUSE_MUTEXES */

    vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
    vListInitialiseItem( &( pxNewTCB->xEventListItem ) );

   /*状态列表项的pvowner指向任务*/
    listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );

    /* 事件列表项的数值记录优先级,pvowner指向任务*/
    listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); 
    listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );

   #if ( configUSE_APPLICATION_TASK_TAG == 1 )
    {
        pxNewTCB->pxTaskTag = NULL;
    }
    #endif /* configUSE_APPLICATION_TASK_TAG */

    #if ( configUSE_TASK_NOTIFICATIONS == 1 )
    {
        pxNewTCB->ulNotifiedValue = 0;
        pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
    }
    #endif

    pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );

    if( pxCreatedTask != NULL )
    {
        *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
    }
}

这里有几个关键点.状态和事件列表项都指向的任务,并且把事件列表项设置成优先级相对于最大优先级的相反数.那我们可以大胆猜测任务运行的顺序就是通过任务的事件列表项数值来确定的.

还有就是桟顶的赋值.这里桟 顶必须在第一个位置,也是为了任务切换找到栈顶,从而进行任务的切换.

还有就是任务桟的初始化(以risc_v为例)

StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
    pxTopOfStack--;
    *pxTopOfStack = portINITIAL_MSTATUS;    /* MSTATUS */
#ifndef __riscv_32e
    pxTopOfStack -= 22;    /* X11 - X31. */
#else
    pxTopOfStack -= 6;    /* X11 - X15. */
#endif


    *pxTopOfStack = ( StackType_t ) pvParameters;    /* X10/A0 */
    pxTopOfStack -= 6; /* X5 - X9 */
    *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS;    /* RA, X1 */

    pxTopOfStack --;
    *pxTopOfStack = ( ( StackType_t ) pxCode ) ;    /* PC */

    return pxTopOfStack;
}

任务的函数指针,参数,寄存器一一入栈. 

 

再来看看加入就绪列表是个什么东东.

static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
    taskENTER_CRITICAL();
    {
        uxCurrentNumberOfTasks++;
        if( pxCurrentTCB == NULL )
        {
            pxCurrentTCB = pxNewTCB;

            if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
            {
                prvInitialiseTaskLists();
            }
        }
        else
        {
            if( xSchedulerRunning == pdFALSE )
            {
                if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
                {
                    pxCurrentTCB = pxNewTCB;
                }
            }
        }

        uxTaskNumber++;

        prvAddTaskToReadyList( pxNewTCB );
    }
    taskEXIT_CRITICAL();

    if( xSchedulerRunning != pdFALSE )
    {
        if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
        {
            taskYIELD_IF_USING_PREEMPTION();
        }
    }
}

 

1.首先进入临界区,防止中断打断

2.uxCurrentNumberOfTasks全局变量记录任务数量,

3.pxCurrentTCB全局变量,记录当前的任务句柄

4.如果是第一个任务则初始化其他全局变量,那些重要的全局变量了,如下面所说.

  否则,如果调度器挂起了,pxCurrentTCB指向最高优先级任务

5.状态列表项插入对应优先级的就绪列表项尾部

6.如果调度器没有挂起,进行任务的切换

其他重要的全局变量:

static List_t pxReadyTasksLists[ configMAX_PRIORITIES ]; /*就绪列表*/
static List_t xDelayedTaskList1;                                              /*延时列表*/
static List_t xDelayedTaskList2;                                              /*延时列表,用于时间溢出的处理*/
static List_t * volatile pxDelayedTaskList;               /*指向xDelayedTaskList1*/
static List_t * volatile pxOverflowDelayedTaskList;        /*指向xDelayedTaskList2*/
static List_t xPendingReadyList;                      /*等待就绪列表,由于调度器不能放入就绪列表,暂时放入.*/

static List_t xTasksWaitingTermination;              /*任务删除列表.*/

static List_t xSuspendedTaskList;                    /*挂起任务列表*/

 

三.思考与总结

全局列表变量和状态转换是一一对应的.

回顾之前的任务创建,就一目了然了.

有一点还没有说明-任务的切换.放在任务调度章节来说明.

.

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值