本節描述任務相關的控制。
主要講解使用,原始碼分析後面對應章節會有。
學習本節前,建議同學們往前回憶下任務控制塊的內容。
參考:
任務控制主要是對任務控制塊的處理。
比如任務延時、重置任務優先順序、任務掛起與恢復。
對於延時相關的程式碼細節,可以參考前面的【freertos】007-系統節拍和系統延時管理實現細節章節詳細分析。
void vTaskDelay( portTickTypexTicksToDelay );
vTaskDelay()
用於相對延時,是指每次延時都是從任務執行函數vTaskDelay()
開始,延時指定的時間結束。xTicksToDelay
引數用於設定延遲的時鐘節拍個數。#define portMAX_DELAY (TickType_t )0xffffffffUL
static void lzmTestTask(void* parameter)
{
/* task init */
printf("start lzmTestTask\r\n");
for(;;)
{
/* 任務主體 */
/* 延時1000個tick再跑 */
vTaskDelay(1000);
}
}
該功能可用於週期性任務,保證執行頻率不變。
BaseType_t vTaskDelayUntil( TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement );
vTaskDelayUntil()
用於絕對延時,也叫週期性延時。想象下精度不高的定時器。pxPreviousWakeTime
引數是儲存任務上次處於非阻塞狀態時刻的變數地址。xTimeIncrement
引數用於設定週期性延時的時鐘節拍個數。pdFALSE
說明延時失敗。#defineINCLUDE_vTaskDelayUntil 1
static void lzmTestTask(void* parameter)
{
portTickType last_wake_time = 0;
/* task init */
printf("start lzmTestTask\r\n");
/* 重置下該變數 */
last_wake_time = xTaskGetTickCount();
for(;;)
{
/* 再確保任務主體佔用CPU時長不會超過週期值(1000tick)的情況下,
不管任務主體跑多長時間,1000tick後依然內跑回這裡。 */
vTaskDelayUntil(&last_wake_time, 1000);
/* 任務主體 */
}
}
UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask );
xTask
引數為任務控制程式碼。傳入NULL,表示獲取當前呼叫該API的任務的優先順序。INCLUDE_vTaskPriorityGet
為1。UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask )
{
TCB_t const * pxTCB;
UBaseType_t uxReturn;
taskENTER_CRITICAL(); /* 加入臨界 */
{
/* 獲取任務控制塊 */
pxTCB = prvGetTCBFromHandle( xTask );
/* 通過任務控制塊獲取任務優先順序 */
uxReturn = pxTCB->uxPriority;
}
taskEXIT_CRITICAL(); /* 退出臨界 */
return uxReturn;
}
void vAFunction( void )
{
TaskHandle_t xHandle;
/* 建立一個任務,儲存該控制程式碼 */
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
// ...
/* 使用控制程式碼獲取建立的任務的優先順序 */
if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY )
{
/* 任務可以改變自己的優先順序 */
}
// ...
/* 當前任務優先順序比建立的任務優先順序高? */
if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) )
{
/* 當前優先順序較高 */
}
}
任務優先順序除了在建立時設定外,也可以在系統啟動後重置,畢竟任務優先順序的本質也只是任務控制塊裡面的一直成員值。
但是修改優先順序時需要維護優先順序繼承機制。
void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority );
作用:
引數:
xTask
:需要修改任務優先順序的任務控制程式碼。NULL時,表示修改當前任務的任務優先順序。uxNewPriority
:新的任務優先順序。在[0,configMAX_PRIORITIES - 1]範圍內,否則會引起斷言。使能方法:使用該功能需要在FreeRTOSConfig.h中設定INCLUDE_vTaskPrioritySet
為1。
更改任務優先順序的實現,是更改任務控制塊裡面記錄的任務優先順序值,但是需要維護好優先順序繼承機制。
看到了原始碼,產生兩個疑問:
重置優先順序簡要步驟:
傳入的優先順序必須小於限制值,否則會觸發斷言。
/* 斷言式引數校驗 */
configASSERT( uxNewPriority < configMAX_PRIORITIES );
/* 引數糾正 */
if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
{
uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
}
重置任務優先順序,涉及到就緒連結串列、事件連結串列的操作,而系統時鐘節拍這些中斷會設計到操作這些連結串列。
通過任務控制程式碼獲取任務控制塊,通過任務控制塊獲取任務優先順序。
如果使能了互斥量,及系統支援優先順序繼承機制時,需要區分基優先順序uxBasePriority
和在用優先順序uxPriority
。
/* 獲取任務控制塊 */
pxTCB = prvGetTCBFromHandle( xTask );
#if ( configUSE_MUTEXES == 1 )
{
/* 開啟了互斥量就獲取基優先順序,處理優先順序繼承使用 */
uxCurrentBasePriority = pxTCB->uxBasePriority;
}
#else
{
/* 沒有開啟互斥量功能就直接獲取優先順序 */
uxCurrentBasePriority = pxTCB->uxPriority;
}
#endif
在修改任務優先順序前,先檢查修改後是否需要進行任務排程,以下情況都需要任務排程:
實現程式碼如下:
/* 檢查是否需要標記任務排程 */
if( uxNewPriority > uxCurrentBasePriority ) /* 新的優先順序比基優先順序更高了 */
{
if( pxTCB != pxCurrentTCB )
{
/* 如果需要修改的任務不是當前在跑任務,且新設定的優先順序大於當前在跑的任務優先順序,需要標記任務排程 */
if( uxNewPriority >= pxCurrentTCB->uxPriority )
{
/* 標記任務排程 */
xYieldRequired = pdTRUE;
}
}
else
{
/* 如果被提高優先順序的任務已經在跑了,就不需要工作切換 */
}
}
else if( pxTCB == pxCurrentTCB ) /* 把當前任務優先順序下調,也需要觸發任務排程 */
{
/* 標記任務排程 */
xYieldRequired = pdTRUE;
}
else
{
/* 下調其它任務優先順序,不需要排程 */
}
在更新任務優先順序前,需要儲存該任務在用優先順序,等等用於遷移就緒連結串列。
/* 獲取該任務當前使用的優先順序 */
uxPriorityUsedOnEntry = pxTCB->uxPriority;
如果開啟了互斥量功能,檢查該任務是否處於優先順序繼承狀態:
如果是,則不更新該任務在用優先順序值。
如果不是,則需要更新該任務在用優先順序值。
#if ( configUSE_MUTEXES == 1 )
{
/* 開啟了互斥量功能,但是當前沒有在優先順序繼承狀態,可以更新當前任務在用優先順序 */
if( pxTCB->uxBasePriority == pxTCB->uxPriority )
{
pxTCB->uxPriority = uxNewPriority;
}
/* 更新基優先順序 */
pxTCB->uxBasePriority = uxNewPriority;
}
#else /* if ( configUSE_MUTEXES == 1 ) */
{
/* 沒有開啟互斥量功能就直接更新當前在用優先順序 */
pxTCB->uxPriority = uxNewPriority;
}
#endif /* if ( configUSE_MUTEXES == 1 ) */
按照作者的想法,任務優先順序會影響到該任務在事件連結串列中的位置,所以也需要對事件連結串列處理。
由於事件連結串列節點值按功能裝載不同的值:
所以修改該值前先判斷當前是否裝載任務優先順序。
/* 當前事件連結串列節點值是否被鎖定。參考freertos事件組元件 */
if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
{
/* 時間連結串列節點值沒有被鎖定,則預設用於儲存任務優先順序,用於事件連結串列排序。可更新事件連結串列節點值。 */
listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) );
}
當前freertos官方提供的修改任務優先順序API內事件連結串列處理程式碼就這。
按照作者的想法,如果更新了任務優先順序到事件節點值。
也應該檢查下當前任務是否阻塞在有序事件連結串列中,如訊息佇列,這些都是按照優先順序插入事件連結串列的,解除阻塞是取應該排序在前的任務的。
如果被修改任務優先順序的任務在就緒連結串列,需要遷移到新的優先順序就緒連結串列中。
該任務如果處於就緒態,會存在在用優先順序的就緒連結串列中,而不是基優先順序的就緒連結串列。
遷移就緒連結串列時需要注意,如果遷出就緒連結串列後,該連結串列沒有就緒任務了,需要對系統任務優先順序點陣圖值uxTopReadyPriority
進行更新處理。
uxTopReadyPriority
該值是一個點陣圖值。uxTopReadyPriority
該值就是系統就緒任務中最高優先順序的值。所以實現程式碼如下:
/* 判斷被調節優先順序的任務是否處於就緒態,如果是,需要遷移到新的優先順序的就緒連結串列。 */
if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE )
{
/* 解除任務所有狀態,即是遷出當前就緒連結串列。 */
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
/* 如果當前就緒連結串列沒有其它任務了,遷出就緒任務優先順序點陣圖值對應位。 */
portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority );
}
/* 根據新的優先順序重新插入就緒連結串列 */
prvAddTaskToReadyList( pxTCB );
}
所有功能都實現後,觸發任務排程,退出臨界後,便可進入排程異常的回撥進行任務排程。
void vAFunction( void )
{
TaskHandle_t xHandle;
/* 建立一個任務,儲存該控制程式碼 */
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
// ...
/* 使用控制程式碼重置建立任務的優先順序 */
vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 );
// ...
/* 傳入null,重置當前任務優先順序 */
vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 );
}
void vTaskSuspend( TaskHandle_t xTaskToSuspend );
引數:xTaskToSuspend
:需要掛起的任務的任務控制程式碼。為NULL時,掛起當前任務。
使能方法:在FreeRTOSConfig.h中設定INCLUDE_vTaskSuspend
為1。
作用:掛起一個任務。任務掛起後,插入到就掛起連結串列中,該任務不會被排程,也無權佔用CPU。
配對使用API:呼叫vTaskResume()
恢復被掛起的任務到就緒連結串列。
掛起任務的處理設計到任務狀態連結串列和任務解除阻塞時間這些全域性資料,而這些資料在滴答時鐘或者其它中斷回撥中使用的字尾FromISR API中也可能用到。
所以為了維護這些資料的原子性,需要使用臨界級別來實現。
進出臨界使用的函數:
/* 進入臨界 */
taskENTER_CRITICAL()
/* 退出臨界 */
taskEXIT_CRITICAL()
/* 獲取需要掛起的任務控制程式碼。傳入NULL,即獲取當前任務的控制程式碼。 */
pxTCB = prvGetTCBFromHandle( xTaskToSuspend );
切換任務狀態不是設定某個任務狀態值,而是把任務按規則放到各種狀態連結串列。
/* 解除任務所有狀態。即是把任務從狀態連結串列中遷出。 */
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
/* 移出後如果當前優先順序的就緒連結串列沒有其它任務了,就需要重置下點陣圖示。(開啟了優先順序優化功能才會生效) */
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
}
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
{
/* 如果存在事件,需要從事件中移除。 */
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
}
/* 把任務插入到掛起連結串列 */
vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
#if ( configUSE_TASK_NOTIFICATIONS == 1 ) /* 任務通知功能 */
{
BaseType_t x;
for( x = 0; x < configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ )
{
if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION )
{
/* 如果任務正在等待任務通知,則當任務被掛起時,需要清除這些任務通知。 */
pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION;
}
}
}
#endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */
完成以上四小步才算是把任務從其它狀態切入到掛起態(是掛起任務的掛起態)。
為了防止掛起的任務是下一個需要解除阻塞的任務而導致系統提前進入檢索解除阻塞任務的多餘操作,這裡可以重新整理下解除阻塞任務的時間。
if( xSchedulerRunning != pdFALSE ) /* 排程器已經啟動了 */
{
taskENTER_CRITICAL();
{
/* 如果排程器已經開啟了,需要更新下一個需要解除任務阻塞的時間 */
prvResetNextTaskUnblockTime();
}
taskEXIT_CRITICAL();
}
如果掛起的任務是當前任務,那需要更新下pxCurrentTCB
值。
如果排程器已經啟動了,掛起當前任務後,需要強制觸發任務排程。
如果排程器還沒有啟動,掛起了當前任務,就需要更新pxCurrentTCB
值即可。等待排程器啟動後先跑pxCurrentTCB
。
如果全部任務都被掛起了,就設定pxCurrentTCB
為空即可。下次建立任務或者恢復任務時會重置pxCurrentTCB
。至少會在啟動排程器時會建立空閒任務,所以在啟動排程器前不必在乎pxCurrentTCB
值是否為空。
如果不是全部任務都被掛起,那就從就緒表中選出最合適的任務到pxCurrentTCB
。
vTaskSwitchContext()
,該任務的分析可以往前面的工作切換章節翻。if( pxTCB == pxCurrentTCB ) /* 掛起當前任務 */
{
if( xSchedulerRunning != pdFALSE )
{
/* 排程器正常執行,需要強制觸發任務排程,把任務切走 */
configASSERT( uxSchedulerSuspended == 0 );
portYIELD_WITHIN_API();
}
else
{
if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */
{
/* 如果所有任務都被掛起了,就把pxCurrentTCB值標記為空 */
pxCurrentTCB = NULL;
}
else
{
/* 找出新的pxCurrentTCB值 */
vTaskSwitchContext();
}
}
}
void vTaskResume( TaskHandle_t xTaskToResume );
xTaskToResume
:需要解除掛起的任務控制程式碼。
INCLUDE_vTaskSuspend
必須定義為vTaskSuspend() 1,這個函數才生效。
該函數用於解除掛起的任務。
被一個或多個vTaskSuspend()
呼叫掛起的任務將通過對vTaskResume()
的單個呼叫重新可用。
解除任務的掛起態的實現比較簡單,主要思路:
vTaskResume()
:
#if ( INCLUDE_vTaskSuspend == 1 ) /* 使能 */
void vTaskResume( TaskHandle_t xTaskToResume )
{
TCB_t * const pxTCB = xTaskToResume;
/* 任務控制程式碼不能為NULL */
configASSERT( xTaskToResume );
/* 正在跑的任務在執行態,不用處理。 */
if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) )
{
taskENTER_CRITICAL(); /* 進入臨界。因為下面操作涉及任務狀態表等系統相關的全域性變數。 */
{
if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) /* 如果該任務處於掛起態 */
{
/* 從掛起連結串列遷出 */
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
/* 重新插入到就緒連結串列 */
prvAddTaskToReadyList( pxTCB );
/* 如果恢復的任務的優先順序更高,就觸發任務排程。 */
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
{
/* 觸發任務排程 */ taskYIELD_IF_USING_PREEMPTION();
}
}
}
taskEXIT_CRITICAL(); /* 退出臨界 */
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* INCLUDE_vTaskSuspend */
prvTaskIsTaskSuspended()
:
#if ( INCLUDE_vTaskSuspend == 1 ) /* 使能 */
static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask )
{
BaseType_t xReturn = pdFALSE;
const TCB_t * const pxTCB = xTask;
/* 存取xPendingReadyList,因此必須從臨界區呼叫。所以需要在呼叫本函數前進入。 */
/* 檢查在跑任務是否掛起是沒有意義的 */
configASSERT( xTask );
/* 檢查該任務的狀態 */
if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ) != pdFALSE ) /* 該任務掛載在掛起連結串列 */
{
if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) == pdFALSE )/* 該任務不是因為排程器掛起而暫時放到掛起連結串列的 */
{
/* 再判斷該任務是否是因為等待事件而永久阻塞的,如果是,也不屬於掛起態。 */
if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) != pdFALSE )
{
xReturn = pdTRUE;
}
}
}
return xReturn;
}
#endif /* INCLUDE_vTaskSuspend */
void vAFunction( void )
{
TaskHandle_t xHandle;
/* 建立一個任務,儲存該控制程式碼 */
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
// ...
/* 掛起這個剛建立的任務 */
vTaskSuspend( xHandle );
// ...
/* 掛起當前在跑任務 */
vTaskSuspend( NULL );
/* 在被其它任務恢復當前任務前,是不會跑到這裡的 */
}
void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority )
{
TCB_t * pxTCB;
UBaseType_t uxCurrentBasePriority, uxPriorityUsedOnEntry;
BaseType_t xYieldRequired = pdFALSE;
/* 斷言式引數校驗 */
configASSERT( uxNewPriority < configMAX_PRIORITIES );
/* 引數糾正 */
if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
{
uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 進入臨界處理 */
taskENTER_CRITICAL();
{
/* 獲取任務控制塊 */
pxTCB = prvGetTCBFromHandle( xTask );
traceTASK_PRIORITY_SET( pxTCB, uxNewPriority );
#if ( configUSE_MUTEXES == 1 )
{
/* 開啟了互斥量就獲取基優先順序,處理優先順序繼承使用 */
uxCurrentBasePriority = pxTCB->uxBasePriority;
}
#else
{
/* 沒有開啟互斥量功能就直接獲取優先順序 */
uxCurrentBasePriority = pxTCB->uxPriority;
}
#endif
/* 新設定的優先順序和原有的優先順序不一樣才會處理 */
if( uxCurrentBasePriority != uxNewPriority )
{
/* 檢查是否需要標記任務排程 */
if( uxNewPriority > uxCurrentBasePriority ) /* 新的優先順序比基優先順序更高了 */
{
if( pxTCB != pxCurrentTCB )
{
/* 如果需要修改的任務不是當前在跑任務,且新設定的優先順序大於當前在跑的任務優先順序,需要標記任務排程 */
if( uxNewPriority >= pxCurrentTCB->uxPriority )
{
/* 標記任務排程 */
xYieldRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
/* 如果被提高優先順序的任務已經在跑了,就不需要工作切換 */
}
}
else if( pxTCB == pxCurrentTCB ) /* 把當前任務優先順序下調,也需要觸發任務排程 */
{
/* 標記任務排程 */
xYieldRequired = pdTRUE;
}
else
{
/* 下調其它任務優先順序,不需要排程 */
}
/* 記錄該任務當前使用的優先順序 */
uxPriorityUsedOnEntry = pxTCB->uxPriority;
#if ( configUSE_MUTEXES == 1 )
{
/* 開啟了互斥量功能,但是當前沒有在優先順序繼承狀態,可以更新當前任務在用優先順序 */
if( pxTCB->uxBasePriority == pxTCB->uxPriority )
{
pxTCB->uxPriority = uxNewPriority;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 更新基優先順序 */
pxTCB->uxBasePriority = uxNewPriority;
}
#else /* if ( configUSE_MUTEXES == 1 ) */
{
/* 沒有開啟互斥量功能就直接更新當前在用優先順序 */
pxTCB->uxPriority = uxNewPriority;
}
#endif /* if ( configUSE_MUTEXES == 1 ) */
/* 當前事件連結串列節點值是否被鎖定。參考freertos事件組元件 */
if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
{
/* 時間連結串列節點值沒有被鎖定,則預設用於儲存任務優先順序,用於事件連結串列排序。可更新事件連結串列節點值。 */
listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 判斷被調節優先順序的任務是否處於就緒態,如果是,需要遷移到新的優先順序的就緒連結串列。 */
if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE )
{
/* 解除任務所有狀態,即是遷出當前就緒連結串列。 */
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
/* 如果當前就緒連結串列沒有其它任務了,遷出就緒任務優先順序點陣圖值對應位。 */
portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 根據新的優先順序重新插入就緒連結串列 */
prvAddTaskToReadyList( pxTCB );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( xYieldRequired != pdFALSE )
{
/* 觸發任務排程 */
taskYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 編譯警告處理 */
( void ) uxPriorityUsedOnEntry;
}
}
/* 退出臨界 */
taskEXIT_CRITICAL();
}
#if ( INCLUDE_vTaskSuspend == 1 )
void vTaskSuspend( TaskHandle_t xTaskToSuspend )
{
TCB_t * pxTCB;
taskENTER_CRITICAL();
{
/* 獲取需要掛起的任務控制程式碼。傳入NULL,即獲取當前任務的控制程式碼。 */
pxTCB = prvGetTCBFromHandle( xTaskToSuspend );
traceTASK_SUSPEND( pxTCB );
/* 解除任務所有狀態。即是把任務從狀態連結串列中遷出。 */
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();
}
/* 把任務插入到掛起連結串列 */
vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
#if ( configUSE_TASK_NOTIFICATIONS == 1 ) /* 任務通知功能 */
{
BaseType_t x;
for( x = 0; x < configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ )
{
if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION )
{
/* 如果任務正在等待任務通知,則當任務被掛起時,需要清除這些任務通知。 */
pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION;
}
}
}
#endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */
}
taskEXIT_CRITICAL();
if( xSchedulerRunning != pdFALSE )
{
taskENTER_CRITICAL();
{
/* 如果排程器已經開啟了,需要更新下一個需要解除任務阻塞的時間 */
prvResetNextTaskUnblockTime();
}
taskEXIT_CRITICAL();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( pxTCB == pxCurrentTCB ) /* 掛起當前任務 */
{
if( xSchedulerRunning != pdFALSE )
{
/* 排程器正常執行,需要強制觸發任務排程,把任務切走 */
configASSERT( uxSchedulerSuspended == 0 );
portYIELD_WITHIN_API();
}
else
{
if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */
{
/* 如果所有任務都被掛起了,就把pxCurrentTCB值標記為空 */
pxCurrentTCB = NULL;
}
else
{
/* 找出新的pxCurrentTCB值 */
vTaskSwitchContext();
}
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* INCLUDE_vTaskSuspend */