開局一張圖,一步一步分析就好,

(一)什么是任務?
在多任務系統中,我們按照功能不同,把整個系統分割成一個個獨立的,且無法回傳的函式,這個函式我們稱為任務;任務包含幾個屬性:任務堆疊,任務函式、任務控制塊、任務優先級;下面主要介紹一下任務控制塊,其他都比較容易理解,
(二)什么是任務控制塊?
任務控制塊內包含了該任務的全部資訊,任務的執行需要通過任務調度器來控制,那么任務調度器怎么“控制”任務物體的呢?就要抓住任務的小辮子---“任務控制塊”,系統對任務的全部操作都可以通過任務控制塊來實作!它是一種特別的資料結構,
在任務創建函式xTaskCreat()創建任務的時候就會自動給每個任務分配一個任務控制塊,
typedef struct tskTaskControlBlock { volatile StackType_t *pxTopOfStack; /*任務堆疊堆疊頂指標*/ #if ( portUSING_MPU_WRAPPERS == 1 ) xMPU_SETTINGS xMPUSettings; /*MPU相關設定*/ #endif ListItem_t xStateListItem; /*狀態串列項,這是一個內置在TCB控制塊中的一個鏈表節點,通過這個節點,將任務掛到其他鏈表中
比如就緒串列,阻塞串列,掛起串列等*/
ListItem_t xEventListItem; /*事件串列項,用于參考事件串列中的任務*/ UBaseType_t uxPriority; /*任務優先級*/ 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; /*debug的時候用到*/ UBaseType_t uxTaskNumber; /*trace的時候用到*/ #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 ) struct _reent xNewLib_reent; /*定義一個newlib結構體變數*/ #endif #if( configUSE_TASK_NOTIFICATIONS == 1 ) /*任務通知相關變數*/ volatile uint32_t ulNotifiedValue; /*任務通知值*/ volatile uint8_t ucNotifyState; /*任務通知狀態*/ #endif /* 用來標記任務是動態創建還是靜態創建*/ #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) uint8_t ucStaticallyAllocated; /*靜態創建此變數為pdTURE;動態創建此變數為pdFALSE*/ #endif #if( INCLUDE_xTaskAbortDelay == 1 ) uint8_t ucDelayAborted; #endif } tskTCB;
注:#if 開頭的都是條件編譯,咱們可以先不用理解,基本結構如下:

指標pxStack指向堆疊的起始位置,任務創建時會分配指定數目的任務堆疊,申請堆疊記憶體函式回傳的指標就被賦給該變數,
很多剛接觸FreeRTOS的人會分不清指標pxTopOfStack和pxStack的區別,這里簡單說一下:pxTopOfStack指向當前堆疊堆疊頂,隨著進堆疊出堆疊,pxTopOfStack指向的位置是會變化的;pxStack指向當前堆疊的起始位置,一經分配后,堆疊起始位置就固定了,不會被改變了,那么為什么需要pxStack變數呢,這是因為隨著任務的運行,堆疊可能會溢位,在堆疊向下增長的系統中,這個變數可用于檢查堆疊是否溢位;如果在堆疊向上增長的系統中,要想確定堆疊是否溢位,還需要另外一個變數pxEndOfStack來輔助診斷是否堆疊溢位,
(三)任務是怎么創建出來的?
任務有兩種創建方式,動態創建和靜態創建,兩者的區別就是: 靜態創建時候任務控制塊和任務堆疊的記憶體是由用戶自己定義的,任務洗掉的時候,記憶體不能自動釋放,動態創建,任務堆疊和任務控制塊的記憶體是由系統自動創建的,自動釋放的,
動態創建任務的函式為 xTaskCreate();
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, //任務函式的名稱 const char * const pcName, //任務的名稱 const uint16_t usStackDepth, //任務堆疊大小 void * const pvParameters, //任務的形參 UBaseType_t uxPriority, //任務優先級 TaskHandle_t * const pxCreatedTask ) // 用于傳回一個任務句柄,創建任務后使用這個句柄參考(控制)任務,本質上是一個空指標, { TCB_t *pxNewTCB; BaseType_t xReturn; #define portSTACK_GROWTH //-1表示滿減堆疊 #if( portSTACK_GROWTH > 0 ){ } #else{ /* portSTACK_GROWTH<0 代表堆疊向下增長 */ StackType_t *pxStack; /* 任務堆疊記憶體分配,stm32是向下增長的堆疊,獲取到的pxStack 是一個堆疊底的指標*/ pxStack = ( StackType_t *) 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 /* portSTACK_GROWTH */ if( pxNewTCB != NULL ) { /* 新建任務初始化 */ prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); /* 把任務添加到就緒串列中 */ prvAddNewTaskToReadyList( pxNewTCB ); xReturn = pdPASS; } else{ xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; } return xReturn; }
之后,又呼叫了函式 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; /* 計算堆疊頂的地址 */ #if( portSTACK_GROWTH < 0 ){ /* 把堆疊空間的高地址分配給堆疊頂 */ pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); /* 堆疊對齊----堆疊要8位元組對齊 */ 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 */ /* 存盤任務名稱 */ for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ){ pxNewTCB->pcTaskName[ x ] = pcName[ x ]; if( pcName[ x ] == 0x00 ){ break; } else{ mtCOVERAGE_TEST_MARKER(); } } /* \0補齊字串 */ pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; /* 判斷任務分配的優先級,是否大于最大值 如果超過最大值,賦值最大值 */ if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ){ uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; } else{ mtCOVERAGE_TEST_MARKER(); } /* 賦值任務優先級到任務控制塊 */ pxNewTCB->uxPriority = uxPriority; /* 任務狀態表 事件表初始化 */ vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); /* 設定任務控制塊中的狀態串列項的成員變數ower ,是屬于PxNewTCB(擁有該結點的內核物件) */ listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); /*更改事件串列項中的成員變數xItemValue的值,目的是串列在排列的時候,是按照優先級由大到小排列 */ listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );
/*設定任務控制塊中事件串列項的成員變數ower,同上*/ listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); #if( portUSING_MPU_WRAPPERS == 1 ){ } #else{ /* portUSING_MPU_WRAPPERS */ /* 初始化任務堆疊,之后回傳任務堆疊頂 */ pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); } #endif /* portUSING_MPU_WRAPPERS */ if( ( void * ) pxCreatedTask != NULL ){ /* 任務句柄指向任務控制塊 */ *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; } else{ mtCOVERAGE_TEST_MARKER(); } }
prvInitialiseNewTask()函式的形參,出來xTaskCreat()的形參之外,又多出來pxNewTCB和xRegions兩個形參;
后面又呼叫了 pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters)
來初始化任務堆疊,
StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters){ pxTopOfStack--; /* 入堆疊程式狀態暫存器 */ *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; /* 入堆疊PC指標 */ *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */ pxTopOfStack--; /* 入堆疊LR鏈接暫存器 */ *pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */ pxTopOfStack -= 5; /* 跳過R12, R3, R2 and R1這四個暫存器,不初始化 */ *pxTopOfStack = ( StackType_t ) pvParameters; /* R0作為傳參入堆疊 */ pxTopOfStack--; /* 保存EXC_RETURN的值,用于退出SVC或PendSV中斷時候,處理器處于什么狀態*/ *pxTopOfStack = portINITIAL_EXEC_RETURN; pxTopOfStack -= 8; /* 跳過R11, R10, R9, R8, R7, R6, R5 and R4這8個暫存器,不初始化 */ return pxTopOfStack; /*最侄訓傳堆疊頂*/
初始化堆疊完成之后堆疊如下圖:

層層深入完畢,現在我們回傳到xTaskCreat()函式后面,看看 prvAddNewTaskToReadyList( pxNewTCB ); 函式是怎么把任務添加到就緒串列中!
static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) { taskENTER_CRITICAL(); { uxCurrentNumberOfTasks++; if( pxCurrentTCB == NULL ) //正在運行的任務塊為NULL,沒有任務運行; { pxCurrentTCB = pxNewTCB; //將新任務控制塊賦值給pxCurrentTCB if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) //為1說明正在創建的任務是第一個任務, { prvInitialiseTaskLists(); //初始化串列,就緒串列、阻塞串列等等 } else { mtCOVERAGE_TEST_MARKER(); } } else { if( xSchedulerRunning == pdFALSE ) //判斷任務調度器是否運行,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; } #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(); //呼叫此函式完成一次任務切換 } else { mtCOVERAGE_TEST_MARKER(); } } else { mtCOVERAGE_TEST_MARKER(); } }
一定要耐心分析,別無他法,加油!不難,
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/456080.html
標籤:其他
