FreeRTOS Task Management(3)- 任務建立與刪除
阿新 • • 發佈:2021-06-17
FreeRTOS Task Management(3)- 任務建立與刪除
/* FreeRTOS Kernel V10.4.1 */
/* 為了便於描述,原始碼中去除了一些用於除錯的語句,如常見的trace_XXX, mtCOVERAGE_TEST_MARKER()等 */
原文連結:https://www.cnblogs.com/yanpio/p/14892113.html
1 任務建立
FreeRTOS
提供了一系列的任務建立函式,主要的區別是建立任務時,記憶體分配方式存在差異。下表對比了幾種任務建立函式的記憶體分配方式,從原始碼中可以具體瞭解到這些不同之處。
四種任務建立函式都會呼叫初始化函式prvInitialiseNewTask()
prvAddNewTaskToReadyList()
.以下首先介紹這兩個函式。
1.1 prvInitialiseNewTask()
/* 初始化新的task,主要是對TCB進行操作。*/ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,/* task執行函式 */ const char * const pcName, /* task名稱 */ const uint32_t ulStackDepth,/* task棧深度,單位是word,即 4 bytes */ void * const pvParameters,/* 用於傳遞引數給task,可以指向一個簡單變數,也可以指向一個結構體變數*/ UBaseType_t uxPriority,/* 優先順序 */ TaskHandle_t * const pxCreatedTask,/* 可用於返回任務控制代碼 */ TCB_t * pxNewTCB, /* TCB指標 */ const MemoryRegion_t * const xRegions ) /* 如果使用記憶體保護單元(MPU),則會使用該變數*/ { StackType_t * pxTopOfStack; UBaseType_t x; /* 如果使用 portUSING_MPU_WRAPPERS,則進行如下操作。對此不太理解,因此後文預設 portUSING_MPU_WRAPPERS = 0 */ #if ( portUSING_MPU_WRAPPERS == 1 ) /* Should the task be created in privileged mode? */ BaseType_t xRunPrivileged; if( ( uxPriority & portPRIVILEGE_BIT ) != 0U ) { xRunPrivileged = pdTRUE; } else { xRunPrivileged = pdFALSE; } uxPriority &= ~portPRIVILEGE_BIT; #endif /* portUSING_MPU_WRAPPERS == 1 */ /* 如果開啟選擇,則將stack初始化為固定值0xa5(可用於除錯). */ #if ( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 ) { ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) ); } #endif /* 根據stack生長的方向,來進行stack初始化. */ #if ( portSTACK_GROWTH < 0 ) { /* 如果stack向下生長,則棧頂在高地址 */ pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] ); /* 棧頂地址對齊,此操作在FreeRTOS Memory Management 有較為詳細的分析 */ pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /* 檢查對齊是否OK */ configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); /* 記錄棧空間最高位地址 */ #if ( configRECORD_STACK_HIGH_ADDRESS == 1 ) { pxNewTCB->pxEndOfStack = pxTopOfStack; } #endif } #else /* 如果stack向上生長,則棧頂在低地址 */ { pxTopOfStack = pxNewTCB->pxStack; /* 檢查對齊情況 */ configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); /* 記錄棧空間最高地址 */ pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); } #endif /* 儲存task name. */ if( pcName != NULL ) { for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) { pxNewTCB->pcTaskName[ x ] = pcName[ x ]; /* 不能直接複製configMAX_TASK_NAME_LEN,因為可能name的實際長度沒有這麼長,並且實際長度後的記憶體訪問情況不確定 */ if( pcName[ x ] == ( char ) 0x00 ) { break; } } /* 加上結束符'\0'. */ pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; } else { pxNewTCB->pcTaskName[ 0 ] = 0x00; } /* 優先順序值要在合理範圍內. */ if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) { uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; } /* 記錄優先順序相關的變數,可參考上一篇文章中對TCB結構的相關描述 */ pxNewTCB->uxPriority = uxPriority; #if ( configUSE_MUTEXES == 1 ) { pxNewTCB->uxBasePriority = uxPriority; pxNewTCB->uxMutexesHeld = 0; } #endif /* 初始化xStateListItem 和 xEventListItem(將item的pxContainer指標置為NULL) */ vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); /* 設定xStateListItem 的 owner 為當前TCB. */ listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); /* 設定 xEventListItem的值,設定為 configMAX_PRIORITIES - uxPriority. 優先順序高的(數值大的)在陣列前面。 */ listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /* 設定xEventListItem 的 owner 為當前TCB. */ listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); /* 將臨界區巢狀深度初始化為0 */ #if ( portCRITICAL_NESTING_IN_TCB == 1 ) { pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U; } #endif #if ( configUSE_APPLICATION_TASK_TAG == 1 ) { pxNewTCB->pxTaskTag = NULL; } #endif /* 執行計數初始化為0 */ #if ( configGENERATE_RUN_TIME_STATS == 1 ) { pxNewTCB->ulRunTimeCounter = 0UL; } #endif /* 如果開啟portUSING_MPU_WRAPPERS, 則儲存MPUSettings */ #if ( portUSING_MPU_WRAPPERS == 1 ) { vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth ); } #else { ( void ) xRegions; } #endif /* 將本地引數指標內容清空 */ #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) { memset( ( void * ) &( pxNewTCB->pvThreadLocalStoragePointers[ 0 ] ), 0x00, sizeof( pxNewTCB->pvThreadLocalStoragePointers ) ); } #endif /* 將任務通知相關的變數清空,後續會更詳細說明 */ #if ( configUSE_TASK_NOTIFICATIONS == 1 ) { memset( ( void * ) &( pxNewTCB->ulNotifiedValue[ 0 ] ), 0x00, sizeof( pxNewTCB->ulNotifiedValue ) ); memset( ( void * ) &( pxNewTCB->ucNotifyState[ 0 ] ), 0x00, sizeof( pxNewTCB->ucNotifyState ) ); } #endif /* newlib 相關初始化 */ #if ( configUSE_NEWLIB_REENTRANT == 1 ) { _REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) ); } #endif #if ( INCLUDE_xTaskAbortDelay == 1 ) { pxNewTCB->ucDelayAborted = pdFALSE; } #endif /* 為了方便描述,以下對原始碼進行一些簡化, * 假設 portUSING_MPU_WRAPPERS = 0 ,portSTACK_GROWTH = -1,portHAS_STACK_OVERFLOW_CHECKING = 1 * portHAS_STACK_OVERFLOW_CHECKING值標誌 port 是否具有stack overflow 檢查的功能 * pxPortInitialiseStack()功能根據port不同而異,一般是初始化棧空間中的一些暫存器值 */ pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters ); /* 如果任務建立成功,則通過輸入指標返回任務控制代碼,用於任務的其他操作(修改優先順序,刪除任務等) */ if( pxCreatedTask != NULL ) { *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; } }
1.2 prvAddNewTaskToReadyList()
static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB ) { /* 更新ready list時,需要保證不被打擾.因此使用taskENTER_CRITICAL() 和 taskEXIT_CRITICAL() 進行保護*/ taskENTER_CRITICAL(); { /* task 數量遞增 */ uxCurrentNumberOfTasks++; if( pxCurrentTCB == NULL ) { /* 如果 pxCurrentTCB == NULL,則意味著還沒有任務,或者所有任務都被掛起了. */ pxCurrentTCB = pxNewTCB; /* 如果任務數量為1,則需要進行初始化。因為排程器會建立一個idle task, * 因此即使有任務的增刪操作,uxCurrentNumberOfTasks的數值不會再次變為1,初始化操作也只會進行一次 */ if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) { /* 詳見本節(下面),關於 prvInitialiseTaskLists() 的分析 */ prvInitialiseTaskLists(); } } else { /* 如果排程器不在執行,新加入的優先順序更高,則將pxCurrentTCB的值設定為pxNewTCB。等排程器恢復後,則直接執行優先順序較高的task */ if( xSchedulerRunning == pdFALSE ) { if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority ) { pxCurrentTCB = pxNewTCB; } } } uxTaskNumber++; #if ( configUSE_TRACE_FACILITY == 1 ) { /* uxTCBNumber(TCB序號)賦值,用於跟蹤除錯. */ pxNewTCB->uxTCBNumber = uxTaskNumber; } #endif /* 詳見本節(後面)關於 prvAddTaskToReadyList()的分析*/ prvAddTaskToReadyList( pxNewTCB ); portSETUP_TCB( pxNewTCB );/* #define portSETUP_TCB( pxTCB ) ( void ) pxTCB */ } taskEXIT_CRITICAL(); if( xSchedulerRunning != pdFALSE ) { /* 如果排程器正在執行,並且新加入的task優先順序比當前執行的task優先順序高,則當前task退出,重新排程. */ if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority ) { taskYIELD_IF_USING_PREEMPTION(); } } } /*---------------------------------------------------------------------------------------------------------*/ /* 初始化任務列表 */ static void prvInitialiseTaskLists( void ) { UBaseType_t uxPriority; /* 按照優先順序,對pxReadyTasksLists陣列逐個進行初始化。 */ for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ ) { /* vListInitialise 函式詳見 < FreeRTOS Task Management(1)- list實現 > */ vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) ); } /* 初始化各個全域性的list */ vListInitialise( &xDelayedTaskList1 ); vListInitialise( &xDelayedTaskList2 ); vListInitialise( &xPendingReadyList ); #if ( INCLUDE_vTaskDelete == 1 ) { vListInitialise( &xTasksWaitingTermination ); } #endif #if ( INCLUDE_vTaskSuspend == 1 ) { vListInitialise( &xSuspendedTaskList ); } #endif /* 指標指向對應的list,用於taskDelay相關操作. */ pxDelayedTaskList = &xDelayedTaskList1; pxOverflowDelayedTaskList = &xDelayedTaskList2; } /*---------------------------------------------------------------------------------------------------------*/ /* 主要分兩步,一是記錄當前最高的優先順序(如果pxTCB的優先順序更高), * 二是將xStateListItem插入到對應優先順序的pxReadyTasksLists中,這樣排程器就可以進行排程 */ #define prvAddTaskToReadyList( pxTCB ) \ taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \ vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); /*---------------------------------------------------------------------------------------------------------*/
1.3 xTaskCreate()
/* xTaskCreate 是經常使用的一種 <動態> 建立任務的方式。僅在configSUPPORT_DYNAMIC_ALLOCATION == 1,才能進行呼叫 */
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
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;
/* 由於是動態分配,因此需要在建立任務時 分配 stack 和 TCB的空間。
* 根據portSTACK_GROWTH(棧生產方向)的不同,需要注意stack和tcb的分配順序,以確定stack生長不會影響到TCB。*/
/* 棧向高地址生長,則先分配TCB,再分配stack */
#if ( portSTACK_GROWTH > 0 )
{
/* pvPortMalloc詳細介紹見FreeRTOS Memory Management 系列部落格 */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
/* 如果TCB 和 stack 分配都OK了,則繼續進行初始化操作*/
if( pxNewTCB != NULL )
{
pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
if( pxNewTCB->pxStack == NULL )
{
vPortFree( pxNewTCB );
pxNewTCB = NULL;
}
}
}
#else /* 棧向低地址生長,則先分配stack,再分配TCB */
{
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
if( pxNewTCB != NULL )
{
#if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
{
/* stack 和 TCB 都是動態分配,在刪除任務時都需要進行free,詳見2.2節 */
pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
}
#endif
/* 初始化操作,見上述3.1節 和 3.2節的詳細分析 */
prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
prvAddNewTaskToReadyList( pxNewTCB );
xReturn = pdPASS;
}
else
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
return xReturn;
}
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
1.4 xTaskCreateStatic()
/* xTaskCreateStatic 函式的特點是靜態分配TCB 和stack */
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint32_t ulStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
StackType_t * const puxStackBuffer, /* 分配好的stack 空間 */
StaticTask_t * const pxTaskBuffer ) /* 分配好的TCB空間 */
{
TCB_t * pxNewTCB;
TaskHandle_t xReturn;
/* 進行一波檢查,以確定輸入的空間地址和大小是否正常 */
configASSERT( puxStackBuffer != NULL );
configASSERT( pxTaskBuffer != NULL );
#if ( configASSERT_DEFINED == 1 )
{
volatile size_t xSize = sizeof( StaticTask_t );
configASSERT( xSize == sizeof( TCB_t ) );
( void ) xSize;
}
#endif
/* 如果上述檢查沒有問題,則對pxNewTCB 和 pxNewTCB->pxStack進行賦值 */
if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) )
{
pxNewTCB = ( TCB_t * ) pxTaskBuffer;
pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer;
#if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
{
/* stack 和 TCB 都是靜態分配,在刪除任務時都不需要進行free,詳見2.2節 */
pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB;
}
#endif
/* 初始化操作,見上述3.1節 和 3.2節的詳細分析 */
prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL );
prvAddNewTaskToReadyList( pxNewTCB );
}
else
{
xReturn = NULL;
}
return xReturn;
}
#endif
1.5 xTaskCreateRestrictedStatic()
/* 僅在實現了MPU的系統上,才能呼叫此函式 */
/*
* typedef struct xTASK_PARAMETERS
* {
* TaskFunction_t pvTaskCode;
* const char * const pcName;
* configSTACK_DEPTH_TYPE usStackDepth;
* void * pvParameters;
* UBaseType_t uxPriority;
* StackType_t * puxStackBuffer;
* MemoryRegion_t xRegions[ portNUM_CONFIGURABLE_REGIONS ];
* #if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )
* StaticTask_t * const pxTaskBuffer;
* #endif
* } TaskParameters_t;
*/
#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )
/* pxTaskDefinition引數的定義如上,除了xTaskCreate函式的輸入引數之外,還包含了xRegions,用於prvInitialiseNewTask函式進行初始化。
* 函式實現與xTaskCreateStatic基本一致 */
BaseType_t xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition,
TaskHandle_t * pxCreatedTask ) /* 建立的task控制代碼 */
{
TCB_t * pxNewTCB;
BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
configASSERT( pxTaskDefinition->puxStackBuffer != NULL );
configASSERT( pxTaskDefinition->pxTaskBuffer != NULL );
if( ( pxTaskDefinition->puxStackBuffer != NULL ) && ( pxTaskDefinition->pxTaskBuffer != NULL ) )
{
pxNewTCB = ( TCB_t * ) pxTaskDefinition->pxTaskBuffer;
pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer;
#if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
{
/* stack 和 TCB 都是靜態分配,在刪除任務時都不需要進行free,詳見2.2節 */
pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB;
}
#endif
/* 初始化操作,見上述3.1節 和 3.2節的詳細分析 */
prvInitialiseNewTask( pxTaskDefinition->pvTaskCode,
pxTaskDefinition->pcName,
( uint32_t ) pxTaskDefinition->usStackDepth,
pxTaskDefinition->pvParameters,
pxTaskDefinition->uxPriority,
pxCreatedTask, pxNewTCB,
pxTaskDefinition->xRegions );/* 在portUSING_MPU_WRAPPERS == 0時 ,此引數輸入NULL */
prvAddNewTaskToReadyList( pxNewTCB );
xReturn = pdPASS;
}
return xReturn;
}
#endif
1.6 xTaskCreateRestricted()
#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition,
TaskHandle_t * pxCreatedTask )
{
TCB_t * pxNewTCB;
BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
configASSERT( pxTaskDefinition->puxStackBuffer );
if( pxTaskDefinition->puxStackBuffer != NULL )
{
/* 分配TCB. */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
if( pxNewTCB != NULL )
{
/* 從輸入引數中獲取stack空間 */
pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer;
#if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
{
/* stack 是靜態分配,在刪除任務時需要free TCB,詳見2.2節 */
pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_ONLY;
}
#endif
/* 初始化操作,見上述3.1節 和 3.2節的詳細分析 */
prvInitialiseNewTask( pxTaskDefinition->pvTaskCode,
pxTaskDefinition->pcName,
( uint32_t ) pxTaskDefinition->usStackDepth,
pxTaskDefinition->pvParameters,
pxTaskDefinition->uxPriority,
pxCreatedTask, pxNewTCB,
pxTaskDefinition->xRegions );/* 在portUSING_MPU_WRAPPERS == 0時 ,此引數輸入NULL */
prvAddNewTaskToReadyList( pxNewTCB );
xReturn = pdPASS;
}
}
return xReturn;
}
#endif
2 任務刪除
2.1 vTaskDelete()
#if ( INCLUDE_vTaskDelete == 1 )
/* 刪除任務主要是將TCB中包含的各種ListItem從list中移除,並釋放資源 */
void vTaskDelete( TaskHandle_t xTaskToDelete )
{
TCB_t * pxTCB;
/* 刪除過程要進行保護,防止中斷打斷 */
taskENTER_CRITICAL();
{
/* 獲取控制代碼對應的TCB. */
pxTCB = prvGetTCBFromHandle( xTaskToDelete );
/* 將task從 ready/delayed list中移除 */
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
}
/* 如果task在等待事件,則從事件列表中移除該task */
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
{
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
}
/* 需要注意,此值是一個靜態全域性變數,與TCB結構體中的uxTaskNumber不是一個東西。該值在任務數量發生變化時(建立或刪除)都會+1,
* 並以此提醒排程器,任務數量已經發生變化,需要重新生成任務列表 */
uxTaskNumber++;
/* 如果被刪除的是當前正在執行的Task,則需要idle task 去執行清理操作。因為task無法釋放自己的資源。
* 需要的做的是將任務新增到待刪除列表中 */
if( pxTCB == pxCurrentTCB )
{
/* 將需要刪除的task新增到刪除列表,等待idle task去做後續的清理工作,詳見2.4節 */
vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );
/* 待清除任務計數加1. */
++uxDeletedTasksWaitingCleanUp;
}
else /* 如果不是當前正在執行的Task,則直接進行清理即可 */
{
/* 當前task數量減1。需要指出的是,在pxTCB == pxCurrentTCB時,並沒有直接減1,
* 是因為在idle task 裡進行了減1 操作,詳見2.4節 */
--uxCurrentNumberOfTasks;
/* 見2.2 節詳細分析,主要工作是釋放記憶體 */
prvDeleteTCB( pxTCB );
/* 重置延時時間,詳見2.3節. */
prvResetNextTaskUnblockTime();
}
}
taskEXIT_CRITICAL();
/* 如果排程器在運作,並且當前的Task被刪除,則需要主動釋放執行權,排程器重新排程. */
if( xSchedulerRunning != pdFALSE )
{
if( pxTCB == pxCurrentTCB )
{
configASSERT( uxSchedulerSuspended == 0 );
portYIELD_WITHIN_API();
}
}
}
#endif
2.2 prvDeleteTCB()
static void prvDeleteTCB( TCB_t * pxTCB )
{
/* #define portCLEAN_UP_TCB( pxTCB ) ( void ) pxTCB */
portCLEAN_UP_TCB( pxTCB );
/* See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
* for additional information. */
#if ( configUSE_NEWLIB_REENTRANT == 1 )
{
_reclaim_reent( &( pxTCB->xNewLib_reent ) );
}
#endif
/* 參照1中的表格,以下這種搭配,TCB和stack都需要釋放 */
#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) )
{
vPortFree( pxTCB->pxStack );
vPortFree( pxTCB );
}
/* 僅在tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0時,才會記錄pxTCB->ucStaticallyAllocated的值 */
#elif ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
{
/* 1.3節中的xTaskCreate函式的記憶體分配方式是 tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB */
if( pxTCB->ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB )
{
/* TCB 和 stack都是動態分配的,因此都需要free */
vPortFree( pxTCB->pxStack );
vPortFree( pxTCB );
}
/* 1.4節中xTaskCreateRestrictedStatic函式和1.5節中xTaskCreateRestrictedStatic函式都是
* tskSTATICALLY_ALLOCATED_STACK_ONLY. */
else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY )
{
/* 僅有stack是靜態分配,因此TCB需要free */
vPortFree( pxTCB );
}
/*1.6節中的 xTaskCreateRestricted函式記憶體分配方式是tskSTATICALLY_ALLOCATED_STACK_AND_TCB */
else
{
/* TCB 和 stack都是靜態分配的,因此不需要free */
configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB );
}
}
#endif
}
2.3 prvResetNextTaskUnblockTime()
/* 此函式涉及到任務延時,此處簡單說明,會在後續的文章中詳細描述 task delay */
static void prvResetNextTaskUnblockTime( void )
{
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
{
/* 刪除task時,已經將其從delayed list 中刪除。如果此時list已經為空,則將xNextTaskUnblockTime 設定為最大值 */
xNextTaskUnblockTime = portMAX_DELAY;
}
else
{
/* 如果list不為空,則將xNextTaskUnblockTime的值設定為下一個即將執行的task的delay time. */
xNextTaskUnblockTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxDelayedTaskList );
}
}
2.4 prvCheckTasksWaitingTermination()
static void prvCheckTasksWaitingTermination( void )
{
/** THIS FUNCTION IS CALLED FROM THE RTOS IDLE TASK **/
TCB_t * pxTCB;
/* 當有任務需要清除時,就逐個進行清除 */
while( uxDeletedTasksWaitingCleanUp > ( UBaseType_t ) 0U )
{
taskENTER_CRITICAL();
{
pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) );
/* 執行清理工作 */
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
--uxCurrentNumberOfTasks;
--uxDeletedTasksWaitingCleanUp;
}
taskEXIT_CRITICAL();
/* 詳見2.2節 */
prvDeleteTCB( pxTCB );
}
}