4、tasks.c解析
時隔兩年,還是決定繼續把這個系統解析完成,有始有終。不過這次原始碼又從官網上下載了最新的,可能和我以前看的略有區別,但應該基本不影響理解。
接下來正式開始。
-
4.1.3 新增或是遺漏的兩個宏
1 /* Returns pdTRUE if the task is actively running and not scheduled to yield. */ 2 /* 如果任務正在執行並且沒有被排程,則返回 TRUE。 */ 3 #define taskTASK_IS_RUNNING( pxTCB ) ( ( ( pxTCB ) == pxCurrentTCB ) ? ( pdTRUE ) : ( pdFALSE ) ) 4 #define taskTASK_IS_RUNNING_OR_SCHEDULED_TO_YIELD( pxTCB ) ( ( ( pxTCB ) == pxCurrentTCB ) ? ( pdTRUE ) : ( pdFALSE ) )
4.2 介面解析
4.2.1 靜態建立任務--xTaskCreateStatic
這個介面建立的任務,根據“FreeRTOS.h”檔案的描述,當任務刪除時,任務的棧和TCB都不能被釋放。
1 /* 根據FreeRTOSConfig.h配置的不同結構體會有差異, 這是我的結果 */ 2 struct xSTATIC_LIST_ITEM 3 { 4 TickType_t xDummy2; 5 void * pvDummy3[ 4 ]; 6 }; 7 typedef struct xSTATIC_LIST_ITEM StaticListItem_t; 8 9 /* 根據FreeRTOSConfig.h配置的不同結構體會有差異, 這是我的結果。 10 * configMAX_TASK_NAME_LEN = 16 11 * configTASK_NOTIFICATION_ARRAY_ENTRIES = 1 */ 12 typedef struct xSTATIC_TCB { 13 void * pxDummy1; 14 StaticListItem_t xDummy3[ 2 ]; 15 UBaseType_t uxDummy5; 16 void * pxDummy6; 17 uint8_t ucDummy7[ configMAX_TASK_NAME_LEN ]; 18 UBaseType_t uxDummy12[ 2 ]; 19 uint32_t ulDummy18[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; 20 uint8_t ucDummy19[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; 21 uint8_t uxDummy20; 22 } StaticTask_t; 23 24 25 static TCB_t * prvCreateStaticTask( TaskFunction_t pxTaskCode, 26 const char * const pcName, 27 const configSTACK_DEPTH_TYPE uxStackDepth, 28 void * const pvParameters, 29 UBaseType_t uxPriority, 30 StackType_t * const puxStackBuffer, 31 StaticTask_t * const pxTaskBuffer, 32 TaskHandle_t * const pxCreatedTask ) 33 { 34 TCB_t * pxNewTCB; 35 36 configASSERT( puxStackBuffer != NULL ); 37 configASSERT( pxTaskBuffer != NULL ); 38 39 #if ( configASSERT_DEFINED == 1 ) 40 { 41 /* Sanity check that the size of the structure used to declare a 42 * variable of type StaticTask_t equals the size of the real task 43 * structure. */ 44 /* 確保靜態的TCB和TCB_t大小是一致的,一般是一致的 */ 45 volatile size_t xSize = sizeof( StaticTask_t ); 46 configASSERT( xSize == sizeof( TCB_t ) ); 47 ( void ) xSize; /* Prevent unused variable warning when configASSERT() is not used. */ 48 } 49 #endif /* configASSERT_DEFINED */ 50 51 if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) ) 52 { 53 /* The memory used for the task's TCB and stack are passed into this 54 * function - use them. */ 55 /* MISRA Ref 11.3.1 [Misaligned access] */ 56 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-113 */ 57 /* coverity[misra_c_2012_rule_11_3_violation] */ 58 pxNewTCB = ( TCB_t * ) pxTaskBuffer; 59 ( void ) memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); 60 pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer; 61 62 #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) 63 { 64 /* Tasks can be created statically or dynamically, so note this 65 * task was created statically in case the task is later deleted. */ 66 /* 標記任務是靜態建立的 */ 67 pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; 68 } 69 #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ 70 71 /* 初始化任務引數,後面會講到 */ 72 prvInitialiseNewTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); 73 } 74 else 75 { 76 pxNewTCB = NULL; 77 } 78 79 return pxNewTCB; 80 } 81 /*-----------------------------------------------------------*/ 82 83 /* pxTaskCode:任務入口; 84 * pcName:任務名稱; 85 * uxStackDepth:任務棧深; 86 * pvParameters:任務形參; 87 * uxPriority:任務優先順序; 88 * puxStackBuffer:任務棧,使用者提供記憶體; 89 * pxTaskBuffer:任務TCB資料存放,使用者提供記憶體。 */ 90 TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, 91 const char * const pcName, 92 const configSTACK_DEPTH_TYPE uxStackDepth, 93 void * const pvParameters, 94 UBaseType_t uxPriority, 95 StackType_t * const puxStackBuffer, 96 StaticTask_t * const pxTaskBuffer ) 97 { 98 TaskHandle_t xReturn = NULL; 99 TCB_t * pxNewTCB; 100 101 /* 設定任務,初始化引數 */ 102 pxNewTCB = prvCreateStaticTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, puxStackBuffer, pxTaskBuffer, &xReturn ); 103 104 if( pxNewTCB != NULL ) 105 { 106 prvAddNewTaskToReadyList( pxNewTCB ); /* 加入到就緒列表中,後面會講到 */ 107 } 108 else 109 { 110 mtCOVERAGE_TEST_MARKER(); 111 } 112 113 return xReturn; 114 }
4.2.2 動態建立任務--xTaskCreate
1 static TCB_t * prvCreateTask( TaskFunction_t pxTaskCode, 2 const char * const pcName, 3 const configSTACK_DEPTH_TYPE uxStackDepth, 4 void * const pvParameters, 5 UBaseType_t uxPriority, 6 TaskHandle_t * const pxCreatedTask ) 7 { 8 TCB_t * pxNewTCB; 9 10 11 StackType_t * pxStack; 12 13 /* 在堆疊管理的文章中有介紹過,申請記憶體 14 * #define pvPortMallocStack pvPortMalloc 15 * #define vPortFreeStack vPortFree */ 16 pxStack = pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) ); 17 18 if( pxStack != NULL ) 19 { 20 /* Allocate space for the TCB. */ 21 pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); 22 23 if( pxNewTCB != NULL ) 24 { 25 ( void ) memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); 26 27 /* Store the stack location in the TCB. */ 28 pxNewTCB->pxStack = pxStack; 29 } 30 else 31 { 32 /* The stack cannot be used as the TCB was not created. Free 33 * it again. */ 34 vPortFreeStack( pxStack ); 35 } 36 } 37 else 38 { 39 pxNewTCB = NULL; 40 } 41 42 if( pxNewTCB != NULL ) 43 { 44 #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) 45 { 46 /* Tasks can be created statically or dynamically, so note this 47 * task was created dynamically in case it is later deleted. */ 48 /* 標記任務是動態建立的 */ 49 pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; 50 } 51 #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ 52 53 /* 初始化任務引數,後面會講到 */ 54 prvInitialiseNewTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); 55 } 56 57 return pxNewTCB; 58 } 59 /*-----------------------------------------------------------*/ 60 61 /* 這裡的形參就不介紹了,應該都比較清楚 */ 62 BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, 63 const char * const pcName, 64 const configSTACK_DEPTH_TYPE uxStackDepth, 65 void * const pvParameters, 66 UBaseType_t uxPriority, 67 TaskHandle_t * const pxCreatedTask ) 68 { 69 TCB_t * pxNewTCB; 70 BaseType_t xReturn; 71 72 /* 設定任務,初始化引數 */ 73 pxNewTCB = prvCreateTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask ); 74 75 if( pxNewTCB != NULL ) 76 { 77 prvAddNewTaskToReadyList( pxNewTCB ); /* 加入到就緒列表中,後面會講到 */ 78 xReturn = pdPASS; 79 } 80 else 81 { 82 xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; 83 } 84 85 return xReturn; 86 }
4.2.3 初始化任務--prvInitialiseNewTask
這個函式比較複雜,是在建立任務的時候呼叫的,基本邏輯比較清晰,去除了一些多核、MPU的程式碼就簡化很多。有一些列表的值也許暫時不清楚為何如此設定,但到使用時應該就清楚這個初始值的含義了。
1 static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, 2 const char * const pcName, 3 const configSTACK_DEPTH_TYPE uxStackDepth, 4 void * const pvParameters, 5 UBaseType_t uxPriority, 6 TaskHandle_t * const pxCreatedTask, 7 TCB_t * pxNewTCB, 8 const MemoryRegion_t * const xRegions ) 9 { 10 StackType_t * pxTopOfStack; 11 UBaseType_t x; 12 13 /* Calculate the top of stack address. This depends on whether the stack 14 * grows from high memory to low (as per the 80x86) or vice versa. 15 * portSTACK_GROWTH is used to make the result positive or negative as required 16 * by the port. */ 17 #if ( portSTACK_GROWTH < 0 ) 18 { 19 /* 往前走4個位元組,因為這個棧是先存值,再移動指標,所以需要確保棧頂指標指向棧空間的最後一個有效位置 */ 20 pxTopOfStack = &( pxNewTCB->pxStack[ uxStackDepth - ( configSTACK_DEPTH_TYPE ) 1 ] ); 21 /* 棧需要對齊 */ 22 pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); 23 24 /* Check the alignment of the calculated top of stack is correct. */ 25 configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0U ) ); 26 } 27 #endif 28 29 /* Store the task name in the TCB. */ 30 if( pcName != NULL ) 31 { 32 for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) 33 { 34 pxNewTCB->pcTaskName[ x ] = pcName[ x ]; 35 36 /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than 37 * configMAX_TASK_NAME_LEN characters just in case the memory after the 38 * string is not accessible (extremely unlikely). */ 39 if( pcName[ x ] == ( char ) 0x00 ) 40 { 41 break; 42 } 43 else 44 { 45 mtCOVERAGE_TEST_MARKER(); 46 } 47 } 48 49 /* Ensure the name string is terminated in the case that the string length 50 * was greater or equal to configMAX_TASK_NAME_LEN. */ 51 pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1U ] = '\0'; 52 } 53 else 54 { 55 mtCOVERAGE_TEST_MARKER(); 56 } 57 58 /* This is used as an array index so must ensure it's not too large. */ 59 configASSERT( uxPriority < configMAX_PRIORITIES ); 60 61 if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) 62 { 63 uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; 64 } 65 else 66 { 67 mtCOVERAGE_TEST_MARKER(); 68 } 69 70 pxNewTCB->uxPriority = uxPriority; 71 #if ( configUSE_MUTEXES == 1 ) 72 { 73 pxNewTCB->uxBasePriority = uxPriority; /* 用於優先順序繼承機制 */ 74 } 75 #endif /* configUSE_MUTEXES */ 76 77 vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); 78 vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); 79 80 /* Set the pxNewTCB as a link back from the ListItem_t. This is so we can get 81 * back to the containing TCB from a generic item in a list. */ 82 /* 設定ListItem_t的持有者,這樣可以從Item處獲取到TCB */ 83 listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); 84 85 /* Event lists are always in priority order. */ 86 /* 事件列表總是按優先順序排序,具體的可以等解析事件的時候再會過來看為什麼這麼設定 */ 87 listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); 88 listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); 89 90 /* Initialize the TCB stack to look as if the task was already running, 91 * but had been interrupted by the scheduler. The return address is set 92 * to the start of the task function. Once the stack has been initialised 93 * the top of stack variable is updated. */ 94 #if ( portUSING_MPU_WRAPPERS == 0 ) 95 { 96 /* If the port has capability to detect stack overflow, 97 * pass the stack end address to the stack initialization 98 * function as well. */ 99 #if ( portHAS_STACK_OVERFLOW_CHECKING == 0 ) 100 { 101 /* 初始化棧,偽造一個現場 */ 102 pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); 103 } 104 #endif /* portHAS_STACK_OVERFLOW_CHECKING */ 105 } 106 #endif /* portUSING_MPU_WRAPPERS */ 107 108 if( pxCreatedTask != NULL ) 109 { 110 /* Pass the handle out in an anonymous way. The handle can be used to 111 * change the created task's priority, delete the created task, etc.*/ 112 *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; 113 } 114 else 115 { 116 mtCOVERAGE_TEST_MARKER(); 117 } 118 }
4.2.4 加入到就緒列表--prvAddNewTaskToReadyList
1 static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB ) 2 { 3 /* Ensure interrupts don't access the task lists while the lists are being 4 * updated. */ 5 /* 會操作任務列表,所以需要進入臨界區,防止有系統中斷打斷 */ 6 taskENTER_CRITICAL(); 7 { 8 uxCurrentNumberOfTasks = ( UBaseType_t ) ( uxCurrentNumberOfTasks + 1U ); 9 10 if( pxCurrentTCB == NULL ) 11 { 12 /* There are no other tasks, or all the other tasks are in 13 * the suspended state - make this the current task. */ 14 pxCurrentTCB = pxNewTCB; 15 16 if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) 17 { 18 /* This is the first task to be created so do the preliminary 19 * initialisation required. We will not recover if this call 20 * fails, but we will report the failure. */ 21 /* 第一個任務被建立,需要先初始化任務列表,具體流程後面會解析 */ 22 prvInitialiseTaskLists(); 23 } 24 else 25 { 26 mtCOVERAGE_TEST_MARKER(); 27 } 28 } 29 else 30 { 31 /* If the scheduler is not already running, make this task the 32 * current task if it is the highest priority task to be created 33 * so far. */ 34 /* 35 如果排程器未被執行且這是至今為止建立的最高優先順序的任務,則把此任務作為當前任務 */ 36 if( xSchedulerRunning == pdFALSE ) 37 { 38 if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority ) 39 { 40 pxCurrentTCB = pxNewTCB; 41 } 42 else 43 { 44 mtCOVERAGE_TEST_MARKER(); 45 } 46 } 47 else 48 { 49 mtCOVERAGE_TEST_MARKER(); 50 } 51 } 52 53 uxTaskNumber++; 54 55 prvAddTaskToReadyList( pxNewTCB ); // 真正的操作列表,放進去 56 57 portSETUP_TCB( pxNewTCB ); 58 } 59 taskEXIT_CRITICAL(); 60 61 if( xSchedulerRunning != pdFALSE ) 62 { 63 /* If the created task is of a higher priority than the current task 64 * then it should run now. */ 65 /* 如果建立的任務優先順序比當前任務高,則yield */ 66 taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxNewTCB ); 67 } 68 else 69 { 70 mtCOVERAGE_TEST_MARKER(); 71 } 72 }
4.2.5 初始化任務列表--prvInitialiseTaskLists
1 static void prvInitialiseTaskLists( void ) 2 { 3 UBaseType_t uxPriority; 4 5 /* 初始化所有的任務列表,包括就緒、阻塞、停止列表等 */ 6 for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ ) 7 { 8 vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) ); 9 } 10 11 vListInitialise( &xDelayedTaskList1 ); 12 vListInitialise( &xDelayedTaskList2 ); 13 vListInitialise( &xPendingReadyList ); 14 15 #if ( INCLUDE_vTaskDelete == 1 ) 16 { 17 vListInitialise( &xTasksWaitingTermination ); 18 } 19 #endif /* INCLUDE_vTaskDelete */ 20 21 #if ( INCLUDE_vTaskSuspend == 1 ) 22 { 23 vListInitialise( &xSuspendedTaskList ); 24 } 25 #endif /* INCLUDE_vTaskSuspend */ 26 27 /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList 28 * using list2. */ 29 pxDelayedTaskList = &xDelayedTaskList1; 30 pxOverflowDelayedTaskList = &xDelayedTaskList2; 31 }
補充一下流程圖:
到這裡,建立任務所涉及到的介面都已解析過了,可能還遺留有一些小問題,但我相信在後面的解析過程中,這些問題都會有所解答。
下次開始任務刪除相關的介面解析了。