一、freertos的调度方式
在单核心处理器在任何给定时间只能有一个任务处于运行状态。
FreeRTOS操作系统支持三种调度方式:抢占式调度(Pre-emptive),时间片调度(time slice)和合作式调度(co-operative)。实际应用主要是抢占式调度和时间片调度结合的调度方法,合作式调度用到的很少。
co-operative = Non-Pre-emptive = 不可剥夺 = 非抢占式
Pre-emptive = 抢占式 = 可剥夺
1、抢占式调度(Pre-emptive)
Pre-emptive scheduling algorithms will immediately ‘pre-empt’ the Running state task if a task that has a priority higher than the Running state task enters the Ready state. Being pre-empted means bein involuntarily (without explicitly yielding or blocking) moved out of the Running state and into the Ready state to allow a different task to enter the Running state.
CPU总是运行多个任务中优先级别最高的那个任务,即使CPU正在运行某个低级别的任务,当有更高优先级别的任务准备就绪时,更高优先级别的任务就会剥夺正在运行任务的CPU使用权,从而使自己获得CPU的使用权。
上图中任务优先级:
Task1 > Task2 > Task3 > idle Task
空闲任务以最低优先级运行,因此每次更高优先级的任务进入就绪状态时都会被抢占——例如,在时间 t3、t5 和 t9。
task 3 是一个事件驱动的任务,以相对较低的优先级执行,但高于空闲优先级。
task 2是是一个周期性任务,分别在t1、t6和t9执行。
2、合作式调度(co-operative)
When the co-operative scheduler is used, a context switch will only occur when the Running state task enters the Blocked state, or the Running state task explicitly yields (manually requests a re-schedule) by calling taskYIELD(). Tasks are never pre-empted, so time slicing cannot be used.
使用合作调度器时,只有在Running状态任务进入Blocked状态,或者Running状态任务通过调用taskYIELD()显式让步(手动请求重新调度)时才会发生任务切换。 任务永远不会被抢占,因此不能使用时间切片。
虚线表示任务处于就绪状态
task 1 具有最高优先级。 它以阻塞状态开始,等待信号量。在时间 t3 一个中断给出信号量,导致任务 1 离开阻塞状态并进入就绪状态。但是在co-operative调度模式下task1并不会立即执行,因为task3还在占用调度器。
task 2:在时间 t2 task2 是最高优先级的就绪状态任务,但由于是co-operative调度模式,task2并不会立即执行,除非task3进入阻塞状态或者调用taskYIELD()。在T4,task3调用taskYIELD()让出调度器,但此时task 1 是最高优先级的就绪状态任务,所以任务 2 并没有真正执行,直到任task1 在 t5 重新进入阻塞状态。
3、时间片调度(time slice)
在FreeRTOS操作系统中只有同优先级任务才会使用时间片调度。最常用的的时间片调度算法就是Round-robin调度算法(时间片轮转),freertos就是用的该算法。一个时间片等于freertos中滴答定时器的时间间隔。
上图中任务优先级:
Task1 = Task2 = Task3 = Task4
先运行Task1,运行够一个时间片后,通过时间片调度切换到Task2。Task2 运行够一个时间片后,通过时间片调度切换到Task3。
Task3 在运行期间调用了阻塞式 API 函数,虽然一个时间片还没有用完,此时依然会通过时间片调度切换到下一个任务 Task4。 (注意,没有用完的时间片不会再使用,下次任务 Task3 得到执行还是按照 一个时间片运行),任务 Task4 运行够一个时间片后,通过时间片调度切换到任务 Task1。
二、抢占式调度和时间片调度结合的调度方法
这种方法是freertos中常用的调度方法,示意图如下:
上图中的优先级:
task 1 > task 2 = idle task
上图中一个新的时间片开始在每一个滴答定时器中断上,t1、t2、t3、t4、t5、t8、t9、t10 和 t11。
在t1 - t5,只有task2和idle task两个任务,这两个task 优先级相等(都是0),因此调度器将轮流执行这两个任务。即t1 - t2分配给idle task,t2 - t3分配给task2,t3 - t4分配给idle task .......(t1 -t2时间间隔也就是time slicing,一般是systick中断时间)。
在t5 -t8时间段,前段在执行idle task,中段task1打断了idle task,task 1执行完毕之后,由于要轮流执行,所以后段执行的是task2。到了t8,到了下一个slice time,切换到idle task继续执行。
在上图中,task2和idle task的优先级相同(都是0)。则在没有其他更高优先级的任务的时候,这两个任务轮流调度。但如果task2有工作要做,但idle task是空任务,那么将那么多处理时间分配给idle task可能是不可取的。因此,freertos中可以通过设置configIDLE_SHOULD_YIELD来减小idle task的占用时间。
- 如果 configIDLE_SHOULD_YIELD 设置为 0,则空闲任务将在其整个时间片内保持运行状态,除非它被更高优先级的任务抢占。(就是上面图片的所示)
- 如果 configIDLE_SHOULD_YIELD 设置为 1,那么如果有其他空闲优先级任务处于就绪状态,则空闲任务将在其循环的每次迭代中让步(自愿放弃其分配的时间片的剩余部分)。如下图所示:
ref:
https://www.freertos.org/Documentation/RTOS_book.html
https://blog.csdn.net/baidu_15547923/article/details/100043209