任务调度模块总体设计

总体设计图如下:
notion image

模块设计

模块设计主要依照uC/OS本身的模块划分进行设计。进行模块划分(或者说模块设计)的主要目的是为了更好地确定各个模块需要定义哪些函数接口以供其他模块以及用户使用。
在uC/OS中,主要的模块有:任务调度模块、事件模块、消息模块。
💡
目前我们主要集中于任务调度模块,因此目前的模块设计仅包含了用户以及任务调度模块。而用户并不提供接口,仅调用接口,因此在模块设计中仅需明确任务调度模块需要为用户提供哪些接口即可。
为了保持对原有uC/OS的兼容性,我们的任务调度模块主要像用户提供的接口与uC/OS保持一致。任务调度模块主要提供以下三个基本功能接口
  • OS初始化接口
  • 任务创建接口
  • OS启动接口
💡
由于用户将直接调用这三个接口,因此这三个接口需要加上pub关键字,而其他的接口仅需在当前crate甚至当前文件中可见即可。

接口设计

需要仔细,后续尽量不会发生变化

OS初始化接口

函数签名

主要功能

  • 初始化机器相关部分:如栈对齐,初始化堆栈等
    • 函数签名
    • 初始化全局变量:即为所有的全局变量赋一个初始值
      • 函数签名
        • 💡
          由于Rust要求静态全局变量一定要初始化,因此本函数没有存在的必要
      • 初始化就绪队列:为就绪队列相关变量赋初始值
        • 函数签名
          • 💡
            同样的,本函数也没有存在的必要
        • 初始化TCB:分配TCB空间
          • 函数签名
            • 问题与设计思路
              • 问题:TCB大小不确定
              • 分析:如果按照原有uC/OS代码照搬的话会出现TCB大小不确定而无法预先确定TCB所需空间的问题。出现这个问题主要是因为Future的引入
              • 设计思路:按照Embassy的解决方式,先预先大致分配一段静态内存空间,其大小将由用户设置的任务数量以及TCB大小决定。而TCB的声明以及链表的创建推迟到创建任务的阶段。而链表的创建甚至可以推迟到OS启动的阶段。(不能推迟到OS启动时进行链表创建,因为操作系统启动之后还有可能创建任务)。
              • 💡
                还有一种设计思路是采用Future指针的方式将所有TCB的大小统一,但这样无法保证Future对象的生命周期是静态的。 由于分配空间由静态分配器栈分配器Arena实现,因此如果按照上述思路使用栈分配器实现,则本函数也没有存在的必要
          • 初始化Idle任务:即创建Idle任务
            • 函数签名
              • 问题与设计思路
                • 问题:是否需要Idle任务。
                • 分析:在uC/OS中的Idle任务所实现的功能仅仅是更新Idle计数器并调用用户自定义的OSTaskIdleHook 函数。如果用户没有定义OSTaskIdleHook函数进行一些有意义的操作,就将使CPU/MCU处于忙等状态。
                • 设计思路:借鉴Embassy的方式,当没有任务执行时通过wfe指令使MCU进入低功耗模式。而在进入低功耗模式前,我们也将更新Idle计数器并调用OSTaskIdleHook 函数以便用户更好地自定义系统相关行为。
                • 💡
                  需要注意的是,这种实现与原有的uC/OS并不兼容。 如果按照上述的实现方式,本函数也没有存在的必要
            综上所述,基于Rust的机制,在只有任务调度模块的前提下,其实我们并不需要OSInit 中大部分的操作。在我们的实现中,我们将只在OSInit 中调用用户自定义的OSInitHookBegin 函数。

            任务创建接口

            函数签名

            主要功能

            • 初始化TCB:根据任务创建函数的参数声明(claim)TCB,并初始化相关链表
              • 函数签名
                • 问题一:如何区分传入的任务函数是一个异步函数还是一个普通函数
                  • 设计思路
                      1. 提供两个任务创建的接口,一个用于创建异步任务,一个用于创建普通任务
                      1. 提供一个任务创建的接口,通过宏实现函数的类型判断。
                      1. 提供一个任务创建的接口,通过trait bound实现函数类型的判断。
                • 问题二:如何将异步函数和普通函数统一处理
                  • 设计思路:在经过函数类型判断之后强行为普通函数套上async,并根据async函数返回的Future进行任务创建
              • 调度:由于任务创建并不一定是在操作系统启动前创建的。所以如果创建任务时如果操作系统正在运行,则需要进行重调度。本被调函数放在OS启动部分进行设计
              💡
              在uC/OS的任务创建接口中还有栈初始化相关的接口,由于我们采取将线程和协程统一的理念,因此并不需要将栈与任务进行绑定,即不需要再任务创建接口中进行栈初始化。

              OS启动接口

              函数签名

              主要功能

              • 启动调度
                • 函数签名
                  • 设计思路:在uC/OS中,每次调度之前将先找到最高优先级的任务,然后再进行上下文切换。而在Embassy中,无论是决定下一个被执行的协程,还是协程的执行,都是在执行器的poll函数中实现的。在Rust-uC/OS中,我们将采取Embassy的措施,将所有调度执行相关的部分都封装进执行器的poll接口中,这样不仅能让OS启动接口所带来的封装性更强,也可以减少许多全局静态变量的定义
                    • 💡
                      能减少全局静态变量的定义是由于我们可以将OSTCBCur等与调度相关的全局静态变量作为执行器的成员变量。而执行器我们将使用类似rCore中文件系统的定义实现。

                辅助接口

                为了更好地实现以上三个基本功能接口,需要底层的执行器模块以及栈分配器模块提供相关的辅助接口

                alloc接口

                • 主要功能:用于在Arena中分配一个TCB的空间
                • 设计思路:可以直接借助Embassy的栈分配器Arena实现

                poll接口

                • 主要功能:启动调度
                • 设计思路:总体思路可以参考Embassy执行器poll函数的实现。
                  • 在Rust-uC中,与Embassy的实现主要有两个不同
                  • 选取任务时需要通过优先级位图法选取最高优先级的任务执行。
                  • 需要考虑换栈的问题

                数据结构设计

                不一定细化的具体定义,按需补充数据结构即可
                 
                Loading...
                liamY
                liamY
                Chasing Possible
                最新发布
                Enter AMX (Advanced Matrix Extensions)
                2025-3-26
                ktransformers相关内容学习
                2025-2-16
                sglang_benchmark
                2025-2-7
                SnapKV: LLM Knows What You are Looking for Before Generation
                2024-12-12
                数字电路复习
                2024-12-11
                CacheBlend: Fast Large Language Model Serving with Cached Knowledge Fusion论文学习
                2024-11-23
                公告
                🎉Liam’s blog🎉
                -- 全新上线 ---
                👏欢迎comment👏
                ⚠️由于浏览器缓存的原因,有些内容是更新了的但是需要手动刷新3次左右,页面才会显示更新内容