【FreeRTOS源码阅读】 task.c(2)任务的调度

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

 

状态转换关系如下:

【FreeRTOS源码阅读】 task.c(2)任务的调度_第1张图片

上下文切换函数:

	在调用该函数前,内核已经将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

 

你可能感兴趣的:(FreeRTOS)