freeRTOS源码解析4--tasks.c 6
4.2.14 退出阻塞--xTaskAbortDelay
接口:
BaseType_t xTaskAbortDelay( TaskHandle_t xTask )
形参1:xTask ,想要退出阻塞态的任务;
返回:pdPASS:退出成功;pdFAIL:退出失败。
1 BaseType_t xTaskAbortDelay( TaskHandle_t xTask ) 2 { 3 TCB_t * pxTCB = xTask; 4 BaseType_t xReturn; 5 6 configASSERT( pxTCB ); 7 8 vTaskSuspendAll(); 9 { 10 /* A task can only be prematurely removed from the Blocked state if 11 * it is actually in the Blocked state. */ 12 /* 任务只有真的在阻塞态才能提前移出阻塞态 */ 13 if( eTaskGetState( xTask ) == eBlocked ) 14 { 15 xReturn = pdPASS; 16 17 /* Remove the reference to the task from the blocked list. An 18 * interrupt won't touch the xStateListItem because the 19 * scheduler is suspended. */ 20 ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); 21 22 /* Is the task waiting on an event also? If so remove it from 23 * the event list too. Interrupts can touch the event list item, 24 * even though the scheduler is suspended, so a critical section 25 * is used. */ 26 /* 如果任务在等待事件, 则移出事件列表, 因为调度器暂停时, 中断仍 27 * 能访问事件列表, 所以需要进入临界区 */ 28 taskENTER_CRITICAL(); 29 { 30 if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) 31 { 32 ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); 33 34 /* This lets the task know it was forcibly removed from the 35 * blocked state so it should not re-evaluate its block time and 36 * then block again. */ 37 /* 让任务知道是被强制移出阻塞态的, 防止被重新计算阻塞时间再次进入 38 * 阻塞态 */ 39 pxTCB->ucDelayAborted = ( uint8_t ) pdTRUE; 40 } 41 else 42 { 43 mtCOVERAGE_TEST_MARKER(); 44 } 45 } 46 taskEXIT_CRITICAL(); 47 48 /* Place the unblocked task into the appropriate ready list. */ 49 prvAddTaskToReadyList( pxTCB ); 50 51 /* A task being unblocked cannot cause an immediate context 52 * switch if preemption is turned off. */ 53 #if ( configUSE_PREEMPTION == 1 ) 54 { 55 #if ( configNUMBER_OF_CORES == 1 ) 56 { 57 /* Preemption is on, but a context switch should only be 58 * performed if the unblocked task has a priority that is 59 * higher than the currently executing task. */ 60 /* 比当前任务优先级高, 等调度器运行后, 需要进行yield */ 61 if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) 62 { 63 /* Pend the yield to be performed when the scheduler 64 * is unsuspended. */ 65 xYieldPendings[ 0 ] = pdTRUE; 66 } 67 else 68 { 69 mtCOVERAGE_TEST_MARKER(); 70 } 71 } 72 #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ 73 } 74 #endif /* #if ( configUSE_PREEMPTION == 1 ) */ 75 } 76 else 77 { 78 xReturn = pdFAIL; 79 } 80 } 81 ( void ) xTaskResumeAll(); 82 83 return xReturn; 84 }
这个接口主要就是强制将在阻塞态的任务变成就绪态。
4.2.15 系统滴答时钟处理--xTaskIncrementTick
接口:
BaseType_t xTaskIncrementTick( void )
返回:pdPASS:需要切换上下文;pdFAIL:不需要切换上下文。
接口代码如下:
1 BaseType_t xTaskIncrementTick( void ) 2 { 3 TCB_t * pxTCB; 4 TickType_t xItemValue; 5 BaseType_t xSwitchRequired = pdFALSE; 6 7 /* Tick increment should occur on every kernel timer event. Core 0 has the 8 * responsibility to increment the tick, or increment the pended ticks if the 9 * scheduler is suspended. If pended ticks is greater than zero, the core that 10 * calls xTaskResumeAll has the responsibility to increment the tick. */ 11 if( uxSchedulerSuspended == ( UBaseType_t ) 0U ) 12 { 13 /* Minor optimisation. The tick count cannot change in this 14 * block. */ 15 const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1; 16 17 /* Increment the RTOS tick, switching the delayed and overflowed 18 * delayed lists if it wraps to 0. */ 19 xTickCount = xConstTickCount; 20 21 /* tick溢出了, 交换下delay列表 */ 22 if( xConstTickCount == ( TickType_t ) 0U ) 23 { 24 taskSWITCH_DELAYED_LISTS(); 25 } 26 else 27 { 28 mtCOVERAGE_TEST_MARKER(); 29 } 30 31 /* See if this tick has made a timeout expire. Tasks are stored in 32 * the queue in the order of their wake time - meaning once one task 33 * has been found whose block time has not expired there is no need to 34 * look any further down the list. */ 35 /* 因为delay列表的项是按唤醒时间从小到大排序的, 所以遍历过程中一旦发现 36 * 任务唤醒时间没到, 就可以停止了 */ 37 if( xConstTickCount >= xNextTaskUnblockTime ) 38 { 39 for( ; ; ) 40 { 41 if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) 42 { 43 /* The delayed list is empty. Set xNextTaskUnblockTime 44 * to the maximum possible value so it is extremely 45 * unlikely that the 46 * if( xTickCount >= xNextTaskUnblockTime ) test will pass 47 * next time through. */ 48 xNextTaskUnblockTime = portMAX_DELAY; 49 break; 50 } 51 else 52 { 53 /* The delayed list is not empty, get the value of the 54 * item at the head of the delayed list. This is the time 55 * at which the task at the head of the delayed list must 56 * be removed from the Blocked state. */ 57 /* MISRA Ref 11.5.3 [Void pointer assignment] */ 58 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ 59 /* coverity[misra_c_2012_rule_11_5_violation] */ 60 pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); 61 xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) ); 62 63 if( xConstTickCount < xItemValue ) 64 { 65 /* It is not time to unblock this item yet, but the 66 * item value is the time at which the task at the head 67 * of the blocked list must be removed from the Blocked 68 * state - so record the item value in 69 * xNextTaskUnblockTime. */ 70 /* 这个任务未到唤醒时间, 但因为前面的都移出了, 所以这一定 71 * 是下次最近唤醒的时间, 记录下来 */ 72 xNextTaskUnblockTime = xItemValue; 73 break; 74 } 75 else 76 { 77 mtCOVERAGE_TEST_MARKER(); 78 } 79 80 /* It is time to remove the item from the Blocked state. */ 81 listREMOVE_ITEM( &( pxTCB->xStateListItem ) ); 82 83 /* Is the task waiting on an event also? If so remove 84 * it from the event list. */ 85 /* 等待事件的话, 移出事件列表 */ 86 if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) 87 { 88 listREMOVE_ITEM( &( pxTCB->xEventListItem ) ); 89 } 90 else 91 { 92 mtCOVERAGE_TEST_MARKER(); 93 } 94 95 /* Place the unblocked task into the appropriate ready 96 * list. */ 97 prvAddTaskToReadyList( pxTCB ); 98 99 /* A task being unblocked cannot cause an immediate 100 * context switch if preemption is turned off. */ 101 #if ( configUSE_PREEMPTION == 1 ) 102 { 103 #if ( configNUMBER_OF_CORES == 1 ) 104 { 105 /* Preemption is on, but a context switch should 106 * only be performed if the unblocked task's 107 * priority is higher than the currently executing 108 * task. 109 * The case of equal priority tasks sharing 110 * processing time (which happens when both 111 * preemption and time slicing are on) is 112 * handled below.*/ 113 /* 移出的任务优先级比当前运行的任务高, 需要上下文切换 */ 114 if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) 115 { 116 xSwitchRequired = pdTRUE; 117 } 118 else 119 { 120 mtCOVERAGE_TEST_MARKER(); 121 } 122 } 123 #endif /* #if( configNUMBER_OF_CORES == 1 ) */ 124 } 125 #endif /* #if ( configUSE_PREEMPTION == 1 ) */ 126 } 127 } 128 } 129 130 /* Tasks of equal priority to the currently running task will share 131 * processing time (time slice) if preemption is on, and the application 132 * writer has not explicitly turned time slicing off. */ 133 /* 相同优先级的任务用时间片运行, 每个时间片长是一个tick. 这里不用管更高 134 * 优先级的任务是否唤醒, 一是上面已经判断过了, 二是只是需要知道是否要上 135 * 下文切换, 这样判断就足够了, 在上下文切换的时候自然会选择合适的任务运行 */ 136 #if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) 137 { 138 #if ( configNUMBER_OF_CORES == 1 ) 139 { 140 if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > 1U ) 141 { 142 xSwitchRequired = pdTRUE; 143 } 144 else 145 { 146 mtCOVERAGE_TEST_MARKER(); 147 } 148 } 149 #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ 150 } 151 #endif /* #if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */ 152 153 #if ( configUSE_TICK_HOOK == 1 ) 154 { 155 /* Guard against the tick hook being called when the pended tick 156 * count is being unwound (when the scheduler is being unlocked). */ 157 if( xPendedTicks == ( TickType_t ) 0 ) 158 { 159 vApplicationTickHook(); 160 } 161 else 162 { 163 mtCOVERAGE_TEST_MARKER(); 164 } 165 } 166 #endif /* configUSE_TICK_HOOK */ 167 168 #if ( configUSE_PREEMPTION == 1 ) 169 { 170 #if ( configNUMBER_OF_CORES == 1 ) 171 { 172 /* For single core the core ID is always 0. */ 173 if( xYieldPendings[ 0 ] != pdFALSE ) 174 { 175 xSwitchRequired = pdTRUE; 176 } 177 else 178 { 179 mtCOVERAGE_TEST_MARKER(); 180 } 181 } 182 #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ 183 } 184 #endif /* #if ( configUSE_PREEMPTION == 1 ) */ 185 } 186 else 187 { 188 /* 调度器处于暂停中, 在xTaskResumeAll接口中检查xPendedTicks的值, 189 * 并调用本接口处理tick值. */ 190 xPendedTicks += 1U; 191 192 /* The tick hook gets called at regular intervals, even if the 193 * scheduler is locked. */ 194 #if ( configUSE_TICK_HOOK == 1 ) 195 { 196 vApplicationTickHook(); 197 } 198 #endif 199 } 200 201 return xSwitchRequired; 202 }
接口比较复杂,流程图就不贴了,太长。这个接口简单来讲就是如果调度器未停止,就检查每个阻塞的任务是否到了等待时间,包括本身延迟的时间和等待事件的超时时间,并加入到就绪列表中。
4.2.16 切换上下文--vTaskSwitchContext
切换上下文,主要作用是挑选出需要切换过去的任务。
接口:
void vTaskSwitchContext( void )
接口代码如下:
1 void vTaskSwitchContext( void ) 2 { 3 if( uxSchedulerSuspended != ( UBaseType_t ) 0U ) 4 { 5 /* The scheduler is currently suspended - do not allow a context 6 * switch. */ 7 /* 调度器暂停时, 不允许上下文切换 */ 8 xYieldPendings[ 0 ] = pdTRUE; 9 } 10 else 11 { 12 xYieldPendings[ 0 ] = pdFALSE; 13 14 /* Check for stack overflow, if configured. */ 15 /* 就是检查当前栈的位置有没有超过栈顶位置 */ 16 taskCHECK_FOR_STACK_OVERFLOW(); 17 18 /* Before the currently running task is switched out, save its errno. */ 19 #if ( configUSE_POSIX_ERRNO == 1 ) 20 { 21 pxCurrentTCB->iTaskErrno = FreeRTOS_errno; 22 } 23 #endif 24 25 /* Select a new task to run using either the generic C or port 26 * optimised asm code. */ 27 /* MISRA Ref 11.5.3 [Void pointer assignment] */ 28 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ 29 /* coverity[misra_c_2012_rule_11_5_violation] */ 30 /* 选择就绪列表中优先级最高的任务 */ 31 taskSELECT_HIGHEST_PRIORITY_TASK(); 32 33 /* Macro to inject port specific behaviour immediately after 34 * switching tasks, such as setting an end of stack watchpoint 35 * or reconfiguring the MPU. */ 36 portTASK_SWITCH_HOOK( pxCurrentTCB ); 37 38 /* After the new task is switched in, update the global errno. */ 39 #if ( configUSE_POSIX_ERRNO == 1 ) 40 { 41 FreeRTOS_errno = pxCurrentTCB->iTaskErrno; 42 } 43 #endif 44 } 45 }
最核心的就是“taskSELECT_HIGHEST_PRIORITY_TASK();”调用。
4.2.17 加入到事件列表--vTaskPlaceOnEventList
把当前任务放到事件列表中,主要用于队列的实现。
接口:
void vTaskPlaceOnEventList( List_t * const pxEventList, const TickType_t xTicksToWait )
形参1:pxEventList,事件列表;
形参2:xTicksToWait ,最长等待时间。
接口代码如下:
1 void vTaskPlaceOnEventList( List_t * const pxEventList, 2 const TickType_t xTicksToWait ) 3 { 4 configASSERT( pxEventList ); 5 6 /* THIS FUNCTION MUST BE CALLED WITH THE 7 * SCHEDULER SUSPENDED AND THE QUEUE BEING ACCESSED LOCKED. */ 8 9 /* Place the event list item of the TCB in the appropriate event list. 10 * This is placed in the list in priority order so the highest priority task 11 * is the first to be woken by the event. 12 * 13 * Note: Lists are sorted in ascending order by ListItem_t.xItemValue. 14 * Normally, the xItemValue of a TCB's ListItem_t members is: 15 * xItemValue = ( configMAX_PRIORITIES - uxPriority ) 16 * Therefore, the event list is sorted in descending priority order. 17 * 18 * The queue that contains the event list is locked, preventing 19 * simultaneous access from interrupts. */ 20 /* 将当前任务加入到事件列表, 列表是按升序存放的, 而一般情况下, 任务的 21 * xEventListItem的值是xItemValue=(configMAX_PRIORITIES-uxPriority), 22 * 所以事件列表是按优先级降序排列的 */ 23 vListInsert( pxEventList, &( pxCurrentTCB->xEventListItem ) ); 24 25 /* 还需要放到延迟列表中, 用于最大延迟时间的唤醒 */ 26 prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); 27 }
4.2.18 加入到无序事件列表--vTaskPlaceOnUnorderedEventList
把当前任务放到无序的事件列表中,主要用于事件组。
接口:
void vTaskPlaceOnUnorderedEventList( List_t * pxEventList,const TickType_t xItemValue, const TickType_t xTicksToWait )
形参1:pxEventList,事件列表;
形参2:xItemValue,设置的值
形参3:xTicksToWait ,最长等待时间。
接口代码如下:
1 void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, 2 const TickType_t xItemValue, 3 const TickType_t xTicksToWait ) 4 { 5 configASSERT( pxEventList ); 6 7 /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by 8 * the event groups implementation. */ 9 configASSERT( uxSchedulerSuspended != ( UBaseType_t ) 0U ); 10 11 /* Store the item value in the event list item. It is safe to access the 12 * event list item here as interrupts won't access the event list item of a 13 * task that is not in the Blocked state. */ 14 /* 中断只会访问处于Blocked态的事件列表项, 这里当前任务明显还未进入Blocked态 */ 15 listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ), xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); 16 17 /* Place the event list item of the TCB at the end of the appropriate event 18 * list. It is safe to access the event list here because it is part of an 19 * event group implementation - and interrupts don't access event groups 20 * directly (instead they access them indirectly by pending function calls to 21 * the task level). */ 22 /* 主要是用于事件组的实现, 中断不会直接访问. 无序存放, 所以直接放在最后 */ 23 listINSERT_END( pxEventList, &( pxCurrentTCB->xEventListItem ) ); 24 25 prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); 26 }
4.2.19 移出事件列表--xTaskRemoveFromEventList
将当前任务从事件列表中移出,主要用于队列的实现。
接口:
BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList )
形参1:pxEventList,事件列表;
返回值:pdTRUE:需要进行上下文切换;pdFALSE:不需要。
接口代码如下:
1 BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) 2 { 3 TCB_t * pxUnblockedTCB; 4 BaseType_t xReturn; 5 6 /* THIS FUNCTION MUST BE CALLED FROM A CRITICAL SECTION. It can also be 7 * called from a critical section within an ISR. */ 8 9 /* The event list is sorted in priority order, so the first in the list can 10 * be removed as it is known to be the highest priority. Remove the TCB from 11 * the delayed list, and add it to the ready list. 12 * 13 * If an event is for a queue that is locked then this function will never 14 * get called - the lock count on the queue will get modified instead. This 15 * means exclusive access to the event list is guaranteed here. 16 * 17 * This function assumes that a check has already been made to ensure that 18 * pxEventList is not empty. */ 19 /* MISRA Ref 11.5.3 [Void pointer assignment] */ 20 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ 21 /* coverity[misra_c_2012_rule_11_5_violation] */ 22 /* 按优先级降序存放的, 所以头部是优先级最高的任务 */ 23 pxUnblockedTCB = listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); 24 configASSERT( pxUnblockedTCB ); 25 listREMOVE_ITEM( &( pxUnblockedTCB->xEventListItem ) ); 26 27 if( uxSchedulerSuspended == ( UBaseType_t ) 0U ) 28 { 29 listREMOVE_ITEM( &( pxUnblockedTCB->xStateListItem ) ); 30 prvAddTaskToReadyList( pxUnblockedTCB ); 31 32 #if ( configUSE_TICKLESS_IDLE != 0 ) 33 { 34 /* If a task is blocked on a kernel object then xNextTaskUnblockTime 35 * might be set to the blocked task's time out time. If the task is 36 * unblocked for a reason other than a timeout xNextTaskUnblockTime is 37 * normally left unchanged, because it is automatically reset to a new 38 * value when the tick count equals xNextTaskUnblockTime. However if 39 * tickless idling is used it might be more important to enter sleep mode 40 * at the earliest possible time - so reset xNextTaskUnblockTime here to 41 * ensure it is updated at the earliest possible time. */ 42 /* 重置最近唤醒时间 */ 43 prvResetNextTaskUnblockTime(); 44 } 45 #endif 46 } 47 else 48 { 49 /* The delayed and ready lists cannot be accessed, so hold this task 50 * pending until the scheduler is resumed. */ 51 /* 调度器暂停, 不能访问延迟和就绪列表, 需要放到pend列表里 */ 52 listINSERT_END( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) ); 53 } 54 55 #if ( configNUMBER_OF_CORES == 1 ) 56 { 57 if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority ) 58 { 59 /* Return true if the task removed from the event list has a higher 60 * priority than the calling task. This allows the calling task to know if 61 * it should force a context switch now. */ 62 /* 取出任务的优先级更高, 需要进行上下文切换 */ 63 xReturn = pdTRUE; 64 65 /* Mark that a yield is pending in case the user is not using the 66 * "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ 67 xYieldPendings[ 0 ] = pdTRUE; 68 } 69 else 70 { 71 xReturn = pdFALSE; 72 } 73 } 74 #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ 75 76 return xReturn; 77 }
16~19都比较简单,这里就简单过一下,下一篇学习空闲任务究竟做了什么事情以及它调用的几个小接口。
下篇再见。