ucos 原理 - 任务调度分析

tech

This article was last updated on <span id="expire-date"></span> days ago, the information described in the article may be outdated.

上面说过了 uCOS-II 通过不断的在任务之间切换实现任务调度,这一个小节我们来分析 uCOS-II 到底是如何调度任务的。

首先我们要清楚,既然用户给定的任务是一个个无穷循环,所以操作系统肯定不能指望任务自己放弃 CPU 的使用权,让给其他的函数去执行;相反的,当一个任务执行一段时间之后,我们的 OS 就需要“强行的”从它手里拿到处理器的时间,这样的操作系统被称为 抢占式 的,相反那种不同任务之间 Co-operate 的,就叫做 非抢占式 的。

可我们的 OS 是怎么做到 “抢占” 的动作呢?

回到 Cortex 内核,我们发现,整个处理器的运行时可以分为两个模式:

  • Thread 模式

    这种模式用于处理我们的后台代码,也就是无穷循环内的代码

  • Handler 模式

    当有系统异常/外部中断发生时,处理器陷入 Handler 模式,像是进入子函数调用一样将当前的上下文入栈,并切换 MSP/PSP,之后从中断向量表中更新用户函数地址到 PC 寄存器

uCOS-II 就是使用了这样的中断模式进行任务切换的,具体来说,它使用了 SysTick 作为系统时基,并且在 SysTick 的中断处理函数内进行操作系统的任务调度,这样做的原因主要有两个:

  1. SysTick 默认使用 HCLK 时钟

    Cortex 内核的大部分组件都使用这个时钟,它是由 SYSCLK 时钟经过 AHB 预分频之后得到的,其实在 SysTick 的配置中我们还可以对这个时钟源在进行一次分频:

    • CLKSOURCE 配置为 1 时使用 HCLK

    • CLKSOURCE 配置为 0 时进行 8 分频

      但是默认在 SysTick 的初始化中使用了 1 倍频,也就是 HCLK,这样操作系统就能得到一个和 Cortex 内核相同频率的稳定时钟源

  2. SysTick 属于 Cortex 的内核设备

    内核既然提供了这样一个专门用的时钟,当然没有必要再去使用其他的外设,浪费MCU的资源

正是因为使用了 SysTick 作为系统时基,所以我们需要仔细的配置它的时间间隔(也就是24位的递减寄存器),这个时间如果配置的过长会导致系统的实时性能下降;配置的过短又会不断的触发中断调度任务,浪费处理的大量资源用于切换上下文。

除了上面的基于时间片的任务调度之外,还有两种情况需要系统进行任务调度:

  • 对于外部中断处理函数,uCOS-II 自然是没有办法追踪处理器的状态,所以用户需要手动的添加代码让 OS 能够知道我们当前在处理中断,并把当前的任务状态转移至中断态:

    1
    2
    3
    OSIntEnter();
    /* ISR Coding */
    OSIntExit();

    当 ISR 执行完毕之后,调用的 OSIntExit 函数会触发一次中断级别的任务调度,具体的行为我们放在以后分析。

  • 如果任务代码中调用了类似 OSTimeDly 之类的 API 函数,则会触发一次任务级别的调度,通过 OS_Sched 函数检测是否存在更高级别的已就绪任务,如果有的话则进行一次切换,这个切换的行为我们也放在之后去讲。

简单来说,uCOS-II 的系统调度分为了三个部分:

  1. 基于 SysTick 的系统时钟调度
  2. 任务内调用“阻塞型” 系统 API 进行调度
  3. 中断调度

通过这些调度方法,OS 可以尽可能的让具有最高优先级的任务处于运行状态之中,这也就是 RTOS 中 实时 性的保证。

Author: 桂小方

Permalink: https://init.blog/ucos-task-schedule-analysing/

文章许可协议:

如果你觉得文章对你有帮助,可以 支持我

Comments