FreeRTOS调度算法

配置调度算法

调度算法是决定哪个Ready状态的任务转换到Running状态的软件例程。

可以使用configUSE_PREEMPTION和configUSE_TIME_SLICING配置常量来更改算法。这两个常量都在FreeRTOSConfig.h中定义。

第三个配置常量configUSE_TICKLESS_IDLE也会影响调度算法,因为使用它会导致tick中断在很长一段时间内完全关闭。configUSE_TICKLESS_IDLE是一个高级选项,专门用于必须最小化能耗的应用程序。以下情况都是假设configUSE_TICKLESS_IDLE被设置为0,这是未定义常量时的默认设置。

在所有可能的配置中,FreeRTOS调度器将确保共享优先级的任务依次被选中进入运行状态。这种“轮流进行”的策略通常被称为“轮询调度”。

轮询调度算法不保证相同优先级的任务之间的时间平等共享,只保证相同优先级的“就绪”状态的任务依次进入“运行”状态。


基于时间切片的优先抢占调度

表所示的配置将FreeRTOS调度器设置为使用一种名为“基于时间切片的固定优先级抢占调度”的调度算法,这是大多数小型RTOS应用程序使用的调度算法。

“固定优先级”的调度算法不会改变分配给被调度任务的优先级,但也不会阻止任务本身改变自己的优先级或其他任务的优先级。

抢占式调度算法将立即“抢占”Running状态任务,如果一个优先级高于Running状态任务的任务进入Ready状态。抢占意味着不自觉地(不显式地让步或阻塞)从Running状态移出并进入Ready状态,以允许不同的任务进入Running状态。

时间切片用于在具有相同优先级的任务之间共享处理时间,即使任务没有显式地让步或进入Blocked状态。使用“时间切片”描述的调度算法将在每个时间切片结束时选择一个新任务进入运行状态,如果有其他Ready状态的任务具有与Running任务相同的优先级。一个时间片等于两个RTOS tick中断之间的时间。

使用基于时间切片的固定优先级抢占调度时如何调度任务。

下图显示了当应用程序中的所有任务都具有唯一优先级时,选择任务以进入Running状态的顺序。

下图显示了当应用程序中的两个任务共享优先级时,选择任务进入Running状态的顺序。

上图显示了由应用程序编写器创建的任务共享Idle任务的处理时间。如果应用程序编写者创建的Idle优先级任务有工作要做,而Idle任务没有,那么分配这么多处理时间给Idle任务可能不太合适。configIDLE_SHOULD_YIELD编译时配置常量可以用来改变Idle任务的调度方式:

  • 如果configIDLE_SHOULD_YIELD被设置为0,那么Idle任务将在整个时间片内保持Running状态,除非它被更高优先级的任务抢占。
  • 如果configIDLE_SHOULD_YIELD设置为1,那么Idle任务将在其循环的每次迭代中,如果有其他Idle优先级任务处于Ready状态,Idle任务将让步(自愿放弃其分配的时间片的剩余部分)。

下图所示的执行模式是在configIDLE_SHOULD_YIELD设置为1的情况。在Idle任务之后选择进入Running状态的任务不会执行整个时间片,而是执行Idle任务产生期间剩余的时间片


优先抢占式调度(无时间切片)

没有时间切片的优先级抢占调度维护了上一节中描述的相同的任务选择和抢占算法,但不使用时间切片在具有相同优先级的任务之间共享处理时间。

如果使用时间切片,并且有多个具有最高优先级的就绪状态任务可以运行,那么调度器将在每个RTOS tick中断(标记时间片结束的tick中断)期间选择一个新任务进入Running状态。

如果没有使用时间切片,那么调度程序将只选择一个新任务进入运行状态,当:

  • 优先级较高的任务进入“就绪”状态。
  • 运行中的任务进入“阻塞”或“挂起”状态。

 不使用时间切片时的任务上下文切换比使用时间切片时的任务上下文切换少。因此,关闭时间切片可以减少调度器的处理开销。然而,关闭时间切片也会导致具有相同优先级的任务接收到的处理时间相差很大,如下图所示。由于这个原因,运行没有时间切片的调度器被认为是一种高级技术,只有有经验的用户才应该使用。


协同调度

当使用协同调度器时,只有当Running状态任务进入Blocked状态,或者Running状态任务通过调用taskYIELD()显式地产生(手动请求重新调度)时,才会发生上下文切换。任务永远不会被抢占,因此不能使用时间切片。

下图演示了协同调度程序的行为。下图中的水平虚线显示了任务何时处于Ready状态。

任务1的优先级最高。它从Blocked状态开始,等待一个信号量。

任务2的优先级介于任务1和任务3之间。它以Blocked状态开始,等待Task 3在t2时刻发送给它的消息。


在多任务处理的应用程序中,应用程序编写人员必须注意不要让多个任务同时访问资源,因为同时访问可能会破坏资源。作为示例,考虑以下场景,其中被访问的资源是一个UART(串行端口)。两个任务是向UART写入字符串;Task 1在写“abcdefghijklmnop”,Task 2在写“123456789”:

1. Task 1处于Running状态,开始写它的字符串。它将“abcdefg”写入UART,但在写入任何进一步的字符之前离开Running状态。

2. Task 2进入Running状态,在离开Running状态之前向UART写入“123456789”。

3.Task 1重新进入Running状态并将其字符串的剩余字符写入UART。

在这种情况下,实际写入UART的内容是“abcdefg123456789hijklmnop”。Task 1写入的字符串没有按照预期的顺序以未中断的顺序写入UART,而是被损坏了,因为Task 2写入UART的字符串出现在其中。

通常情况下,使用合作调度器比使用抢占调度器更容易避免同时访问引起的问题:

  • 当使用抢占式调度器时,运行状态任务可以在任何时候被抢占,包括它与另一个任务共享的资源处于不一致状态时。正如刚才UART示例所演示的,将资源置于不一致的状态会导致数据损坏。
  • 当使用协同调度程序时,应用程序编写器将控制何时切换到另一个任务。因此,应用程序编写者可以确保在资源处于不一致状态时不会发生切换到另一个任务的情况。
  • 在上面的UART示例中,应用程序编写器可以确保Task 1在其整个字符串被写入UART之前不会离开Running状态,这样做可以消除字符串被另一个任务的活动损坏的可能性。

与使用抢占式调度器相比,使用协同调度器时系统的响应会更慢:

  • 当使用抢占式调度器时,调度器将立即开始运行一个任务,直到该任务成为最高优先级的Ready状态任务。这在必须在规定时间内响应高优先级事件的实时系统中通常是必不可少的。
  • 当使用协同调度程序时,直到Running状态任务进入Blocked状态或调用taskYIELD(),才执行切换到已成为最高优先级Ready状态任务的任务。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QxNL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值