以下转载自安富莱电子: http://forum.armfly.com/forum.php
本章教程为大家将介绍 FreeRTOS 操作系统支持的任务调度方式:抢占式,时间片和合作式,这部分
算是 FreeRTOS 操作系统的核心了。 对于初学者来说,要一下子就能够理解这些比较困难,需要多花些时
间把这些基本概念搞清楚,然后阅读下源码,深入理解实现方法。
关于合作式调度器的特别说明
FreeRTOS 支持的调度方式
FreeRTOS 操作系统支持三种调度方式:抢占式调度,时间片调度和合作式调度。 实际应用主要是
抢占式调度和时间片调度,合作式调度用到的很少。
抢占式调度
每个任务都有不同的优先级,任务会一直运行直到被高优先级任务抢占或者遇到阻塞式的 API 函数,
比如 vTaskDelay。
时间片调度
每个任务都有相同的优先级,任务会运行固定的时间片个数或者遇到阻塞式的 API 函数,比如
vTaskDelay,才会执行同优先级任务之间的任务切换。
什么是调度器
简单的说,调度器就是使用相关的调度算法来决定当前需要执行的任务。所有的调度器有一个共同的
特性:
调度器可以区分就绪态任务和挂起任务(由于延迟,信号量等待,邮箱等待,事件组等待等原因而使
得任务被挂起)。
调度器可以选择就绪态中的一个任务,然后激活它(通过执行这个任务)。 当前正在执行的任务是运
行态的任务。
不同调度器之间最大的区别就是如何分配就绪态任务间的完成时间。
嵌入式实时操作系统的核心就是调度器和任务切换,调度器的核心就是调度算法。任务切换的实现在
不同的嵌入式实时操作系统中区别不大,基本相同的硬件内核架构,任务切换也是相似的。调度算法就有
些区别了。下面我们主要了解一下抢占式调度器和时间片调度器。
抢占式调度器基本概念
在实际的应用中,不同的任务需要不同的响应时间。例如,我们在一个应用中需要使用电机,键盘和
LCD 显示。电机比键盘和 LCD 需要更快速的响应,如果我们使用合作式调度器或者时间片调度,那么电
机将无法得到及时的响应,这时抢占式调度是必须的。
如果使用了抢占式调度,最高优先级的任务一旦就绪,总能得到 CPU 的控制权。 比如,当一个运行
着的任务被其它高优先级的任务抢占,当前任务的 CPU 使用权就被剥夺了,或者说被挂起了,那个高优
先级的任务立刻得到了 CPU 的控制权并运行。 又比如,如果中断服务程序使一个高优先级的任务进入就
绪态,中断完成时,被中断的低优先级任务被挂起,优先级高的那个任务开始运行。
使用抢占式调度器,使得最高优先级的任务什么时候可以得到 CPU 的控制权并运行是可知的,同时
使得任务级响应时间得以最优化。
总的来说,学习抢占式调度要掌握的最关键一点是:每个任务都被分配了不同的优先级,抢占式调度
器会获得就绪列表中优先级最高的任务,并运行这个任务。
FreeRTOS 抢占式调度器的实现
如果用户在 FreeRTOS 的配置文件 FreeRTOSConfig.h 中禁止使用时间片调度, 那么每个任务必须配
置不同的优先级。当 FreeRTOS 多任务启动执行后,基本会按照如下的方式去执行:
首先执行的最高优先级的任务 Task1, Task1 会一直运行直到遇到系统阻塞式的 API 函数,比如延迟,
事件标志等待,信号量等待,Task1 任务会被挂起,也就是释放 CPU 的执行权,让低优先级的任务
得到执行。
FreeRTOS 操作系统继续执行任务就绪列表中下一个最高优先级的任务 Task2,Task2 执行过程中有
两种情况:
Task1由于延迟时间到, 接收到信号量消息等方面的原因, 使得 Task1从挂起状态恢复到就绪态,
在抢占式调度器的作用下,Task2 的执行会被 Task1 抢占。
Task2 会一直运行直到遇到系统阻塞式的 API 函数,比如延迟,事件标志等待,信号量等待, Task2
任务会被挂起,继而执行就绪列表中下一个最高优先级的任务。
如果用户创建了多个任务并且采用抢占式调度器的话,基本都是按照上面两条来执行。 根据抢占式调
度器,当前的任务要么被高优先级任务抢占,要么通过调用阻塞式 API 来释放 CPU 使用权让低优先
级任务执行,没有用户任务执行时就执行空闲任务。
运行条件:
这里仅对抢占式调度进行说明。
创建 3 个任务 Task1,Task2 和 Task3。
Task1 的优先级为 1,Task2 的优先级为 2,Task3 的优先级为 3。 FreeRTOS 操作系统是设置的数值
越小任务优先级越低,故 Task3 的优先级最高,Task1 的优先级最低。
此框图是 FreeRTOS 操作系统运行过程中的一部分。
运行过程描述如下:
此时任务 Task1 在运行中,运行过程中由于 Task2 就绪,在抢占式调度器的作用下任务 Task2 抢占
Task1 的执行。 Task2 进入到运行态,Task1 由运行态进入到就绪态。
任务 Task2 在运行中,运行过程中由于 Task3 就绪,在抢占式调度器的作用下任务 Task3 抢占 Task2
的执行。 Task3 进入到运行态,Task2 由运行态进入到就绪态。
任务 Task3 运行过程中调用了阻塞式 API 函数,比如 vTaskDelay,任务 Task3 被挂起,在抢占式调
度器的作用下查找到下一个要执行的最高优先级任务是 Task2,任务 Task2 由就绪态进入到运行态。
任务 Task2 在运行中,运行过程中由于 Task3 再次就绪,在抢占式调度器的作用下任务 Task3 抢占
Task2 的执行。 Task3 进入到运行态,Task2 由运行态进入到就绪态。
上面就是一个简单的不同优先级任务通过抢占式调度进行任务调度和任务切换的过程。
时间片调度器基本概念
在小型的嵌入式 RTOS 中,最常用的的时间片调度算法就是 Round-robin 调度算法。这种调度算法
可以用于抢占式或者合作式的多任务中。另外,时间片调度适合用于不要求任务实时响应的情况。
实现 Round-robin 调度算法需要给同优先级的任务分配一个专门的列表,用于记录当前就绪的任务,
并为每个任务分配一个时间片(也就是需要运行的时间长度,时间片用完了就进行任务切换)。
FreeRTOS 时间片调度器的实现
在 FreeRTOS 操作系统中只有同优先级任务才会使用时间片调度,另外还需要用户在
FreeRTOSConfig.h 文件中使能宏定义:
#define configUSE_TIME_SLICING 1
默认情况下,此宏定义已经在 FreeRTOS.h 文件里面使能了,用户可以不用在 FreeRTOSConfig.h 文
件中再单独使能。
下面我们通过如下的框图来说明一下时间片调度在 FreeRTOS 中的运行过程,让大家有一个形象的认识。
运行条件:
这里仅对时间片调度进行说明。
创建 4 个同优先级任务 Task1,Task2,Task3 和 Task4。
每个任务分配的时间片大小是 5 个系统时钟节拍。
运行过程描述如下:
先运行任务 Task1,运行够 5 个系统时钟节拍后,通过时间片调度切换到任务 Task2。
任务 Task2 运行够 5 个系统时钟节拍后,通过时间片调度切换到任务 Task3。
任务 Task3 在运行期间调用了阻塞式 API 函数,调用函数时,虽然 5 个系统时钟节拍的时间片大小
还没有用完,此时依然会通过时间片调度切换到下一个任务 Task4。 (注意,没有用完的时间片不会
再使用,下次任务 Task3 得到执行还是按照 5 个系统时钟节拍运行)
任务 Task4 运行够 5 个系统时钟节拍后,通过时间片调度切换到任务 Task1。
上面就是一个简单的同优先级任务通过时间片调度进行任务调度和任务切换的过程。
Summary:
时间片调度和抢占式调度我们一般都是开启了的,这样优先级相同时,使用时间片调度,优先级不同时,使用抢占式调度。默认情况下,在freertos.h中使能了时间片调度:
而抢占式调度需要我们用户自己开启,一般在freertosconfig.h中使能: