<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在FreeRTOS基礎系列 FreeRTOS任務建立和刪除中介紹了任務建立API函數xTaskCreate(),我們這裡先回顧一下這個函數的宣告:
BaseType_t xTaskCreate( TaskFunction_tp vTaskCode, const char * constpcName, unsigned short usStackDepth, void *pvParameters, UBaseType_t uxPriority, TaskHandle_t *pvCreatedTask );
這個API函數的作用是建立新的任務並將它加入到任務就緒列表,函數引數含義為:
pvTaskCode
:函數指標,指向任務函數的入口。任務永遠不會返回(位於死迴圈內)。該引數型別TaskFunction_t定義在檔案projdefs.h中,定義為:typedef void(*TaskFunction_t)( void * ),即引數為空指標型別並返回空型別。
pcName
:任務描述。主要用於偵錯。字串的最大長度(包括字串結束字元)由宏configMAX_TASK_NAME_LEN指定,該宏位於FreeRTOSConfig.h檔案中。
usStackDepth
:指定任務堆疊大小,能夠支援的堆疊變數數量(堆疊深度),而不是位元組數。
比如,在16位元寬度的堆疊下,usStackDepth定義為100,則實際使用200位元組堆疊儲存空間。堆疊的寬度乘以深度必須不超過size_t型別所能表示的最大值。
比如,size_t為16位元,則可以表示堆疊的最大值是65535位元組。這是因為堆疊在申請時是以位元組為單位的,申請的位元組數就是堆疊寬度乘以深度,如果這個乘積超出size_t所表示的範圍,就會溢位,分配的堆疊空間也不是我們想要的。
pvParameters
:指標,當任務建立時,作為一個引數傳遞給任務。
uxPriority
:任務的優先順序。具有MPU支援的系統,可以通過置位優先順序引數的portPRIVILEGE_BIT位,隨意的在特權(系統)模式下建立任務。
比如,建立一個優先順序為2的特權任務,引數uxPriority可以設定為 ( 2 | portPRIVILEGE_BIT )。
pvCreatedTask
:用於回傳一個控制程式碼(ID),建立任務後可以使用這個控制程式碼參照任務。
雖然xTaskCreate()看上去很像函數,但其實是一個宏,真正被呼叫的函數是xTaskGenericCreate(),xTaskCreate()宏定義如下所示:
#define xTaskCreate( pvTaskCode, pcName, usStackDepth,pvParameters, uxPriority, pxCreatedTask ) xTaskGenericCreate( ( pvTaskCode ),( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask), ( NULL ), ( NULL ), ( NULL ) )
可以看到,xTaskCreate比xTaskGenericCreate少了三個引數,在宏定義中,這三個引數被設定為NULL。這三個引數用於使用靜態變數的方法分配堆疊、任務TCB空間以及設定MPU相關的引數。
一般情況下,這三個引數是不使用的,所以任務建立宏xTaskCreate定義的時候,將這三個引數對使用者隱藏了。接下來的章節中,為了方便,我們還是稱xTaskCreate()為函數,雖然它是一個宏定義。
上面我們提到了任務TCB(任務控制塊),這是一個需要重點介紹的關鍵點。它用於儲存任務的狀態資訊,包括任務執行時的環境。每個任務都有自己的任務TCB。
任務TCB是一個相對比較大的資料結構,這也是情理之中的,因為與任務相關的程式碼佔到整個FreeRTOS程式碼量的一半左右,這些程式碼大都與任務TCB相關,我們先來介紹一下任務TCB資料結構的定義:
typedef struct tskTaskControlBlock { volatile StackType_t *pxTopOfStack; /*當前堆疊的棧頂,必須位於結構體的第一項*/ #if ( portUSING_MPU_WRAPPERS == 1 ) xMPU_SETTINGS xMPUSettings; /*MPU設定,必須位於結構體的第二項*/ #endif ListItem_t xStateListItem; /*任務的狀態列表項,以參照的方式表示任務的狀態*/ ListItem_t xEventListItem; /*事件列表項,用於將任務以參照的方式掛接到事件列表*/ UBaseType_t uxPriority; /*儲存任務優先順序,0表示最低優先順序*/ StackType_t *pxStack; /*指向堆疊的起始位置*/ char pcTaskName[ configMAX_TASK_NAME_LEN ];/*任務名字*/ #if ( portSTACK_GROWTH > 0 ) StackType_t *pxEndOfStack; /*指向堆疊的尾部*/ #endif #if ( portCRITICAL_NESTING_IN_TCB == 1 ) UBaseType_t uxCriticalNesting; /*儲存臨界區巢狀深度*/ #endif #if ( configUSE_TRACE_FACILITY == 1 ) UBaseType_t uxTCBNumber; /*儲存一個數值,每個任務都有唯一的值*/ UBaseType_t uxTaskNumber; /*儲存一個特定數值*/ #endif #if ( configUSE_MUTEXES == 1 ) UBaseType_t uxBasePriority; /*儲存任務的基礎優先順序*/ UBaseType_t uxMutexesHeld; #endif #if ( configUSE_APPLICATION_TASK_TAG == 1 ) TaskHookFunction_t pxTaskTag; #endif #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) void *pvThreadLocalStoragePointers[configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; #endif #if( configGENERATE_RUN_TIME_STATS == 1 ) uint32_t ulRunTimeCounter; /*記錄任務在執行狀態下執行的總時間*/ #endif #if ( configUSE_NEWLIB_REENTRANT == 1 ) /* 為任務分配一個Newlibreent結構體變數。Newlib是一個C庫函數,並非FreeRTOS維護,FreeRTOS也不對使用結果負責。如果使用者使用Newlib,必須熟知Newlib的細節*/ struct _reent xNewLib_reent; #endif #if( configUSE_TASK_NOTIFICATIONS == 1 ) volatile uint32_t ulNotifiedValue; /*與任務通知相關*/ volatile uint8_t ucNotifyState; #endif #if( configSUPPORT_STATIC_ALLOCATION == 1 ) uint8_t ucStaticAllocationFlags; /* 如果堆疊由靜態陣列分配,則設定為pdTRUE,如果堆疊是動態分配的,則設定為pdFALSE*/ #endif #if( INCLUDE_xTaskAbortDelay == 1 ) uint8_t ucDelayAborted; #endif } tskTCB; typedef tskTCB TCB_t;
下面我們詳細的介紹這個資料結構的主要成員:
指標pxTopOfStack必須位於結構體的第一項,指向當前堆疊的棧頂,對於向下增長的堆疊,pxTopOfStack總是指向最後一個入棧的專案。
如果使用MPU,xMPUSettings必須位於結構體的第二項,用於MPU設定。
接下來是狀態列表項xStateListItem和事件列表項xEventListItem,我們在上一章介紹列表和列表項的文章中提到過:列表被FreeRTOS排程器使用,用於跟蹤任務,處於就緒、掛起、延時的任務,都會被掛接到各自的列表中。
排程器就是通過把任務TCB中的狀態列表項xStateListItem和事件列表項xEventListItem掛接到不同的列表中來實現上述過程的。
在task.c中,定義了一些靜態列表變數,其中有就緒、阻塞、掛起列表,例如當某個任務處於就緒態時,排程器就將這個任務TCB的xStateListItem列表項掛接到就緒列表。
事件列表項也與之類似,當佇列滿的情況下,任務因入隊操作而阻塞時,就會將事件列表項掛接到佇列的等待入佇列表上。
uxPriority用於儲存任務的優先順序,0為最低優先順序。任務建立時,指定的任務優先順序就被儲存到該變數中。
指標pxStack指向堆疊的起始位置,任務建立時會分配指定數目的任務堆疊,申請堆疊記憶體函數返回的指標就被賦給該變數。
很多剛接觸FreeRTOS的人會分不清指標pxTopOfStack和pxStack的區別,這裡簡單說一下:
pxTopOfStack指向當前堆疊棧頂,隨著進棧出棧,pxTopOfStack指向的位置是會變化的;pxStack指向當前堆疊的起始位置,一經分配後,堆疊起始位置就固定了,不會被改變了。
那麼為什麼需要pxStack變數呢,這是因為隨著任務的執行,堆疊可能會溢位,在堆疊向下增長的系統中,這個變數可用於檢查堆疊是否溢位;
如果在堆疊向上增長的系統中,要想確定堆疊是否溢位,還需要另外一個變數pxEndOfStack來輔助診斷是否堆疊溢位,後面會講到這個變數。
字元陣列pcTaskName用於儲存任務的描述或名字,在任務建立時,由引數指定。
名字的長度由宏configMAX_TASK_NAME_LEN(位於FreeRTOSConfig.h中)指定,包含字串結束標誌。
如果堆疊向上生長(portSTACK_GROWTH > 0),指標pxEndOfStack指向堆疊尾部,用於檢驗堆疊是否溢位。
變數uxCriticalNesting用於儲存臨界區巢狀深度,初始值為0。
接下來兩個變數用於視覺化追蹤,僅當宏configUSE_TRACE_FACILITY(位於FreeRTOSConfig.h中)為1時有效。變數uxTCBNumber儲存一個數值,在建立任務時由核心自動分配數值(通常每建立一個任務,值增加1),每個任務的uxTCBNumber值都不同,主要用於偵錯。
變數uxTaskNumber用於儲存一個特定值,與變數uxTCBNumber不同,uxTaskNumber的數值不是由核心分配的,而是通過API函數vTaskSetTaskNumber()來設定的,數值由函數引數指定。
如果使用互斥量(configUSE_MUTEXES == 1),任務優先順序被臨時提高時,變數uxBasePriority用來儲存任務原來的優先順序。
變數ucStaticAllocationFlags也需要說明一下,我們前面說過任務建立API函數xTaskCreate()只能使用動態記憶體分配的方式建立任務堆疊和任務TCB,如果要使用靜態變數實現任務堆疊和任務TCB就需要使用函數xTaskGenericCreate()來實現。
如果任務堆疊或任務TCB由靜態陣列和靜態變數實現,則將該變數設定為pdTRUE(任務堆疊空間由靜態陣列變數實現時為0x01,任務TCB由靜態變數實現時為0x02,任務堆疊和任務TCB都由靜態變數實現時為0x03),如果堆疊是動態分配的,則將該變數設定為pdFALSE。
到這裡任務TCB的資料結構就講完了,下面我們用一個例子來講述任務建立的過程,為方便起見,假設被建立的任務叫“任務A”,任務函數為vTask_A():
TaskHandle_t xHandle; xTaskCreate(vTask_A,」Task A」,120,NULL,1,&xHandle);
這裡建立了一個任務,任務優先順序為1,由於硬體平臺是32為架構,所以指定了120*4=480位元組的任務堆疊,向任務函數vTask_A()傳遞的引數為空(NULL),任務控制程式碼由變數xHandle儲存。
當這個語句執行後,任務A被建立並加入就緒任務列表,我們這章的主要目的,就是看看這個語句在執行過程中,發生了什麼事情。
呼叫函數prvAllocateTCBAndStack()建立任務堆疊和任務TCB。有兩種方式建立任務堆疊和任務TCB,一種是使用動態記憶體分配方法,這樣當任務刪除時,任務堆疊和任務控制塊空間會被釋放,可用於其它任務;
另一種是使用靜態變數來實現,在建立任務前定義好全域性或者靜態堆疊陣列和任務控制塊變數,在呼叫建立任務API函數時,將這兩個變數以引數的形式傳遞給任務建立函數xTaskGenericCreate()。
如果使用預設的xTaskCreate()建立任務函數,則使用動態記憶體分配,因為與靜態記憶體分配有關的引數不可見(在本文一開始我們說過xTaskCreate()其實是一個帶引數的宏定義,真正被執行的函數是xTaskGenericCreate(),參考宏xTaskCreate()的定義可以知道,xTaskCreate()對外隱藏了使用靜態記憶體分配的引數,在呼叫xTaskGenericCreate()時,這些引數被設定為NULL)。
任務堆疊成功分配後,經過對齊的堆疊起始地址被儲存到任務TCB的pxStack欄位。
如果使能堆疊溢位檢查或者使用視覺化追蹤功能,則使用固定值tskSTACK_FILL_BYTE(0xa5)填充堆疊。
函數prvAllocateTCBAndStack()的原始碼去除斷言和不常用的條件編譯後如下所示:
static TCB_t *prvAllocateTCBAndStack( const uint16_t usStackDepth, StackType_t * const puxStackBuffer, TCB_t * const pxTaskBuffer ) { TCB_t *pxNewTCB; StackType_t *pxStack; /* 分配堆疊空間*/ pxStack = ( StackType_t * ) pvPortMallocAligned( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ), puxStackBuffer ); if( pxStack != NULL ) { /* 分配TCB空間 */ pxNewTCB = ( TCB_t * ) pvPortMallocAligned( sizeof( TCB_t ), pxTaskBuffer ); if( pxNewTCB != NULL ) { /* 將堆疊起始位置存入TCB*/ pxNewTCB->pxStack = pxStack; } else { /* 如果TCB分配失敗,釋放之前申請的堆疊空間 */ if( puxStackBuffer == NULL ) { vPortFree( pxStack ); } } } else { pxNewTCB = NULL; } if( pxNewTCB != NULL ) { /* 如果需要,使用固定值填充堆疊 */ #if( ( configCHECK_FOR_STACK_OVERFLOW> 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark== 1 ) ) { /* 僅用於偵錯 */ ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) usStackDepth * sizeof( StackType_t ) ); } #endif } return pxNewTCB; }
呼叫函數prvInitialiseTCBVariables()初始化任務TCB必要的欄位。在呼叫建立任務API函數xTaskCreate()時,引數pcName(任務描述)、uxPriority(任務優先順序)都會被寫入任務TCB相應的欄位,TCB欄位中的xStateListItem和xEventListItem列表項也會被初始化,初始化後的列表項如圖2-1所示。
在圖2-1中,列表項xEventListItem的成員列表項值xItemValue被初始為4,這是因為我在應用中設定的最大優先順序數目(configMAX_PRIORITIES)為5,而xEventListItem. xItemValue等於configMAX_PRIORITIES減去任務A的優先順序(為1),即5-1=4。這一點很重要,在這裡xItemValue不是直接儲存任務優先順序,而是儲存優先順序的補數,這意味著xItemValue的值越大,對應的任務優先順序越小。
FreeRTOS核心使用vListInsert函數(詳細見FreeRTOS進階列表和列表項範例分析)將事件列表項插入到一個列表,這個函數根據xItemValue的值的大小順序來進行插入操作。
使用宏listGET_OWNER_OF_HEAD_ENTRY獲得列表中的第一個列表項的xItemValue值總是最小,也就是優先順序最高的任務!
圖2-1:初始化狀態和事件列表項
此外,TCB其它的一些欄位也被初始化,比如臨界區巢狀次數、執行時間計數器、任務通知值、任務通知狀態等,函數prvInitialiseTCBVariables()的原始碼如下所示:
static void prvInitialiseTCBVariables( TCB_t * const pxTCB, const char * const pcName, UBaseType_t uxPriority, const MemoryRegion_t * const xRegions, const uint16_t usStackDepth ) { UBaseType_t x; /* 將任務描述存入TCB */ for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) { pxTCB->pcTaskName[ x ] = pcName[ x ]; if( pcName[ x ] == 0x00 ) { break; } } /* 確保字串有結束 */ pxTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = ' '; /* 調整優先順序,宏configMAX_PRIORITIES的值在FreeRTOSConfig.h中設定 */ if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) { uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; } pxTCB->uxPriority = uxPriority; #if ( configUSE_MUTEXES == 1 ) /*使用互斥量*/ { pxTCB->uxBasePriority = uxPriority; pxTCB->uxMutexesHeld = 0; } #endif /* configUSE_MUTEXES */ /*初始化列表項*/ vListInitialiseItem( &( pxTCB->xStateListItem ) ); vListInitialiseItem( &( pxTCB->xEventListItem ) ); /* 設定列表項xStateListItem的成員pvOwner指向當前任務控制塊 */ listSET_LIST_ITEM_OWNER( &( pxTCB->xStateListItem ), pxTCB ); /* 設定列表項xEventListItem的成員xItemValue*/ listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /* 設定列表項xEventListItem的成員pvOwner指向當前任務控制塊 */ listSET_LIST_ITEM_OWNER( &( pxTCB->xEventListItem ), pxTCB ); #if ( portCRITICAL_NESTING_IN_TCB ==1 ) /*使能臨界區巢狀功能*/ { pxTCB->uxCriticalNesting = ( UBaseType_t ) 0U; } #endif /* portCRITICAL_NESTING_IN_TCB */ #if ( configUSE_APPLICATION_TASK_TAG == 1 ) /*使能任務標籤功能*/ { pxTCB->pxTaskTag = NULL; } #endif /* configUSE_APPLICATION_TASK_TAG */ #if ( configGENERATE_RUN_TIME_STATS == 1 ) /*使能事件統計功能*/ { pxTCB->ulRunTimeCounter = 0UL; } #endif /* configGENERATE_RUN_TIME_STATS */ #if ( portUSING_MPU_WRAPPERS == 1 ) /*使用MPU功能*/ { vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), xRegions, pxTCB->pxStack, usStackDepth ); } #else /* portUSING_MPU_WRAPPERS */ { ( void ) xRegions; ( void ) usStackDepth; } #endif /* portUSING_MPU_WRAPPERS */ #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 )/*使能執行緒本地儲存指標*/ { for( x = 0; x < ( UBaseType_t )configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ ) { pxTCB->pvThreadLocalStoragePointers[ x ] = NULL; } } #endif #if ( configUSE_TASK_NOTIFICATIONS == 1 ) /*使能任務通知功能*/ { pxTCB->ulNotifiedValue = 0; pxTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; } #endif #if ( configUSE_NEWLIB_REENTRANT == 1 ) /*使用Newlib*/ { _REENT_INIT_PTR( ( &( pxTCB->xNewLib_reent ) ) ); } #endif #if( INCLUDE_xTaskAbortDelay == 1 ) { pxTCB->ucDelayAborted = pdFALSE; } #endif }
呼叫函數pxPortInitialiseStack()初始化任務堆疊,並將最新的棧頂指標賦值給任務TCB的pxTopOfStack欄位。
呼叫函數pxPortInitialiseStack()後,相當於執行了一次系統節拍時鐘中斷:將一些重要暫存器入棧。雖然任務還沒開始執行,也並沒有中斷髮生,但看上去就像暫存器已經被入棧了,並且部分堆疊值被修改成了我們需要的已知值。
對於不同的硬體架構,入棧的暫存器也不相同,所以我們看到這個函數是由移植層提供的。對於Cortex-M3架構,需要依次入棧xPSR、PC、LR、R12、R3~R0、R11~R4,假設堆疊是向下生長的,初始化後的堆疊如圖3-1所示。
在圖3-1中我們看到暫存器xPSR被初始為0x01000000,其中bit24被置1,表示使用Thumb指令;
暫存器PC被初始化為任務函數指標vTask_A,這樣當某次工作切換後,任務A獲得CPU控制權,任務函數vTask_A被出棧到PC暫存器,之後會執行任務A的程式碼;
LR暫存器初始化為函數指標prvTaskExitError,這是由移植層提供的一個出錯處理常式。
當中斷髮生時,LR被設定成中斷要返回的地址,但是每個任務都是一個死迴圈,正常情況下不應該退出任務函數,所以一旦從任務函數退出,說明那裡出錯了,這個時候會呼叫暫存器LR指向的函數來處理這個錯誤,即prvTaskExitError;
根據ATPCS(ARM-Thumb過程呼叫標準),我們知道子函數呼叫通過暫存器R0~R3傳遞引數,在文章的最開始講xTaskCreate()函數時,提到這個函數有一個空指標型別的引數pvParameters,當任務建立時,它作為一個引數傳遞給任務,所以這個引數被儲存到R0中,用來向任務傳遞引數。
任務TCB結構體成員pxTopOfStack表示當前堆疊的棧頂,它指向最後一個入棧的專案,所以在圖中它指向R4,TCB結構體另外一個成員pxStack表示堆疊的起始位置,所以在圖中它指向堆疊的最開始處。
圖3-1:初始化任務堆疊
呼叫taskENTER_CRITICAL()進入臨界區,這是一個宏定義,最終進入臨界區的程式碼由移植層提供。
在tasks.c中 ,定義了一些靜態私有變數,用來跟蹤任務的數量或者狀態等等,其中變數uxCurrentNumberOfTasks表示當前任務的總數量,每建立一個任務,這個變數都會增加1。
如果這是第一個任務(uxCurrentNumberOfTasks等於1),則呼叫函數prvInitialiseTaskLists()初始化任務列表。FreeRTOS使用列表來跟蹤任務,在tasks.c中,定義了靜態型別的列表變數:
PRIVILEGED_DATAstatic List_t pxReadyTasksLists[ configMAX_PRIORITIES ];/*按照優先順序排序的就緒態任務*/ PRIVILEGED_DATAstatic List_t xDelayedTaskList1; /*延時的任務 */ PRIVILEGED_DATAstatic List_t xDelayedTaskList2; /*延時的任務 */ PRIVILEGED_DATAstatic List_t xPendingReadyList; /*任務已就緒,但排程器被掛起 */ #if (INCLUDE_vTaskDelete == 1 ) PRIVILEGED_DATA static List_t xTasksWaitingTermination; /*任務已經被刪除,但記憶體尚未釋放*/ #endif #if (INCLUDE_vTaskSuspend == 1 ) PRIVILEGED_DATA static List_t xSuspendedTaskList; /*當前掛起的任務*/ #endif
現在這些列表都要進行初始化,會呼叫API函數vListInitialise()初始化列表,這個函數在FreeRTOS列表和列表項中講過,每個列表的初始化方式都是相同的,以就緒態列表pxReadyTasksLists[0]為例,初始化後如圖6-1所示:
圖6-1:初始化後的列表
函數prvInitialiseTaskLists()的原始碼如下所示:
static void prvInitialiseTaskLists( void ) { UBaseType_tuxPriority; for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ ) { vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) ); } vListInitialise( &xDelayedTaskList1 ); vListInitialise( &xDelayedTaskList2 ); vListInitialise( &xPendingReadyList ); #if ( INCLUDE_vTaskDelete == 1 ) { vListInitialise( &xTasksWaitingTermination ); } #endif /* INCLUDE_vTaskDelete */ #if ( INCLUDE_vTaskSuspend == 1 ) { vListInitialise( &xSuspendedTaskList ); } #endif /* INCLUDE_vTaskSuspend */ /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskListusing list2. */ pxDelayedTaskList = &xDelayedTaskList1; pxOverflowDelayedTaskList = &xDelayedTaskList2; }
tasks.c中定義了一個任務TCB指標型變數:
PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB= NULL;
這是一個全域性變數,在tasks.c中只定義了這一個全域性變數。這個變數用來指向當前正在執行的任務TCB,我們需要多瞭解一下這個變數。
FreeRTOS的核心是確保處於優先順序最高的就緒任務獲得CPU執行權。在下一章講述工作切換時會知道,工作切換就是找到優先順序最高的就緒任務,而找出的這個最高優先順序任務的TCB,就被賦給變數pxCurrentTCB。
如果排程器還沒有準備好(程式剛開始執行時,可能會先建立幾個任務,之後才會啟動排程器),並且新建立的任務優先順序大於變數pxCurrentTCB指向的任務優先順序,則設定pxCurrentTCB指向當前新建立的任務TCB(確保pxCurrentTCB指向優先順序最高的就緒任務)。
if( xSchedulerRunning == pdFALSE ) { if( pxCurrentTCB->uxPriority <= uxPriority ) { pxCurrentTCB = pxNewTCB; } else { mtCOVERAGE_TEST_MARKER(); } }
呼叫prvAddTaskToReadyList(pxNewTCB)將建立的任務TCB加入到就緒列表陣列中,任務的優先順序確定了加入到就緒列表陣列的哪個下標。比如我們新建立的任務優先順序為1,則這個任務被加入到列表pxReadyTasksLists[1]中。
prvAddTaskToReadyList()其實是一個宏,由一系列語句組成,去除其中的跟蹤宏外,這個宏定義如下所示:
#defineprvAddTaskToReadyList( pxTCB ) taskRECORD_READY_PRIORITY( ( pxTCB)->uxPriority ); vListInsertEnd( &( pxReadyTasksLists[ (pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) );
宏taskRECORD_READY_PRIORITY()用來更新變數uxTopReadyPriority,這個變數在tasks.c中定義為靜態變數,記錄處於就緒態的最高任務優先順序。這個變數參與了FreeRTOS的最核心程式碼:確保處於優先順序最高的就緒任務獲得CPU執行權。它在這裡參與如何最快的找到優先順序最高的就緒任務。為了最快,不同的架構會各顯神通,一些架構還有特殊指令可用,所以這個宏由移植層提供。我們會在下一章介紹工作切換時,以Cortex-M3架構為例,詳細介紹如何最快的找到優先順序最高的就緒任務。
函數vListInsertEnd()將列表項插入到列表末端,在FreeRTOS進階列表和列表項範例分析中已經提到過,這裡會結合著例子再看一下這個函數。
從前面我們直到,在呼叫函數vListInsertEnd()之前,就緒列表pxReadyTasksLists[1]和任務TCB的狀態列表項xStateListItem都已經初始化好了,見圖6-1和圖2-1,為了方便檢視,我們將這兩幅圖合成一副,見圖8-1。
圖8-1:初始化後的列表和列表項
呼叫vListInsertEnd(a,b)會將列表項b,插入到列表a的後面,函數執行完畢後,列表和列表項的關係如圖8-2所示。
圖8-2:插入一個列表項後的列表
在此基礎上,假設又建立了任務B,任務A和任務B優先順序相同,都為1。和任務A一樣,任務B也有它自己的任務TCB,其中的狀態列表項欄位xStateListItem也要插入到列表pxReadyTasksLists[1]中,新的列表和列表項如圖8-3所示。
圖8-3:相同優先順序就緒列表掛接兩個列表項
呼叫taskEXIT_CRITICAL()退出臨界區,這是一個宏定義,最終退出臨界區的程式碼由移植層提供。
如果上面的步驟都正確執行,並且排程器也開始工作,則判斷當前任務的優先順序是否大於新建立的任務優先順序。如果新建立的任務優先順序更高,則呼叫taskYIELD_IF_USING_PREEMPTION()強制進行一次上下文切換,切換後,新建立的任務將獲得CPU控制權,精簡後的程式碼如下所示。
if( xReturn == pdPASS ) { if( xSchedulerRunning != pdFALSE ) { /* 如果新建立的任務優先順序大於當前任務優先順序,則新建立的任務應該被立即執行。*/ if(pxCurrentTCB->uxPriority < uxPriority ) { taskYIELD_IF_USING_PREEMPTION(); } } }
以上就是FreeRTOS進階之任務建立完全解析的詳細內容,更多關於FreeRTOS進階任務建立分析的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45