freeRTOS源码解析4--tasks.c 2
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 }
补充一下流程图:
到这里,创建任务所涉及到的接口都已解析过了,可能还遗留有一些小问题,但我相信在后面的解析过程中,这些问题都会有所解答。
下次开始任务删除相关的接口解析了。