freeRTOS源码解析4--tasks.c 6
cnblogs 2024-09-30 08:15:00 阅读 73
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都比较简单,这里就简单过一下,下一篇学习空闲任务究竟做了什么事情以及它调用的几个小接口。
下篇再见。
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。