调度机制概述
FreeRTOS的默认调度机制具有以下特点:
优先级不同
- 高优先级的任务优先执行,可以抢占低优先级的任务;
- 高优先级的任务不停止,低优先级的任务永远无法执行;
- 同等优先级的任务轮流执行(时间片轮转)
状态
- 运行态
- 就绪态
- 阻塞态
- 暂停态
通过链表深入理解调度机制
任务创建时,任务被加入就绪态链表尾部,具体流程如下所示:
上述示例中,实际创建了4个任务,xTaskStartScheduler函数会创建优先级为0的空闲任务。pxReadyTaskLists是一个链表数组,大小与优先级的数量相同。创建任务时,会根据任务的优先级把任务添加进相应链表的尾部。上述示例的链表情况如下所示:
执行时,从数组的高地址向低地址查找任务,并从链表的头部逐个取出任务进行执行。
任务的切换与Tick和PendSV中断有关,最终的切换在PendSV中断通过调用vTaskSwitchContext函数完成,与tick中断相关的函数是xTaskIncrementTick。相关代码如下:
__asm void xPortPendSVHandler( void )
{
extern uxCriticalNesting;
extern pxCurrentTCB;
extern vTaskSwitchContext;
/* *INDENT-OFF* */
PRESERVE8
mrs r0, psp
isb
ldr r3, =pxCurrentTCB /* Get the location of the current TCB. */
ldr r2, [ r3 ]
stmdb r0 !, { r4 - r11 } /* Save the remaining registers. */
str r0, [ r2 ] /* Save the new top of stack into the first member of the TCB. */
stmdb sp !, { r3, r14 }
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
msr basepri, r0
dsb
isb
bl vTaskSwitchContext
mov r0, #0
msr basepri, r0
ldmia sp !, { r3, r14 }
ldr r1, [ r3 ]
ldr r0, [ r1 ] /* The first item in pxCurrentTCB is the task top of stack. */
ldmia r0 !, { r4 - r11 } /* Pop the registers and the critical nesting count. */
msr psp, r0
isb
bx r14
nop
/* *INDENT-ON* */
}
第一个执行的任务为优先级最高中的最后创建的任务,原因如下:
// static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
if( xSchedulerRunning == pdFALSE )
{
if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
{
pxCurrentTCB = pxNewTCB;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
状态的切换就是把任务放进不同的链表的中,如调用vTaskDelay后,任务将会被移进pxDelayedTaskList等链表。