任務創建/刪除流程
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; #endifListItem_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, //任務棧大小,單位4bytevoid * 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;// 獲取待刪除任務的TCBpxTCB = 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和棧內存(動態創建時),靜態創建需用戶手動釋放。
注意事項
- 刪除任務時需確保其未持有信號量、隊列等資源,否則可能導致內存泄漏。
- 靜態創建任務需保證預分配內存生命周期覆蓋任務運行周期。