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 }
xTaskAbortDelay

 这个接口主要就是强制将在阻塞态的任务变成就绪态。

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 }
xTaskIncrementTick

   接口比较复杂,流程图就不贴了,太长。这个接口简单来讲就是如果调度器未停止,就检查每个阻塞的任务是否到了等待时间,包括本身延迟的时间和等待事件的超时时间,并加入到就绪列表中。

 

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 }
vTaskSwitchContext

  最核心的就是“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 }
vTaskPlaceOnEventList

  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 }
vTaskPlaceOnUnorderedEventList

  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 }
xTaskRemoveFromEventList

 

  16~19都比较简单,这里就简单过一下,下一篇学习空闲任务究竟做了什么事情以及它调用的几个小接口。

  下篇再见。