FreeRTOS任务调度机制
1、通过就绪态(ready)、阻塞态链表(delay)、挂起态链表(suspend)进行任务调度。
链表定义
/* 就绪、阻塞、挂起任务链表. --------------------*/
PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ] = {0}; /*< Prioritised ready tasks. */
PRIVILEGED_DATA static List_t xDelayedTaskList1 = {0}; /*< Delayed tasks. */
PRIVILEGED_DATA static List_t xDelayedTaskList2 = {0}; /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */
PRIVILEGED_DATA static List_t xPendingReadyList = {0}; /*< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready list when the scheduler is resumed. */
1、当优先级相同(优先级大于0)的任务创建时,先执行最后一个创建的任务,然后再执行第一个创建的任务。(如果优先级都为0,则先执行空闲任务,然后执行第一个创建的任务);完成任务创建后OS进行同等优先级任务轮流执行(空闲任务礼让一次,让其它任务先运行一个tick)。
2、示例
xTaskCreate(vTask1,"Task1",100,NULL,0,NULL);
xTaskCreate(vTask2,"Task1",100,NULL,0,NULL);
xTaskCreate(vTask3,"Task1",100,NULL,0,NULL);
xTaskCreate(IDEL_task,"Idle",100,NULL,0,NULL); //空闲任务
在任务优先级同为0的pxReadyTaskList[4] 链表中
pxReadyTaskList[0] = task1;
pxReadyTaskList[1] = task2;
pxReadyTaskList[2] = task3;
pxReadyTaskList[3] = Idle task;
则执行顺序为
TCB -> pxReadyTaskList[3];
TCB -> pxReadyTaskList[0];
TCB -> pxReadyTaskList[1];
TCB -> pxReadyTaskList[2];
2、TCB控制块结构体描述
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; /*< 指向任务堆栈中最后一项的位置。这必须是TCB结构体的第一个成员。 */
ListItem_t xStateListItem; /*< 任务的状态列表项引用的列表表示该任务的状态(就绪、阻塞、挂起). */
ListItem_t xEventListItem; /*< 用于从事件列表中引用任务. */
UBaseType_t uxPriority; /*< 任务的优先级。0为最低优先级. */
StackType_t *pxStack; /*<指向堆栈的起点. */
char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< 创建时给任务的描述性名称. Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
} tskTCB;
3、向就绪态任务链表增加任务
就绪态链表结构体
/*
* Place the task represented by pxTCB into the appropriate ready list for
* the task. It is inserted at the end of the list.
*/
#define prvAddTaskToReadyList( pxTCB ) \
traceMOVED_TASK_TO_READY_STATE( pxTCB ); \
taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \
vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )
/*-----------------------------------------------------------*/
// Place the task represented by pxTCB into the appropriate ready list for the task. It is inserted at the end of the list
#define prvAddTaskToReadyList( pxTCB ) \
traceMOVED_TASK_TO_READY_STATE( pxTCB ); \
taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \
vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ]), &( ( pxTCB )->xStateListItem ) ); \
tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )
static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
/* Ensure interrupts don't access the task lists while the lists are being
updated. */
taskENTER_CRITICAL();
{
uxCurrentNumberOfTasks++;
if( pxCurrentTCB == NULL )
{
/* 没有其他任务,或者所有其他任务都在
挂起状态——将其作为当前任务 */
pxCurrentTCB = pxNewTCB;
if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
{
/* 这是要创建的第一个任务,因此需要进行初步初始化。
如果调用失败,我们将无法恢复,但我们将报告失败. */
prvInitialiseTaskLists();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
/* 如果调度器尚未运行,如果该任务是迄今要创建的最高
优先级任务,则将其设置为当前任务. */
if( xSchedulerRunning == pdFALSE )
{
if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
{
pxCurrentTCB = pxNewTCB;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
uxTaskNumber++;
#if ( configUSE_TRACE_FACILITY == 1 )
{
/* 在TCB中添加计数器仅用于跟踪. */
pxNewTCB->uxTCBNumber = uxTaskNumber;
}
#endif /* configUSE_TRACE_FACILITY */
traceTASK_CREATE( pxNewTCB );
prvAddTaskToReadyList( pxNewTCB );
portSETUP_TCB( pxNewTCB );
}
taskEXIT_CRITICAL();
if( xSchedulerRunning != pdFALSE )
{
/*如果创建的任务的优先级高于当前任务,高优先级任务先运行. */
if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
{
taskYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
4、向阻塞态任务链表增加任务
阻塞态链表结构体
/* pxDelayedTaskList and pxOverflowDelayedTaskList are switched when the tick
count overflows. */
#define taskSWITCH_DELAYED_LISTS() \
{ \
List_t *pxTemp; \
\
/* The delayed tasks list should be empty when the lists are switched. */ \
configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) ); \
\
pxTemp = pxDelayedTaskList; \
pxDelayedTaskList = pxOverflowDelayedTaskList; \
pxOverflowDelayedTaskList = pxTemp; \
xNumOfOverflows++; \
prvResetNextTaskUnblockTime(); \
}
static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely )
{
TickType_t xTimeToWake;
const TickType_t xConstTickCount = xTickCount;
#if( INCLUDE_xTaskAbortDelay == 1 )
{
/* About to enter a delayed list, so ensure the ucDelayAborted flag is
reset to pdFALSE so it can be detected as having been set to pdTRUE
when the task leaves the Blocked state. */
pxCurrentTCB->ucDelayAborted = pdFALSE;
}
#endif
/* 在将任务添加到阻止列表之前,将其从就绪列表中删除
因为两个列表使用相同的列表项 */
if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
/* 当前任务必须在就绪列表中,因此不需要
检查后,可以直接调用端口重置宏. */
portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
#if ( INCLUDE_vTaskSuspend == 1 )
{
if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) )
{
/* 将任务添加到挂起任务列表,而不是延迟任务
列表,以确保它不会被定时事件唤醒。它会无限期地阻塞. */
vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );
}
else
{
/*如果事件发生,计算任务应该被唤醒的时间
不会发生。这可能会溢出,但没关系内核将正确地管理它.
*/
xTimeToWake = xConstTickCount + xTicksToWait;
/* The list item will be inserted in wake time order. */
listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );
if( xTimeToWake < xConstTickCount )
{
/* Wake time has overflowed. Place this item in the overflow
list. */
vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
}
else
{
/* The wake time has not overflowed, so the current block list
is used. */
vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
/* If the task entering the blocked state was placed at the
head of the list of blocked tasks then xNextTaskUnblockTime
needs to be updated too. */
if( xTimeToWake < xNextTaskUnblockTime )
{
xNextTaskUnblockTime = xTimeToWake;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
}
#else /* INCLUDE_vTaskSuspend */
{
/* Calculate the time at which the task should be woken if the event
does not occur. This may overflow but this doesn't matter, the kernel
will manage it correctly. */
xTimeToWake = xConstTickCount + xTicksToWait;
/* The list item will be inserted in wake time order. */
listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );
if( xTimeToWake < xConstTickCount )
{
/* Wake time has overflowed. Place this item in the overflow list. */
vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
}
else
{
/* The wake time has not overflowed, so the current block list is used. */
vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
/* If the task entering the blocked state was placed at the head of the
list of blocked tasks then xNextTaskUnblockTime needs to be updated
too. */
if( xTimeToWake < xNextTaskUnblockTime )
{
xNextTaskUnblockTime = xTimeToWake;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* Avoid compiler warning when INCLUDE_vTaskSuspend is not 1. */
( void ) xCanBlockIndefinitely;
}
#endif /* INCLUDE_vTaskSuspend */
}