在FreeRTOS中,線程的術語又可以被稱之為任務,或許這樣更加合適,本文將介紹任務的創建/刪除,任務參數的使用,以及任務優先級;
1 軟實時和硬實時
硬實時系統的任務運行正確性與響應時限是緊密相關的,一旦超過時限將導致嚴重的后果,比如導彈控制系統、高鐵自動駕駛系統等,都是需要嚴格的響應時限的。 軟實時系統中,雖然也存在時限指標,但是如果輸出響應超過時限,一般不會造成嚴重后果,比如Windows桌面任務,DVD播放機的視頻播放。 大多數嵌入式系統不僅能滿足硬實時要求,也能滿足軟實時要求。 軟實時:
Windows
;Linux
系統通常為軟實時,當然有補丁可以將內核做成硬實時的系統,不過商用沒有這么做的。
硬實時:
VxWorks
,uCOS
,FreeRTOS
,WinCE
,RT-thread
等實時系統;
2 任務概述
2.1 基本寫法
FreeRTOS
是多任務的實時系統,其最基本的運行單元為任務,其表示形式為由C
語言函數實現的,該函數原型要求必須返回 void,并且帶一個 void 類型指針的參數;具體如下所示;
void ATaskFunc(void *args);
每個任務都是在自己權限范圍內的一個小程序。其具有程序入口,通常會運行在一個死循環中,也不會退出,具體如下;
void ATaskFunc(void *args){ while(1){ //TODO }}
FreeRTOS
任務不允許以任何方式從實現函數中返回——它們絕不能有一條return
語句,也不能執行到函數末尾,如果不再需要,則在任務中調用刪除任務的API,具體如下所示;
void ATaskFunc(void *args){ vTaskDelete( NULL );}
2.2 TCB
TCB
為任務控制塊,或者是線程控制塊,另外操作系統中還有PCB為進程控制塊,主要封裝了一個任務在系統調度中所需要的所有資源,FreeRTOS
中TCB
的成員,具體如下所示;
任務狀態如下所示;
typedef struct xTASK_STATUS
{
/* The handle of the task to which the rest of the information in the structure relates. */
TaskHandle_t xHandle;
/* A pointer to the task's name.*/
const char *pcTaskName;
/* A number unique to the task. */
UBaseType_t xTaskNumber;
/* The state in which the task existed when the structure was populated. */
eTaskState eCurrentState;
/* The priority at which the task was running (may be inherited) when the structure was populated. */
UBaseType_t uxCurrentPriority;
UBaseType_t uxBasePriority;
uint32_t ulRunTimeCounter;
StackType_t *pxStackBase;
uint16_t usStackHighWaterMark;
} TaskStatus_t;
每一個任務都會擁有一個自己的TCB
,具體如下圖所示;
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */
#if ( portUSING_MPU_WRAPPERS == 1 )
xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */
#endif
ListItem_t xStateListItem; /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */
ListItem_t xEventListItem; /*< Used to reference a task from an event list. */
UBaseType_t uxPriority; /*< The priority of the task. 0 is the lowest priority. */
StackType_t *pxStack; /*< Points to the start of the stack. */
char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
#if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
StackType_t *pxEndOfStack; /*< Points to the highest valid address for the stack. */
#endif
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
UBaseType_t uxCriticalNesting; /*< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxTCBNumber; /*< Stores a number that increments each time a TCB is created. It allows debuggers to determine when a task has been deleted and then recreated. */
UBaseType_t uxTaskNumber; /*< Stores a number specifically for use by third party trace code. */
#endif
#if ( configUSE_MUTEXES == 1 )
UBaseType_t uxBasePriority; /*< The priority last assigned to the task - used by the priority inheritance mechanism. */
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; /*< Stores the amount of time the task has spent in the Running state. */
#endif
#if ( configUSE_NEWLIB_REENTRANT == 1 )
struct _reent xNewLib_reent;
#endif
#if( configUSE_TASK_NOTIFICATIONS == 1 )
volatile uint32_t ulNotifiedValue;
volatile uint8_t ucNotifyState;
#endif
/* See the comments above the definition of
tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 Macro has been consolidated for readability reasons. */
uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */
#endif
#if( INCLUDE_xTaskAbortDelay == 1 )
uint8_t ucDelayAborted;
#endif
} tskTCB;
3 任務狀態
任務頂層存在兩種狀態,運行態和非運行態; 但是非運行的任務狀態又可以分為:堵塞狀態 / Blocked
,掛起狀態 / Suspend
,就緒狀態 / Ready
,下面簡單做一下介紹;
-
運行狀態 /
Running
運行態的任務完全占用CPU
的使用權,如果當前CPU
只有一個內核,那么在某個時刻只能運行一個任務,就是所謂的單核單線程; -
堵塞狀態 /
Blocked
; 用戶可以主動調用vTaskDelay(T)
將任務進入堵塞狀態,直到任務堵塞時間已經達到T
;或者該任務在等待隊列,信號量,事件組,通知或信號量事件時,也將處于堵塞狀態;處于堵塞狀態的任務不再占用CPU
,同樣也不能直接進入運行狀態,而是先進入就緒狀態; -
掛起狀態 /
Suspend
; 任何狀態的下的任務都可以通過調用vTaskSuspend
函數進入掛起狀態,并且無法直接進入運行態,只能通過調用xTaskResume
函數進入就緒狀態; -
就緒狀態 /
Ready
; 被搶占的任務將處于就緒狀態,掛起的任務被回復的會處于就緒狀態,堵塞的任務收到相應事件也會處于就緒狀態,如果當前沒有更高優先級的任務處于運行,則當前就緒狀態的任務進入運行狀態;
其主要的狀態轉換關系如下圖所示;
FreeRTOS
使用一個枚舉封裝了任務的狀態,具體如下所示;
typedef enum
{
eRunning = 0,/* A task is querying the state of itself, so must be running. */
eReady, /* The task being queried is in a read or pending ready list. */
eBlocked, /* The task being queried is in the Blocked state. */
eSuspended, /* The task being queried is in the Suspended state*/
eDeleted, /* The task being queried has been deleted, but its TCB has not yet been freed. */
eInvalid /* Used as an 'invalid state' value. */
} eTaskState;
4 任務優先級
FreeRTOS的最低優先級是0,分配給了空閑任務,空閑任務的優先級使用宏定義tskIDLE_PRIORITY
進行表示,最大的優先級為需要用戶進行配置;在FreeRTOS.h
中可以看到預編譯指令,具體如下:
#ifndef configMAX_PRIORITIES
#error Missing definition: configMAX_PRIORITIES must be defined in FreeRTOSConfig.h. \
See the Configuration section of the FreeRTOS API documentation for details.
#endif
#if configMAX_PRIORITIES < 1
#error configMAX_PRIORITIES must be defined to be greater than or equal to 1.
#endif
configMAX_PRIORITIES
需要用戶在FreeRTOSConfig.h
進行定義,則任務最大的優先級為configMAX_PRIORITIES - 1
。
5 相關函數
任務管理的函數聲明和一些基本類型都封裝在源碼tasks.h
中;
5.1 創建任務
使用xTaskCreate
創建一個任務,具體如下所示;
// Task to be created.
void vTaskCode( void * pvParameters )
{
for( ;; )
{
// Task code goes here.
}
}
// Function that creates a task.
void vOtherFunction( void )
{
static uint8_t ucParameterToPass;
TaskHandle_t xHandle = NULL;
// Create the task, storing the handle. Note that the passed parameter ucParameterToPass
// must exist for the lifetime of the task, so in this case is declared static. If it was just an
// an automatic stack variable it might no longer exist, or at least have been corrupted, by the time
// the new task attempts to access it.
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle );
configASSERT( xHandle );
// Use the handle to delete the task.
if( xHandle != NULL )
{
vTaskDelete( xHandle );
}
}
5.2 函數刪除
使用函數vTaskDelete
對函數進行刪除;
void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION;
void vOtherFunction( void )
{
TaskHandle_t xHandle;
// Create the task, storing the handle.
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
// Use the handle to delete the task.
vTaskDelete( xHandle );
}
5.3 堵塞任務
使用vTaskDelay
函數可以將任務堵塞一定時間;
void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION;
void vTaskFunction( void * pvParameters )
{
// Block for 500ms.
const TickType_t xDelay = 500 / portTICK_PERIOD_MS;
for( ;; )
{
// Simply toggle the LED every 500ms, blocking between each toggle.
vToggleLED();
vTaskDelay( xDelay );
}
}
5.4 掛起和恢復
使用函數vTaskSuspend
可以將函數掛起,通過vTaskResume(xHandle)
函數可以將掛起的函數恢復到就緒狀態;
void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION;
void vAFunction( void )
{
TaskHandle_t xHandle;
// Create a task, storing the handle.
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
// ...
// Use the handle to suspend the created task.
vTaskSuspend( xHandle );
// ...
// The created task will not run during this period, unless
// another task calls vTaskResume( xHandle ).
//...
// Suspend ourselves.
vTaskSuspend( NULL );
// We cannot get here unless another task calls vTaskResume
// with our handle as the parameter.
}
6 總結
先了解任務之前的狀態,先學會使用FreeRTOS的常用接口,后續對于其調度算法和內存管理的算法可以分析和學習一下。
文中難免有錯誤和紕漏之處,請大佬們不吝賜教 創作不易,如果本文幫到了您; 請幫忙點個贊