第一个任务
FreeRTOS的vTaskStartScheduler()
函数开启了第一个任务。
除去一些初始化代码外,vTaskStartScheduler
通过xPortStartScheduler
调用到vPortStartFirstTask
在ARM中,vPortStartFirstTask
通常是一个汇编函数,主要目的是触发svc
中断
.align 4
vPortStartFirstTask: .asmfunc
; ...
;/* Call SVC to start the first task. */
cpsie i
cpsie f
dsb
isb
; 注意,这里call svc,而不是PendCV
svc #0
.endasmfunc
SVCHandler
是一个简化版的PendCVHandler
,它不需要保存上一个任务的上下文,直接取出任务堆栈地址,跳转。
.align 4
vPortSVCHandler: .asmfunc
;/* Get the location of the current TCB. */
; 取出栈地址,保存到r0
ldr r3, pxCurrentTCBConst
ldr r1, [r3]
ldr r0, [r1]
;/* Pop the core registers. */
; 移除当前堆栈里面的内容
ldmia r0!, {r4-r11}
; 将当前任务栈地址赋值给psp
msr psp, r0
isb
; 开中断
mov r0, #0
msr basepri, r0
orr r14, #0xd
; 跳出中断
bx r14
.endasmfunc
而在任务初始化的时候,FreeRTOS会以任务函数入口为基点,创建第一个挂起点信息块。以ARM CM3的代码为例,会在pxPortInitialiseStack
中看到:
*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */
即将当前任务函数指针,作为pc
压到栈中去。因此SVCHandler
结束后,会返回到第一个任务的函数指针处。main
函数此时已无法再继续了。
任务调度
上文中说到,vTaskSwitchContext
中实现了任务调度,这个函数的主体是taskSELECT_HIGHEST_PRIORITY_TASK
宏。这个宏选择当前优先级最高的可激活(ready)任务,保存进pxCurrentTCB
中去。参考以下代码:
#define taskSELECT_HIGHEST_PRIORITY_TASK() \
{ \
UBaseType_t uxTopPriority = uxTopReadyPriority; \
\
/* Find the highest priority queue that contains ready tasks. */ \
while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) ) \
{ \
configASSERT( uxTopPriority ); \
--uxTopPriority; \
} \
\
/* listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of \
* the same priority get an equal share of the processor time. */ \
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \
uxTopReadyPriority = uxTopPriority; \
} /* taskSELECT_HIGHEST_PRIORITY_TASK */
可见FreeRTOS的任务调度顺序不是在切换任务的时候管理的。让我们再来研究任务的激活过程。在vTaskResume
的代码中可以看到:
void vTaskResume( TaskHandle_t xTaskToResume )
{
// ...
/* The ready list can be accessed even if the scheduler is
* suspended because this is inside a critical section. */
// 从旧list里面移除,加入到ReadyList中。
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
prvAddTaskToReadyList( pxTCB );
// ...
}
prvAddTaskToReadyList
是一个宏,将任务插入到当前优先级激活任务列表的末尾。
#define prvAddTaskToReadyList( pxTCB ) \
traceMOVED_TASK_TO_READY_STATE( pxTCB ); \
taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \
listINSERT_END( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )
因此可以总结,FreeRTOS是根据优先级进行排序,先运行高优先级的激活任务。同一个优先级中,根据激活的时间顺序,先激活的任务先运行。
网友评论