Freertos源码分析:任务创建/删除

任务创建/删除流程

1.简介

FreeRTOS 中任务创建通过 xTaskCreate()xTaskCreateStatic() 实现。动态创建(xTaskCreate)会自动分配任务栈和TCB(任务控制块),静态创建(xTaskCreateStatic)需用户预分配内存。

动态创建流程:

  • 参数校验:检查栈深度、任务函数指针等有效性。
  • 内存分配:调用 pvPortMalloc 分配TCB和栈空间。
  • TCB初始化:填充任务名、优先级、栈指针等字段。
  • 栈初始化:调用 pxPortInitialiseStack 模拟中断压栈,构造初始上下文。
  • 任务就绪:将任务加入就绪列表,触发调度(若调度已启动)。

任务删除流程:任务删除通过 vTaskDelete() 实现,支持删除其他任务或自身(传参 NULL)。删除后资源需由空闲任务回收。

核心步骤:

  • 任务状态检查:若删除自身标记为待删除状态并触发调度;否则直接从就绪/阻塞列表移除。
  • 资源释放:TCB和栈内存由空闲任务通过 prvDeleteTCB() 释放。
  • 调度触发:若删除高优先级任务,可能引发上下文切换。
2.任务控制块/链表项:
typedef struct tskTaskControlBlock       
{
    volatile StackType_t * pxTopOfStack; 

    #if ( portUSING_MPU_WRAPPERS == 1 )
        xMPU_SETTINGS xMPUSettings; 
    #endif

    ListItem_t xStateListItem;                     //链表项,后面会加入任务的状态链表
    ListItem_t xEventListItem;                     //链表项,后页面会加入信号量,消息队列等链表  
    UBaseType_t uxPriority;                        //任务优先级                
    StackType_t * pxStack;                         //任务栈的起点               
    char pcTaskName[ configMAX_TASK_NAME_LEN ];    //任务名,可以为空吧

    #if ( configUSE_TRACE_FACILITY == 1 )          //trace时使用
        UBaseType_t uxTCBNumber;  
        UBaseType_t uxTaskNumber; 
    #endif

    #if ( configUSE_MUTEXES == 1 )                //互斥时使用--初始优先级和继承优先级
        UBaseType_t uxBasePriority; 
        UBaseType_t uxMutexesHeld;
    #endif

    #if ( configUSE_TASK_NOTIFICATIONS == 1 )     //任务通知使用--存放任务通知的值和状态
        volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
        volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
    #endif

} tskTCB;
struct xLIST_ITEM
{
    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE          
    configLIST_VOLATILE TickType_t xItemValue;                //链表值,用于链表排序
    struct xLIST_ITEM * configLIST_VOLATILE pxNext;           //指向后一个链表项
    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;       //指向前一个链表项
    void * pvOwner;                                           //一般用于存放TCB指针                                    
    struct xLIST * configLIST_VOLATILE pxContainer;           //用于存包含此链表项的链表的指针
    listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE          
};
3.任务创建/删除(下文只分析动态创建的):

为什么推荐使用动态创建:

  • 我是用的芯片为f407,ram空间为:192K的----包括64K的CCM(仅CPU可访问)、112K的SRAM1(主RAM)和16K的SRAM2(外设使用)。空间足够大可以直接用heap4管理。
  • heap4提供了空间管理回收,若是自己分配还需要自己释放,检查是否溢出
/***************************************************************
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,                          //回调函数名/地址
                            const char * const pcName,                      //任务名字
                            const configSTACK_DEPTH_TYPE usStackDepth,      //任务栈大小,单位4byte
                            void * const pvParameters,                      //回调函形参
                            UBaseType_t uxPriority,                         //任务优先级,数值越大,优先级越高
                            TaskHandle_t * const pxCreatedTask )            //任务控制块地址/句柄
********************************************************************/

代码分析:

1.xTaskCreate
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
                            const char * const pcName, 
                            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 )                                //增栈
        {
           ************************************
        }
        #else                                                       //减栈
        {
            StackType_t * pxStack;  
            /*-------------------------申请任务栈空间-----------------------*/         
            pxStack = pvPortMallocStack((((size_t)usStackDepth)*sizeof( StackType_t ) ) );
            if( pxStack != NULL )                                   //如果申请栈空间成功
            {   /*---------------------申请TCB空间-------------------*/             
                pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); 
                if( pxNewTCB != NULL )                              //如果TCB空间申请成功
                {
                    memset((void*)pxNewTCB,0x00,sizeof(TCB_t));     //清空TCB空间
                    pxNewTCB->pxStack = pxStack;                    //TCB->pxStack初始化
                }
                else {vPortFreeStack( pxStack );}                   //申请TCB空间失败
            }
            else      pxNewTCB = NULL;                              //申请任务栈空间失败
   
        }
        #endif /* portSTACK_GROWTH */

        if(pxNewTCB != NULL)                                        //TCB和栈空间都申请成功
        {
            prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );          //初始化任务TCB以及栈
            prvAddNewTaskToReadyList( pxNewTCB );                   //将任务添加到就绪链表
            xReturn = pdPASS;
        }
        else xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;       //申请空间失败          
        
        return xReturn;                                             //返回
    }
/*------------------------------------------------------------------------------------*/

1.1调用函数分析:

 prvInitialiseNewTask()

static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
                                  const char * const pcName, 
                                  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;

/*------------------------初始化任务栈,每byte都为0xa5,检查栈溢出使用----------------------*/
    #if ( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 )
    {       
        (void)memset(pxNewTCB->pxStack,(int)tskSTACK_FILL_BYTE,(size_t)ulStackDepth * sizeof(StackType_t));
    }
    #endif 
/*------------------------------------------------------------------------------------*/    
    
/*------------------如果是满减栈:找出栈顶位置(高地址),对齐-------------------------------*/
    #if ( portSTACK_GROWTH < 0 )
    {
        pxTopOfStack = &(pxNewTCB->pxStack[ulStackDepth-(uint32_t)1]);
        pxTopOfStack = (StackType_t *)(((portPOINTER_SIZE_TYPE)pxTopOfStack)&(~(( portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK))); 
       
        configASSERT(((portPOINTER_SIZE_TYPE)pxTopOfStack&(portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK)==0UL));

    }
    #else /* portSTACK_GROWTH */
    *****************************
    #endif /* portSTACK_GROWTH */
/*-----------------------------------------------------------------------------------*/
    
/*-------------------如果任务名不为NULL,TCB->pcTaskName初始化--------------------------*/
    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';
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }
/*----------------------------------------------------------------------------------*/
   
/*------------任务优先级设置:如果任务优先级大于最大优先级,设置为最大优先级
    configASSERT( uxPriority < configMAX_PRIORITIES );
    if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
    {
        uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }
    pxNewTCB->uxPriority = uxPriority;
/*----------------------------------------------------------------------------------*/    


/*--------------------------如果设置了互斥,记录原优先级-------------------------------*/
    #if ( configUSE_MUTEXES == 1 )
    {
        pxNewTCB->uxBasePriority = uxPriority;
    }
    #endif /* configUSE_MUTEXES */
/*---------------------------------------------------------------------------------*/

/*------------链表项初始化,清除链表项中pxContainer,表示未加入任何链表------------------*/
    vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
    vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
/*---------------------------------------------------------------------------------*/ 

/*将TCB指针放入链表项StateListItem,EventListItem,优先级和最高优先级的差放入xEventListItem-*/   
    listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );    
    listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );
    listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );
/*---------------------------------------------------------------------------------*/


    #if ( portUSING_MPU_WRAPPERS == 1 )
    {
      **********************************
    }
    #else  ( void ) xRegions;
    #endif

/*--------------------------------TCB初始化-------------------------------------*/   
    #if ( portUSING_MPU_WRAPPERS == 1 )
    {       
       ********************************
    }
    #else /* portUSING_MPU_WRAPPERS */
    {
        #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 )
        {
           ****************************************
        }
        #else                                                 //获取栈顶
        {
            pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
        }
        #endif /* portHAS_STACK_OVERFLOW_CHECKING */
    }
    #endif 
/*----------------------------TCB地址存入pxCreatedTask--------------------------*/
    if( pxCreatedTask != NULL )
    {
        *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }
}
/*------------------------------------------------------------------------------------*/

prvAddNewTaskToReadyList( )

static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
{
    taskENTER_CRITICAL();                                               //进入临界缓冲区
    {
        uxCurrentNumberOfTasks++;
/*-----------------------------------如果当前没有任务-----------------------------------*/
        if( pxCurrentTCB == NULL )
        {
            pxCurrentTCB = pxNewTCB;
            if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )           //如果这是第一个任务
            {             
                prvInitialiseTaskLists();
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
/*---------------------------------------当前有的任务-----------------------------------*/
        else
        {           
            if( xSchedulerRunning == pdFALSE )                          //如果没有开启调度
            {
                /*--如果新任务优先级高于或者等于当前任务,当前任务设置为新任务--*/
                if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
                {
                    pxCurrentTCB = pxNewTCB;
                }
                else   mtCOVERAGE_TEST_MARKER();                             
            }
            else      //如果开启调度,不需要更换当前任务
            {
                mtCOVERAGE_TEST_MARKER();                              
            }
        }

        uxTaskNumber++;                                                 //统计任务数量

        #if ( configUSE_TRACE_FACILITY == 1 )
        {
            pxNewTCB->uxTCBNumber = uxTaskNumber;                       //TCB中记录自己序号
        }
        #endif /* configUSE_TRACE_FACILITY */
        traceTASK_CREATE( pxNewTCB );

        prvAddTaskToReadyList( pxNewTCB );

        portSETUP_TCB( pxNewTCB );
    }
    taskEXIT_CRITICAL();                                                //退出临界缓冲区

    if( xSchedulerRunning != pdFALSE )                                  //如果调度开启
    {        
        if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )           //新加入任务优先级高
        {
            taskYIELD_IF_USING_PREEMPTION();                   //唤醒任务切换-唤醒pendsv中断
        }
        else                                                   //新任务优先级低,不理会
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    else                                                       //如果任务调度没开启,不予理会
    {
        mtCOVERAGE_TEST_MARKER();
    }
}
/*-----------------------------------------------------------*/

    代码片段:

    void vTaskDelete( TaskHandle_t xTaskToDelete ) {
        TCB_t *pxTCB;
    
        // 获取待删除任务的TCB
        pxTCB = prvGetTCBFromHandle( xTaskToDelete );
    
        // 从状态列表中移除
        uxListRemove( &( pxTCB->xStateListItem ) );
    
        // 如果是删除自身,标记为待删除并触发调度
        if( xTaskToDelete == NULL ) {
            vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );
            portYIELD();
        } else {
            prvDeleteTCB( pxTCB );
        }
    }
    
    2.xTaskCreate()
        void vTaskDelete( TaskHandle_t xTaskToDelete )
        {
            TCB_t * pxTCB;
            taskENTER_CRITICAL();                                     //进入临界区
            {
                pxTCB = prvGetTCBFromHandle( xTaskToDelete );         //判断删除自身还是别的任务      
    /*------------------------------将任务从其状态链表中移除------------------------------*/
                if( uxListRemove(&(pxTCB->xStateListItem))==(UBaseType_t)0)
                {
                    taskRESET_READY_PRIORITY( pxTCB->uxPriority );    //将其从就绪链表中移除
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
    /*--------------------------------如果任务在等待事件---------------------------------*/
                if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
                {
                    ( void ) uxListRemove( &( pxTCB->xEventListItem ) );  //将事件从其链表中移除
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
                uxTaskNumber++;                                        //任务数量依旧增加
    /*--------------------------------如果待删除任务是当前任务---------------------------*/
                if( pxTCB == pxCurrentTCB )
                {
                    /*-------任务放入终止链表中,后续在idle任务中删除--------*/
                    vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );
    
                    /*----增加此值,目的是idle任务可以知道有任务需要删除-----*/
                    ++uxDeletedTasksWaitingCleanUp;        
                    
                    /*---window模式使用,arm不用理会----*/
                    traceTASK_DELETE( pxTCB );
                    portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
                }
    /*-------------------------------如果待删除任务不是当前任务---------------------------*/
                else
                {
                    --uxCurrentNumberOfTasks;                          //减少当前任务数量
                    traceTASK_DELETE( pxTCB );                        
                    prvResetNextTaskUnblockTime();                     //重置解除阻塞时间
                }
            }
            taskEXIT_CRITICAL();                                       //退出临界保护
    /*----------------如果要删除的任务不是当前任务,直接删除TCB,释放空间--------------------*/
            if( pxTCB != pxCurrentTCB )
            {
                prvDeleteTCB( pxTCB );
            }
    /*----------------------------如果调度器重新开始计时调度-----------------------------*/
            if( xSchedulerRunning != pdFALSE )
            {
                if( pxTCB == pxCurrentTCB )
                {
                    configASSERT( uxSchedulerSuspended == 0 );
                    portYIELD_WITHIN_API();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        }
    

    关键函数说明

    • prvInitialiseNewTask():初始化TCB结构,包括栈顶指针、任务入口、优先级链表等。
    • prvAddNewTaskToReadyList():将任务加入就绪列表,若优先级高于当前任务则触发调度。
    • prvDeleteTCB():释放TCB和栈内存(动态创建时),静态创建需用户手动释放。
    注意事项
    • 删除任务时需确保其未持有信号量、队列等资源,否则可能导致内存泄漏。
    • 静态创建任务需保证预分配内存生命周期覆盖任务运行周期。
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值