FreeRTOS的任务调度有抢占、时间片轮询。抢占存在于不同任务优先级,时间片轮询是在同等优先级的任务。
任务的状态有:运行、就绪、打断、挂起、删除
对应任务的状态的有以下几个链表:pxReadyTasksLists[ uxPriority ]、xPendingReadyList、pxDelayedTaskList、pxOverflowDelayedTaskList、xSuspendedTaskList、xTasksWaitingTermination
各种链表对应的TCB中ListItem的关系如下表:
pxReadyTasksLists | xPendingReadyList | pxDelayedTaskList | pxOverflowDelayedTaskList | xSuspendedTaskList | xTasksWaitingTermination |
xGenericListItem | xEventListItem | xGenericListItem | xGenericListItem | xGenericListItem | xGenericListItem |
1、在任务刚创建时,会将任务添加至该链表 2、当任务的优先级更改时,如果任务之前已经在此链表,需要将其移除,然后根据最新的优先级在添加到此链表 3、如果任务被挂起时,调用TaskResume,将会将指定的任务添加到对应优先级的链表中 4、当调用xTaskResumeAll时,将PendingReadyList中的所有任务都添加到对应优先级的ReadyTaskList中去 5、在SystemTick中的xTaskIncrementTick增加TickCount时,检测到任务的Delay时间到,会将任务添加到对应优先级的ReadyTaskList 6、当任务因为等待操作(发送、接受、信号量互斥)队列是被阻塞挂起时,此时条件满足后(等待时间到、有数据可以接收、有空间可以发送)会将任务的从Delay、Suspend的链表中去除,然后添加到对用优先级的ReadyTaskList |
1、当调度器被挂起时,任务状态将要变成Ready(就绪)状态时,会将任务添加到xPendingReadyList中。 当调度器运行以后,这些被添加到该链表中的任务将会被添加到ReadyTaskList 此时的GenericListItem被挂在DelayTaskList、SuspendedTaskList 2、当任务因为等待操作(发送、接受、信号量互斥)队列时,由于QueueIsFull when send or QueueisEmpty when receive任务的EventListItem将会被改在该队列,任务的TCB的GenericListItem将根据延时时间来判断挂到哪个链表;Wait Time = port_MAX_TIME则会把 TCB->GenericListItem 挂到 SuspendedTaskList,如果WaitTime != port_MAX_TIME则会吧TCB->GenericListItem挂在DelayedTaskList |
1、当任务因为等待操作(发送、接受、信号量互斥)队列时,由于QueueIsFull when send or QueueisEmpty when receive任务的EventListItem将会被改在该队列,任务的TCB的GenericListItem将根据延时时间来判断挂到哪个链表;Wait Time = port_MAX_TIME则会把 TCB->GenericListItem 挂到 SuspendedTaskList,如果WaitTime != port_MAX_TIME则会吧TCB->GenericListItem挂在DelayedTaskList 2、当调用TaskDelay函数时,当前TCB->GenericListItem将会被挂在DelayedTaskList |
1、当调用TaskDelay函数、Queue block task时,当前TickCount + DelayTimeCount 的结果溢出,则会将当前TCB->GenericListItem 挂在pxOverflowDelayedTaskList | 1、在调用void vTaskSuspend( TaskHandle_t xTaskToSuspend )时,将指定任务TCB->GenericListItem挂在SuspendedTaskList 2、当任务因为等待操作(发送、接受、信号量互斥)队列时,由于QueueIsFull when send or QueueisEmpty when receive任务的EventListItem将会被改在该队列,任务的TCB的GenericListItem将根据延时时间来判断挂到哪个链表;Wait Time = port_MAX_TIME则会把 TCB->GenericListItem 挂到 SuspendedTaskList,如果WaitTime != port_MAX_TIME则会吧TCB->GenericListItem挂在DelayedTaskList |
1、当调用void vTaskDelete( TaskHandle_t xTaskToDelete ),当前任务TCB->GenericListItem将会被挂在xTasksWaitingTermination |
TaskControlBlock->EvenListItem用于表示当前任务时候在等待信号或者表示任务是否有什么异常;当任务在等待信号量、向队列发送数据、从队列接收数据时,当前条件不满足是会将TCB->EventListItem 插入到 对应信号量(其实也是队列)等待队列中
TaskControlBlock->GenericListItem用于表示任务的当前状态:ReadyTaskList、SuspendedTaskList、DelayedTaskList、WaitTerminateTaskList
状态转换关系如下:
上下文切换函数:
在调用该函数前,内核已经将xPSR\PC\LR\R12\R3\R2\R1\R0自动push到PSP中
extern uxCriticalNesting;
extern pxCurrentTCB;
extern vTaskSwitchContext;
PRESERVE8
/*< 加载当前非中断栈顶指针到r0 >*/
mrs r0, psp
isb
/*< 加载当前TCB的地址到r3 >*/
ldr r3, =pxCurrentTCB /* Get the location of the current TCB. */
/*< 因为TCB模块的第一个变量是当前TCB的栈顶指针,所以这句话就是获取当前的TCB栈顶指针到 r2 >*/
ldr r2, [r3]
/*< 将r4-r11依次保存在当前运行的任务的栈中,与此同时将r0 的地址也随之改变 > */
stmdb r0!, {r4-r11} /* Save the remaining registers. */
/*< 将此时栈顶指针当前TCB的首地址,也就是TCB中存放栈顶指针的位置 >*/
str r0, [r2] /* Save the new top of stack into the first member of the TCB. */
/*< 将R3和R14临时压入堆栈,因为即将调用函数vTaskSwitchContext,调用函数时,返回地址自动保存到R14中,所以一旦调用发生,R14的值会被覆盖,因此需要入栈保护; R3保存的当前激活的任务TCB指针(pxCurrentTCB)地址,函数调用后会用到,因此也要入栈保护 >*/
stmdb sp!, {r3, r14}
/*< 进入临界区 >*/
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
msr basepri, r0
/*< 获取最高优先级任务的TCB >*/
bl vTaskSwitchContext
/*< 退出临界区 >*/
mov r0, #0
msr basepri, r0
/*< 恢复r3 r14 >*/
ldmia sp!, {r3, r14}
/*< 获取最新TCB >*/
ldr r1, [r3]
/*< 加载最新的TCB任务的栈顶指针 >*/
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