freeRTOS源码解析4--tasks.c 3
4.2.6 任务删除--vTaskDelete
这个接口并不复杂,主要是在判断是否要放到xTasksWaitingTermination列表里,还是直接处理。
1 void vTaskDelete( TaskHandle_t xTaskToDelete ) 2 { 3 TCB_t * pxTCB; 4 BaseType_t xDeleteTCBInIdleTask = pdFALSE; 5 BaseType_t xTaskIsRunningOrYielding; 6 7 taskENTER_CRITICAL(); 8 { 9 /* If null is passed in here then it is the calling task that is 10 * being deleted. */ 11 /* 如果是NULL就是删除自己,否则是删除其他任务 */ 12 pxTCB = prvGetTCBFromHandle( xTaskToDelete ); 13 14 /* Remove task from the ready/delayed list. */ 15 if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) 16 { 17 taskRESET_READY_PRIORITY( pxTCB->uxPriority ); 18 } 19 else 20 { 21 mtCOVERAGE_TEST_MARKER(); 22 } 23 24 /* Is the task waiting on an event also? */ 25 if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) 26 { 27 ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); 28 } 29 else 30 { 31 mtCOVERAGE_TEST_MARKER(); 32 } 33 34 /* Increment the uxTaskNumber also so kernel aware debuggers can 35 * detect that the task lists need re-generating. This is done before 36 * portPRE_TASK_DELETE_HOOK() as in the Windows port that macro will 37 * not return. */ 38 /* 这个变量是用于TRACE相关的,调试用的,先不管,以后看trace的代码再解析 */ 39 uxTaskNumber++; 40 41 /* Use temp variable as distinct sequence points for reading volatile 42 * variables prior to a logical operator to ensure compliance with 43 * MISRA C 2012 Rule 13.5. */ 44 /* 确定是否正在运行或正在调度到这个任务 */ 45 xTaskIsRunningOrYielding = taskTASK_IS_RUNNING_OR_SCHEDULED_TO_YIELD( pxTCB ); 46 47 /* If the task is running (or yielding), we must add it to the 48 * termination list so that an idle task can delete it when it is 49 * no longer running. */ 50 if( ( xSchedulerRunning != pdFALSE ) && ( xTaskIsRunningOrYielding != pdFALSE ) ) 51 { 52 /* A running task or a task which is scheduled to yield is being 53 * deleted. This cannot complete when the task is still running 54 * on a core, as a context switch to another task is required. 55 * Place the task in the termination list. The idle task will check 56 * the termination list and free up any memory allocated by the 57 * scheduler for the TCB and stack of the deleted task. */ 58 /* 看注释,不放到终止列表中,无法删除。需要依赖空闲任务来处理 59 * 具体原因暂时还不清楚,看来需要阅读更多源码才能明白。 */ 60 vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) ); 61 62 /* Increment the ucTasksDeleted variable so the idle task knows 63 * there is a task that has been deleted and that it should therefore 64 * check the xTasksWaitingTermination list. */ 65 ++uxDeletedTasksWaitingCleanUp; 66 67 /* Delete the task TCB in idle task. */ 68 xDeleteTCBInIdleTask = pdTRUE; 69 70 /* The pre-delete hook is primarily for the Windows simulator, 71 * in which Windows specific clean up operations are performed, 72 * after which it is not possible to yield away from this task - 73 * hence xYieldPending is used to latch that a context switch is 74 * required. */ 75 /* 只有在Windows下才有用,先不分析了 */ 76 #if ( configNUMBER_OF_CORES == 1 ) 77 portPRE_TASK_DELETE_HOOK( pxTCB, &( xYieldPendings[ 0 ] ) ); 78 #endif 79 } 80 else 81 { 82 /* 这里是删除阻塞任务(pxDelayedTaskList)的(xTaskIsRunningOrYielding = pdFALSE) */ 83 --uxCurrentNumberOfTasks; 84 85 /* Reset the next expected unblock time in case it referred to 86 * the task that has just been deleted. */ 87 /* 主要用于更新最久阻塞的剩余阻塞时间,防止最近需要运行的就是删除的这个任务 */ 88 prvResetNextTaskUnblockTime(); 89 } 90 } 91 taskEXIT_CRITICAL(); 92 93 /* If the task is not deleting itself, call prvDeleteTCB from outside of 94 * critical section. If a task deletes itself, prvDeleteTCB is called 95 * from prvCheckTasksWaitingTermination which is called from Idle task. */ 96 /* 如果删除的是当前运行的任务在空闲任务中处理,阻塞任务在这里处理 */ 97 if( xDeleteTCBInIdleTask != pdTRUE ) 98 { 99 prvDeleteTCB( pxTCB ); 100 } 101 102 /* Force a reschedule if it is the currently running task that has just 103 * been deleted. */ 104 /* 如果删除的是当前运行的任务,需要调度,这很明显。 */ 105 #if ( configNUMBER_OF_CORES == 1 ) 106 { 107 if( xSchedulerRunning != pdFALSE ) 108 { 109 if( pxTCB == pxCurrentTCB ) 110 { 111 configASSERT( uxSchedulerSuspended == 0 ); 112 taskYIELD_WITHIN_API(); 113 } 114 else 115 { 116 mtCOVERAGE_TEST_MARKER(); 117 } 118 } 119 } 120 #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ 121 }
1 static void prvDeleteTCB( TCB_t * pxTCB ) 2 { 3 #if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) ) 4 { 5 /* The task can only have been allocated dynamically - free both 6 * the stack and TCB. */ 7 vPortFreeStack( pxTCB->pxStack ); 8 vPortFree( pxTCB ); 9 } 10 #elif ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) 11 { 12 /* The task could have been allocated statically or dynamically, so 13 * check what was statically allocated before trying to free the 14 * memory. */ 15 /* 按需释放tcb和stack */ 16 if( pxTCB->ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ) 17 { 18 /* Both the stack and TCB were allocated dynamically, so both 19 * must be freed. */ 20 vPortFreeStack( pxTCB->pxStack ); 21 vPortFree( pxTCB ); 22 } 23 else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY ) 24 { 25 /* Only the stack was statically allocated, so the TCB is the 26 * only memory that must be freed. */ 27 vPortFree( pxTCB ); 28 } 29 else 30 { 31 /* Neither the stack nor the TCB were allocated dynamically, so 32 * nothing needs to be freed. */ 33 configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB ); 34 mtCOVERAGE_TEST_MARKER(); 35 } 36 } 37 #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ 38 }
4.2.7 获取任务状态--eTaskGetState
这个接口可以大致看出任务在哪些情况下会处于什么状态。
首先是返回值的说明:
1 typedef enum 2 { 3 eRunning = 0, /* 运行态. */ 4 eReady, /* 就绪态, 任务在 ready or pending ready 列表. */ 5 eBlocked, /* 阻塞态. */ 6 eSuspended, /* 挂起态, 任务在 挂起列表, 或处于无限延迟的阻塞中. */ 7 eDeleted, /* 等待删除. */ 8 eInvalid /* Used as an 'invalid state' value. */ 9 } eTaskState;
1 eTaskState eTaskGetState( TaskHandle_t xTask ) 2 { 3 eTaskState eReturn; 4 List_t const * pxStateList; 5 List_t const * pxEventList; 6 List_t const * pxDelayedList; 7 List_t const * pxOverflowedDelayedList; 8 const TCB_t * const pxTCB = xTask; 9 10 configASSERT( pxTCB ); 11 12 #if ( configNUMBER_OF_CORES == 1 ) 13 if( pxTCB == pxCurrentTCB ) 14 { 15 /* The task calling this function is querying its own state. */ 16 /* 当前任务是查询的任务,则处于运行态 */ 17 eReturn = eRunning; 18 } 19 else 20 #endif 21 { 22 taskENTER_CRITICAL(); 23 { 24 pxStateList = listLIST_ITEM_CONTAINER( &( pxTCB->xStateListItem ) ); 25 pxEventList = listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ); 26 pxDelayedList = pxDelayedTaskList; 27 pxOverflowedDelayedList = pxOverflowDelayedTaskList; 28 } 29 taskEXIT_CRITICAL(); 30 31 if( pxEventList == &xPendingReadyList ) 32 { 33 /* The task has been placed on the pending ready list, so its 34 * state is eReady regardless of what list the task's state list 35 * item is currently placed on. */ 36 /* 任务的事件项处于xPendingReadyList中就是就绪态 37 * 说明xPendingReadyList里放的是任务的xEventListItem 38 * 调度器挂起时,任务已ready,调度器运行后, 39 * 会将xPendingReadyList中的任务移至就绪列表中 */ 40 eReturn = eReady; 41 } 42 else if( ( pxStateList == pxDelayedList ) || ( pxStateList == pxOverflowedDelayedList ) ) 43 { 44 /* The task being queried is referenced from one of the Blocked 45 * lists. */ 46 /* pxDelayedTaskList和pxOverflowDelayedTaskList放的是阻塞任务,xStateListItem */ 47 eReturn = eBlocked; 48 } 49 50 #if ( INCLUDE_vTaskSuspend == 1 ) 51 /* xSuspendedTaskList放的是挂起的任务,xStateListItem */ 52 else if( pxStateList == &xSuspendedTaskList ) 53 { 54 /* The task being queried is referenced from the suspended 55 * list. Is it genuinely suspended or is it blocked 56 * indefinitely? */ 57 /* xEventListItem不在任何列表里 */ 58 if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ) 59 { 60 #if ( configUSE_TASK_NOTIFICATIONS == 1 ) 61 { 62 BaseType_t x; 63 64 /* The task does not appear on the event list item of 65 * and of the RTOS objects, but could still be in the 66 * blocked state if it is waiting on its notification 67 * rather than waiting on an object. If not, is 68 * suspended. */ 69 /* 说明等通知不是把xEventListItem放到某个列表里, 70 * 而且等通知会把任务放到挂起的列表里 */ 71 eReturn = eSuspended; 72 73 for( x = ( BaseType_t ) 0; x < ( BaseType_t ) configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ ) 74 { 75 if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION ) 76 { 77 eReturn = eBlocked; 78 break; 79 } 80 } 81 } 82 #else /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ 83 { 84 eReturn = eSuspended; 85 } 86 #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ 87 } 88 else 89 { 90 /* 如果xStateListItem在挂起列表里且xEventListItem处于某个列表里,则任务会阻塞 */ 91 eReturn = eBlocked; 92 } 93 } 94 #endif /* if ( INCLUDE_vTaskSuspend == 1 ) */ 95 96 #if ( INCLUDE_vTaskDelete == 1 ) 97 else if( ( pxStateList == &xTasksWaitingTermination ) || ( pxStateList == NULL ) ) 98 { 99 /* The task being queried is referenced from the deleted 100 * tasks list, or it is not referenced from any lists at 101 * all. */ 102 /* 等着被删除,这很明显 */ 103 eReturn = eDeleted; 104 } 105 #endif 106 107 else 108 { 109 #if ( configNUMBER_OF_CORES == 1 ) 110 { 111 /* If the task is not in any other state, it must be in the 112 * Ready (including pending ready) state. */ 113 /* 剩下的就只能是在就绪列表里(pxReadyTasksLists) 114 * pxReadyTasksLists是个列表数组,通过排除法,可以 115 * 免于进行遍历操作。 */ 116 eReturn = eReady; 117 } 118 #else /* #if ( configNUMBER_OF_CORES == 1 ) */ 119 { 120 if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE ) 121 { 122 /* Is it actively running on a core? */ 123 eReturn = eRunning; 124 } 125 else 126 { 127 /* If the task is not in any other state, it must be in the 128 * Ready (including pending ready) state. */ 129 eReturn = eReady; 130 } 131 } 132 #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ 133 } 134 } 135 136 /* 简单总结下:xStateListItem会在pxDelayedTaskList、pxOverflowDelayedTaskList、 137 * xSuspendedTaskList和xTasksWaitingTermination里 138 * xEventListItem会在xPendingReadyList里。 139 * 简单分析就是事件、通知来了任务就放到xPendingReadyList里,其他情况就在其他列表里 140 * 具体是不是这样,看后面的源码,会逐渐清晰 */ 141 142 return eReturn; 143 }
4.2.8 任务延迟,释放CPU--vTaskDelay
这个接口比较熟悉,至少是任务中最常用的接口了。但是正如接口的注释所说的那样,这个接口不提供准确的延迟时间,任务唤醒的周期会受中断、其他任务的影响,并且计时起始于这个接口开始被调用的时候,且时间是大于等于给予的延迟时间。如果想要准确的周期性的任务,那就要调用xTaskDelayUntil接口,这个会在后面讲到。
1 void vTaskDelay( const TickType_t xTicksToDelay ) 2 { 3 BaseType_t xAlreadyYielded = pdFALSE; 4 5 /* A delay time of zero just forces a reschedule. */ 6 /* 如果延迟tick为0, 则表示强制执行调度 */ 7 if( xTicksToDelay > ( TickType_t ) 0U ) 8 { 9 vTaskSuspendAll(); // 停止调度器 10 { 11 configASSERT( uxSchedulerSuspended == 1U ); 12 13 /* A task that is removed from the event list while the 14 * scheduler is suspended will not get placed in the ready 15 * list or removed from the blocked list until the scheduler 16 * is resumed. 17 * 18 * This task cannot be in an event list as it is the currently 19 * executing task. */ 20 /* 在调度器暂停时, 从事件列表中删除的任务将不会被放置在就绪列表中 21 * 或从阻塞列表中删除, 直到调度器恢复。此任务不可能在事件列表中, 22 * 因为它是当前正在执行的任务。 */ 23 prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE ); 24 } 25 xAlreadyYielded = xTaskResumeAll(); 26 } 27 else 28 { 29 mtCOVERAGE_TEST_MARKER(); 30 } 31 32 /* Force a reschedule if xTaskResumeAll has not already done so, we may 33 * have put ourselves to sleep. */ 34 /* xTaskResumeAll没有调度, 那在这里手动调度 */ 35 if( xAlreadyYielded == pdFALSE ) 36 { 37 taskYIELD_WITHIN_API(); 38 } 39 else 40 { 41 mtCOVERAGE_TEST_MARKER(); 42 } 43 } 44 45 void vTaskSuspendAll( void ) 46 { 47 #if ( configNUMBER_OF_CORES == 1 ) 48 { 49 /* A critical section is not required as the variable is of type 50 * BaseType_t. Please read Richard Barry's reply in the following link to a 51 * post in the FreeRTOS support forum before reporting this as a bug! - 52 * https://goo.gl/wu4acr */ 53 54 /* portSOFTWARE_BARRIER() is only implemented for emulated/simulated ports that 55 * do not otherwise exhibit real time behaviour. */ 56 portSOFTWARE_BARRIER(); 57 58 /* The scheduler is suspended if uxSchedulerSuspended is non-zero. An increment 59 * is used to allow calls to vTaskSuspendAll() to nest. */ 60 /* uxSchedulerSuspended非0, 则调度器暂停, 加1是为了嵌套调用(中断也调用) */ 61 uxSchedulerSuspended = ( UBaseType_t ) ( uxSchedulerSuspended + 1U ); 62 63 /* Enforces ordering for ports and optimised compilers that may otherwise place 64 * the above increment elsewhere. */ 65 portMEMORY_BARRIER(); 66 } 67 #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ 68 } 69 70 static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, 71 const BaseType_t xCanBlockIndefinitely ) 72 { 73 TickType_t xTimeToWake; 74 const TickType_t xConstTickCount = xTickCount; 75 List_t * const pxDelayedList = pxDelayedTaskList; 76 List_t * const pxOverflowDelayedList = pxOverflowDelayedTaskList; 77 78 #if ( INCLUDE_xTaskAbortDelay == 1 ) 79 { 80 /* About to enter a delayed list, so ensure the ucDelayAborted flag is 81 * reset to pdFALSE so it can be detected as having been set to pdTRUE 82 * when the task leaves the Blocked state. */ 83 pxCurrentTCB->ucDelayAborted = ( uint8_t ) pdFALSE; 84 } 85 #endif 86 87 /* Remove the task from the ready list before adding it to the blocked list 88 * as the same list item is used for both lists. */ 89 /* 将当前任务移出的列表必然是就绪列表, 移出后的就绪列表的item数量为0, 表示 90 * 就绪列表为空了, 需要重置下uxTopReadyPriority */ 91 if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) 92 { 93 /* The current task must be in a ready list, so there is no need to 94 * check, and the port reset macro can be called directly. */ 95 portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); 96 } 97 else 98 { 99 mtCOVERAGE_TEST_MARKER(); 100 } 101 102 #if ( INCLUDE_vTaskSuspend == 1 ) 103 { 104 if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) ) 105 { 106 /* Add the task to the suspended task list instead of a delayed task 107 * list to ensure it is not woken by a timing event. It will block 108 * indefinitely. */ 109 /* 无限等待, 就加入到挂起列表里 */ 110 listINSERT_END( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) ); 111 } 112 else 113 { 114 /* Calculate the time at which the task should be woken if the event 115 * does not occur. This may overflow but this doesn't matter, the 116 * kernel will manage it correctly. */ 117 /* 计算延迟结束的tick */ 118 xTimeToWake = xConstTickCount + xTicksToWait; 119 120 /* The list item will be inserted in wake time order. */ 121 /* 说明xStateListItem的项里的值, 放的是wake的时间点 */ 122 listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake ); 123 124 if( xTimeToWake < xConstTickCount ) 125 { 126 /* Wake time has overflowed. Place this item in the overflow 127 * list. */ 128 /* 处理数值溢出的情况, 说明wake的tick值溢出的话, 任务就在pxOverflowDelayedTaskList 129 * 列表里, 这是freeRTOS处理tick溢出的方式, 即用两个delay列表交替使用来处理 */ 130 traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST(); 131 vListInsert( pxOverflowDelayedList, &( pxCurrentTCB->xStateListItem ) ); 132 } 133 else 134 { 135 /* The wake time has not overflowed, so the current block list 136 * is used. */ 137 /* 没溢出就放到pxDelayedTaskList列表里, 一般一旦有溢出, 后面的任务 138 * 都是处于溢出的情况, 一旦tick加到0了, 就会交换pxDelayedTaskList 139 * 和pxOverflowDelayedTaskList指向的列表, 这样就解决了溢出的问题 */ 140 traceMOVED_TASK_TO_DELAYED_LIST(); 141 vListInsert( pxDelayedList, &( pxCurrentTCB->xStateListItem ) ); 142 143 /* If the task entering the blocked state was placed at the 144 * head of the list of blocked tasks then xNextTaskUnblockTime 145 * needs to be updated too. */ 146 /* 更新最近唤醒任务的时间 */ 147 if( xTimeToWake < xNextTaskUnblockTime ) 148 { 149 xNextTaskUnblockTime = xTimeToWake; 150 } 151 else 152 { 153 mtCOVERAGE_TEST_MARKER(); 154 } 155 } 156 } 157 } 158 #endif /* INCLUDE_vTaskSuspend */ 159 }
vTaskDelay的流程图如下。
接着是vTaskDelay调用的几个接口,除了xTaskIncrementTick接口没有放进来,这个接口比较重要和复杂,后面专门会进行解析。
4.2.8.1 将当前任务加入到延迟列表--prvAddCurrentTaskToDelayedList
1 static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, 2 const BaseType_t xCanBlockIndefinitely ) 3 { 4 TickType_t xTimeToWake; 5 const TickType_t xConstTickCount = xTickCount; 6 List_t * const pxDelayedList = pxDelayedTaskList; 7 List_t * const pxOverflowDelayedList = pxOverflowDelayedTaskList; 8 9 #if ( INCLUDE_xTaskAbortDelay == 1 ) 10 { 11 /* About to enter a delayed list, so ensure the ucDelayAborted flag is 12 * reset to pdFALSE so it can be detected as having been set to pdTRUE 13 * when the task leaves the Blocked state. */ 14 pxCurrentTCB->ucDelayAborted = ( uint8_t ) pdFALSE; 15 } 16 #endif 17 18 /* Remove the task from the ready list before adding it to the blocked list 19 * as the same list item is used for both lists. */ 20 /* 将当前任务移出的列表必然是就绪列表, 移出后的就绪列表的item数量为0, 表示 21 * 就绪列表为空了, 需要重置下uxTopReadyPriority */ 22 if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) 23 { 24 /* The current task must be in a ready list, so there is no need to 25 * check, and the port reset macro can be called directly. */ 26 portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); 27 } 28 else 29 { 30 mtCOVERAGE_TEST_MARKER(); 31 } 32 33 #if ( INCLUDE_vTaskSuspend == 1 ) 34 { 35 if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) ) 36 { 37 /* Add the task to the suspended task list instead of a delayed task 38 * list to ensure it is not woken by a timing event. It will block 39 * indefinitely. */ 40 /* 无限等待, 就加入到挂起列表里 */ 41 listINSERT_END( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) ); 42 } 43 else 44 { 45 /* Calculate the time at which the task should be woken if the event 46 * does not occur. This may overflow but this doesn't matter, the 47 * kernel will manage it correctly. */ 48 /* 计算延迟结束的tick */ 49 xTimeToWake = xConstTickCount + xTicksToWait; 50 51 /* The list item will be inserted in wake time order. */ 52 /* 说明xStateListItem的项里的值, 放的是wake的时间点 */ 53 listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake ); 54 55 if( xTimeToWake < xConstTickCount ) 56 { 57 /* Wake time has overflowed. Place this item in the overflow 58 * list. */ 59 /* 处理数值溢出的情况, 说明wake的tick值溢出的话, 任务就在pxOverflowDelayedTaskList 60 * 列表里, 这是freeRTOS处理tick溢出的方式, 即用两个delay列表交替使用来处理 */ 61 traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST(); 62 vListInsert( pxOverflowDelayedList, &( pxCurrentTCB->xStateListItem ) ); 63 } 64 else 65 { 66 /* The wake time has not overflowed, so the current block list 67 * is used. */ 68 /* 没溢出就放到pxDelayedTaskList列表里, 一般一旦有溢出, 后面的任务 69 * 都是处于溢出的情况, 一旦tick加到0了, 就会交换pxDelayedTaskList 70 * 和pxOverflowDelayedTaskList指向的列表, 这样就解决了溢出的问题 */ 71 traceMOVED_TASK_TO_DELAYED_LIST(); 72 vListInsert( pxDelayedList, &( pxCurrentTCB->xStateListItem ) ); 73 74 /* If the task entering the blocked state was placed at the 75 * head of the list of blocked tasks then xNextTaskUnblockTime 76 * needs to be updated too. */ 77 /* 更新最近唤醒任务的时间 */ 78 if( xTimeToWake < xNextTaskUnblockTime ) 79 { 80 xNextTaskUnblockTime = xTimeToWake; 81 } 82 else 83 { 84 mtCOVERAGE_TEST_MARKER(); 85 } 86 } 87 } 88 } 89 #endif /* INCLUDE_vTaskSuspend */ 90 }
4.2.8.2 停止所有任务(停止调度器)--vTaskSuspendAll
1 void vTaskSuspendAll( void ) 2 { 3 #if ( configNUMBER_OF_CORES == 1 ) 4 { 5 /* A critical section is not required as the variable is of type 6 * BaseType_t. Please read Richard Barry's reply in the following link to a 7 * post in the FreeRTOS support forum before reporting this as a bug! - 8 * https://goo.gl/wu4acr */ 9 10 /* portSOFTWARE_BARRIER() is only implemented for emulated/simulated ports that 11 * do not otherwise exhibit real time behaviour. */ 12 portSOFTWARE_BARRIER(); 13 14 /* The scheduler is suspended if uxSchedulerSuspended is non-zero. An increment 15 * is used to allow calls to vTaskSuspendAll() to nest. */ 16 /* uxSchedulerSuspended非0, 则调度器暂停, 加1是为了嵌套调用(中断也调用) */ 17 uxSchedulerSuspended = ( UBaseType_t ) ( uxSchedulerSuspended + 1U ); 18 19 /* Enforces ordering for ports and optimised compilers that may otherwise place 20 * the above increment elsewhere. */ 21 portMEMORY_BARRIER(); 22 } 23 #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ 24 }
4.2.8.3 继续所有任务(继续调度器)--xTaskResumeAll
这个接口返回的是否已经调度过。
1 BaseType_t xTaskResumeAll( void ) 2 { 3 TCB_t * pxTCB = NULL; 4 BaseType_t xAlreadyYielded = pdFALSE; 5 6 { 7 /* It is possible that an ISR caused a task to be removed from an event 8 * list while the scheduler was suspended. If this was the case then the 9 * removed task will have been added to the xPendingReadyList. Once the 10 * scheduler has been resumed it is safe to move all the pending ready 11 * tasks from this list into their appropriate ready list. */ 12 taskENTER_CRITICAL(); 13 { 14 BaseType_t xCoreID; 15 xCoreID = ( BaseType_t ) portGET_CORE_ID(); // 单核默认返回0 16 17 /* If uxSchedulerSuspended is zero then this function does not match a 18 * previous call to vTaskSuspendAll(). */ 19 /* vTaskSuspendAll和xTaskResumeAll成对调用, 所以前面必然是调用过了 20 * vTaskSuspendAll, 那么uxSchedulerSuspended必然大于0的 */ 21 configASSERT( uxSchedulerSuspended != 0U ); 22 23 uxSchedulerSuspended = ( UBaseType_t ) ( uxSchedulerSuspended - 1U ); 24 portRELEASE_TASK_LOCK(); 25 26 if( uxSchedulerSuspended == ( UBaseType_t ) 0U ) 27 { 28 if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U ) 29 { 30 /* Move any readied tasks from the pending list into the 31 * appropriate ready list. */ 32 /* 为什么是xPendingReadyList, 因为调用vTaskSuspendAll后调度器暂停了, 33 * 如果在调用本接口前(本接口会进入临界区, 临界区会禁止所有系统中断) 34 * 有任务就绪了(中断、事件、通知等), 就会把任务放xPendingReadyList列表里 */ 35 while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE ) 36 { 37 /* 把xPendingReadyList里的所有任务一个一个放到就绪列表中 */ 38 pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); 39 listREMOVE_ITEM( &( pxTCB->xEventListItem ) ); 40 portMEMORY_BARRIER(); 41 listREMOVE_ITEM( &( pxTCB->xStateListItem ) ); 42 prvAddTaskToReadyList( pxTCB ); 43 44 #if ( configNUMBER_OF_CORES == 1 ) 45 { 46 /* If the moved task has a priority higher than the current 47 * task then a yield must be performed. */ 48 /* 如果取出来的任务有优先级比当前任务高的, 就需要调度, 先置标志位 */ 49 if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) 50 { 51 xYieldPendings[ xCoreID ] = pdTRUE; 52 } 53 else 54 { 55 mtCOVERAGE_TEST_MARKER(); 56 } 57 } 58 #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ 59 } 60 61 if( pxTCB != NULL ) 62 { 63 /* A task was unblocked while the scheduler was suspended, 64 * which may have prevented the next unblock time from being 65 * re-calculated, in which case re-calculate it now. Mainly 66 * important for low power tickless implementations, where 67 * this can prevent an unnecessary exit from low power 68 * state. */ 69 /* 任务在调度器暂停时解除阻塞, 立即计算可以防止下一个解除阻塞 70 * 时间被重新计算, 这对于低功耗无障碍实现非常重要,因为这可以 71 * 防止不必要地退出低功耗 */ 72 prvResetNextTaskUnblockTime(); 73 } 74 75 /* If any ticks occurred while the scheduler was suspended then 76 * they should be processed now. This ensures the tick count does 77 * not slip, and that any delayed tasks are resumed at the correct 78 * time. 79 * 80 * It should be safe to call xTaskIncrementTick here from any core 81 * since we are in a critical section and xTaskIncrementTick itself 82 * protects itself within a critical section. Suspending the scheduler 83 * from any core causes xTaskIncrementTick to increment uxPendedCounts. */ 84 /* 在调度器停止的时候, tick增加是增加xPendedTicks这个值, 在这里恢复调度器 85 * 的时候需要把这些增加的xPendedTicks值处理掉, 在临界区调用xTaskIncrementTick 86 * 是安全的, 至于为什么在解析xTaskIncrementTick后应该会清楚 */ 87 { 88 TickType_t xPendedCounts = xPendedTicks; /* Non-volatile copy. */ 89 90 if( xPendedCounts > ( TickType_t ) 0U ) 91 { 92 do 93 { 94 if( xTaskIncrementTick() != pdFALSE ) 95 { 96 /* Other cores are interrupted from 97 * within xTaskIncrementTick(). */ 98 xYieldPendings[ xCoreID ] = pdTRUE; 99 } 100 else 101 { 102 mtCOVERAGE_TEST_MARKER(); 103 } 104 105 --xPendedCounts; 106 } while( xPendedCounts > ( TickType_t ) 0U ); 107 108 xPendedTicks = 0; 109 } 110 else 111 { 112 mtCOVERAGE_TEST_MARKER(); 113 } 114 } 115 116 /* 需要调度的话, 这里立即开始调度(实际应该是 117 * 等退出临界区后进入pendSV中断), 我暂时也不确定, 118 * 等有空了仿真调式看看 */ 119 if( xYieldPendings[ xCoreID ] != pdFALSE ) 120 { 121 #if ( configUSE_PREEMPTION != 0 ) 122 { 123 xAlreadyYielded = pdTRUE; 124 } 125 #endif /* #if ( configUSE_PREEMPTION != 0 ) */ 126 127 #if ( configNUMBER_OF_CORES == 1 ) 128 { 129 taskYIELD_TASK_CORE_IF_USING_PREEMPTION( pxCurrentTCB ); 130 } 131 #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ 132 } 133 else 134 { 135 mtCOVERAGE_TEST_MARKER(); 136 } 137 } 138 } 139 else 140 { 141 mtCOVERAGE_TEST_MARKER(); 142 } 143 } 144 taskEXIT_CRITICAL(); 145 } 146 147 return xAlreadyYielded; 148 }
这篇就解析到这里,里面有不少遗留的问题,相信在逐步解析的过程中会慢慢解决的。下一篇开始解析xTaskDelayUntil和vTaskPrioritySet等接口,一些简单的接口如uxTaskBasePriorityGet这些,应该会选一个举例,其他的可能就一笔带过了。
那么,下一篇再见。