目录
- 1 、什么是任务的调度机制
- 1.1 概念
- 1.2 三种算法
- 1.3 决定算法的宏
- 2、基本词条解释
- 3、调度算法解释
- 3.1 具有时间片的优先级抢先调度 Prioritized Pre-emptive Scheduling with Time Slicing
- 3.1.1 图解高优先级任务抢占低优先级任务
- 3.1.2 图解具有时间片的优先级抢占
- 3.1.3 总结
- 3.2 优先级抢先调度(无时间片)Prioritized Pre-emptive Scheduling (without Time Slicing)
- 3.3 合作调度Co-operative Scheduling
- 4 、总结
1 、什么是任务的调度机制
1.1 概念
调度算法决定了哪个就绪(Ready)状态任务转换到运行(Running)状态。
对于单个内核而言,由于每个时刻,只能有一个任务是实际处于运行状态的,其它任务只能处理运行态以外的其它态(阻塞态blocked,就绪态Ready,暂停态Suspended)。因此,当FreeRTOS在任务调度时必须以一种规则来决定选择哪一个Ready状态的任务来进入Running状态。
1.2 三种算法
FreeRTOS有三种调度算法,分别是:
第一种:具有时间片的优先级抢先调度 Prioritized Pre-emptive Scheduling with Time Slicing
第二种:优先级抢先调度(无时间片)Prioritized Pre-emptive Scheduling (without Time Slicing)
第三种:合作调度Co-operative Scheduling
1.3 决定算法的宏
在FreeRTOSConfig.h配置头文件里有两个宏,分别是 configUSE_PREEMPTION 和 configUSE_TIME_SLICING。这两个宏的取值为1 或 0 共同决定了程序是处于三种调度算法中的哪一种。
宏 | 第一种算法的取值 | 第二种算法的取值 | 第三种算未能的取值 |
---|---|---|---|
configUSE_PREEMPTION | 1 | 1 | 0 |
configUSE_TIME_SLICING | 1 | 0 | 任何值 |
2、基本词条解释
词条 | 解释 |
---|---|
Fixed Priority固定优先级 | 被描述为“固定优先级”的调度算法不会改变分配给被调度任务的优先级,也不会阻止任务本身改变自己或其他任务的优先级。 |
Pre-emptive 抢占 | 如果优先级高于运行状态任务的任务进入就绪Rready状态,抢占式调度算法将立即“抢占”当下在运行状态的任务。 |
Time Slicing 时间片 | 时间片用于在同等优先级的任务之间平均使用处理器时间,。描述为使用“时间片”的调度算法将在每个时间片结束时选择一个新的同优先级的任务进入运行状态。一个时间片等于两个 RTOS tick中断之间的时间 |
3、调度算法解释
3.1 具有时间片的优先级抢先调度 Prioritized Pre-emptive Scheduling with Time Slicing
这是最常用的调度模式。
1、FreeRTOS在每一个tick中断中处理一次任务调度;
2、调度程序总是找到处于就绪Ready状态中的最高优先级任务,并把它切换成运行态去运行。
3、如果,这个最高优先级中有多个任务都处于Ready状态中,那么这些任务会被依次转为运行态去运行,其它的都会被放入Ready就绪状态。每个运行的周期为一个tick周期。
4、当这个最高优先级中没有一个任务处于Ready状态了,也没有处于Running状态了。则调度程序会去查找次一级的优先级中有没有任务处理Ready状态,然后重复上述的调度过程。
5、调度器会产生一个最低优先级(优先级为0)的叫做空闲任务(IDLE Task),用于当没有任何用户任务运行时,来占用CPU时间运行。
3.1.1 图解高优先级任务抢占低优先级任务
以上是“具有时间片的优先级抢先调度”模式的描述,更好的直观理解,见下图:
注意:以上国中中的t1 t2 …t13只是表达了一个时间点,用于示意一定的时间间隔,不是一个严格的tick周期。
图中有四个任务,分别是Task1 具有最高优先级,并是事件触发的,如果事件未触发,则Task1处于阻塞blocked状态。
Task2,是一个优先级低于Task1的,以一个周期定期执行的任务。当时间未到时,任务处理阻塞blocked状态。
Task3,是一个优先级低于Task2的,以事件触发执行的任务,如果事件未触发,则Task3处于阻塞blocked状态。
IDLE Task,是系统的空闲任务,优先级固定为0。
解释:
t1时刻,此时Task2处于运行周期,直至t2时刻运行结束,Task2重新进入阻塞blocked状态。
t2时刻,此时没有其它更高优先级的任务处于Ready状态,只有IDLE空闲任务处于Ready状态,则调茺器在t2时刻调茺IDLE任务进入运行态。
t3时刻,此时Task3被事件唤醒,处于了Ready状态,它的优先级高于正在Running状态的IDLE任务,因此调度程序就调度Task3任务去抢占IDLE任务,使Task3进入Running状态,并切换IDLE任务重回到Ready状态。
t4时刻,Task3任务运行完毕,重新进入阻塞blocked状态,此时没有其它高于优先级0的任务处理Ready状态,则调度器又调度处于Ready状态的IDLE任务进入运行态。
t5时刻,Task3又被事件解发,然后抢占了IDLE任务,进入运行状态。
t6时刻,Task2又到了执行周期,由于它的优先级高于此刻正在运行的Task3,所以调度器调度Task2去抢占Task3进入运行态。则Task3会被调度器切换回Ready状态。
t7时刻,Task2运行完毕,则调度器马上切换当时处于Ready状态的具有更高优先级的Task3任务继续运行。直到t8时刻运行结束。
t8时刻,没有比IDLE更高优先级的任务处于Ready状态了,则调度IDLE任务进入运行态。
t9时刻,Task2又到了运行周期,抢占了IDLE任务。
t10进刻,有最高优先级的Task1被事件解发,进入了Ready状态,则调度器马上调度Task1抢占了还在运行的Task2任务,并把Task2放入Ready状态。
t11时刻,Task1运行结束,重新进入阻塞状态,调度器调度此时的最高优先级处理Ready状态的Task2任务进入运行态。
t12时刻,Task3在这个时刻之前可能已被触发处于Ready状态,但只有等到了t12这个时刻,它才是处于Ready状态的最高优先级任务,所以被调度。
3.1.2 图解具有时间片的优先级抢占
注意:此图中的t1,t2,t3,t4 ,t5, t8, t9,t10等,除了t6与t7以外,都是标准的tick中断。所以时间片等于两个tick中断的间隔周期。
Task1任务具有这里的最高优先级,事件驱动,通常处于阻塞状态。
Task2任务,是一个与IDLE任务同优先级的,处于长期运行状态。
t1时刻,只有IDLE任务持续运行,一直到t2时刻,按时间片的原则,调度程序调期同优先级的task2任务进入running状态。IDLE处于Ready状态。
t2时刻,调度器调度task2任务进入running状态,直到t3时刻,时间片到期。
t3时刻,过程相同,调度器轮流调度Task2与IDLE任务进入running状态。直到t6.
t6时刻,task1被事件触发,进入Ready状态,并启动了调度器抢占了当下的IDLE任务。并在t7时刻完成运行,退回到阻塞状态。调度器再次调度,此时按顺序,在t5-t8这个时间片里,IDLE已运行过,所以调度了task2进入运行状态。直到t8时刻。
t8时刻开始,又进入了轮流调度相同优先级的Task2与IDLE任务进入running状态。
3.1.3 总结
以上两图,详细的解释了具有时间片的情况下,如何调度相同优先级的任务。同一个时间片里,相同优先级任务之间的轮流运行。解释了抢占式调度下,高优先级任务如何抢占低优先级任务。两种情况下是如何相互协调运行。
3.2 优先级抢先调度(无时间片)Prioritized Pre-emptive Scheduling (without Time Slicing)
没有时间分片的优先级抢占式调度保持与上一节中描述的相同的任务选择和抢占算法,但不使用时间分片在同等优先级的任务之
间共享处理时间。简单说,就是不再有同优先级任务之间的轮流执行的情况,只有高优先级任务抢占低优先级任务的调度原则。
当存在三个任务。task1具有最高优先级,事件触发,通常一直处于阻塞状态。
Task2与IDLE任务具有相同的优先级,同时是一个不断执行的任务。
t1时刻,直到t6时刻,由于没有时间片的轮流调度,所以系统就一直执行着IDLE任务,不会切换到同等优先级的Task2
t6时刻,Task1离开了阻塞状态,并抢占了IDLE任务,IDLE任务被挂到了Ready状态链表的后面。直到t7时刻
t7时刻,task1任务重新进入了阻塞状态。则调度器调度处于Ready状态链表的前面的Task2任务。直到t9,Task1又被事件触发,抢占task2任务运行。task2被挂到了ready状态链表的后部。
t10时刻,task1又进入了阻塞状态,没有高优先级任务了,则调度了排在Ready状态链表前面的IDLE任务进入运行状态。
总结:
当没有时间片的优先级抢占式调度时,只有两种情况会出现调度,一是一个更高优先级的任务进入了ready状态。二是当一个正在运行的任务进入了阻塞状态或suspended状态。
3.3 合作调度Co-operative Scheduling
使用协同调度器时,只有在运行状态任务进入阻塞状态,或者运行状态任务通过调用taskYIELD()显式让步(手动请求重新调度)时才会发生上下文切换。任务永远不会被抢占,因此不能使用时间切片。
上图展示了协作调度器的行为。图 中的水平虚线显示任务何时处于就绪状态。
有三个优先级分别由高到低的三个任务task1,task2,task3。
t1时刻,task3处于running状态,一直到t4时刻,task3持行了taskYIELD(),出让持行权限,则调度器立刻调度了此刻处于ready状态的最高优先级的task1任务进入running状态。直到t5时刻。
t5时刻,task1进入blocked状态,则调度器调度此刻处于ready状态的具有较高优先级的task2任务进入running状态。
t6时刻,task2任务进入了阻塞状态,则调度此刻唯 一处于ready状态的任务task3进入运行状态。
4 、总结
三种调度方式,各有应用场景。一般常用的主要是第一种具有时间片的优先级抢先调度 Prioritized Pre-emptive Scheduling with Time Slicing。三种调度方式的选择依赖于在FreeRTOSConfig.h配置头文件里有两个宏,分别是 configUSE_PREEMPTION 和 configUSE_TIME_SLICING 的取值组合。调度时间片长度等于两次tick中断的时间间隔。在每次tick中断时,会执行一次调度程序。
FreeRTOS依托,四个任务状态,优先级,以及调度算法,完成了任务在一种有序,可控的状态下进行调度。而对于开发者,则只需要根据实际情况,安排好任务的优先级,就可以实现多任务程序的开发。