WillOS 核心机理深度解析(第三章):调度算法——指挥多任务并行的“中央大脑”

项目地址:WillOS/WillOS/Inc/allocator.h at master · LieWill/WillOS

返回总导读:关于我独立开发 RTOS 这一件事

如果说 Allocator 是“地产商”,Context 是“瞬移魔法”,那么 Scheduler(调度器) 就是整个操作系统的灵魂——它是一个高效、严谨的“项目经理”,决定了在成百上千个任务中,此时此刻究竟谁有资格占用 CPU 这个极其珍贵的“会议室”。

在 WillOS/Src/scheduler.c 中,任务从出生到消亡的每一个瞬间都受其统筹。


1. 核心机理:环形链表(Ring)与公平正义

绝大多数 RTOS 使用简单的线性链表。但 WillOS 采用了 环形双向链表 (TCB Ring) 结构。

1.1 为什么要用“环”?

在一个分时系统中,如果两个任务优先级相同,它们应该“平分”时间片。

  • 机理:当 SchedulerSwitch 被调用时,它只需执行 Ready = Ready->next
  • 效果:通过简单的指针移动,物理上实现了“轮转调度(Round-Robin)”。不需要复杂的数组重排,性能开销极低且恒定(O(1))。

2. 状态机:任务的四种“生存姿态”

调度器并不只是简单地管理“谁在跑”,它通过四个不同的环(队列)管理着任务的一生:

  1. 就绪态 (Ready):当前的“冲刺者”。这些任务优先级最高,且随时可以运行。它们在环里循环轮转。
  2. 低优先级态 (Lowpriority):排队中的“旁听生”。这是一个有序表,只有当 Ready 环为空时,调度器才会从这里挑选最高权重的任务顶替上去。
  3. 阻塞态 (Blocked):正在“午休”的任务。
    • 机理:利用 ticktime 戳进行排序。每当系统滴答(SysTick)触发,经理会检查这个环的头部。
    • 唤醒机理:如果“由于时间到了”,任务会被瞬间踢回 Ready 环。
  4. 挂起态 (Suspended):处于“待岗”状态。它们不占用 CPU 资源,除非被显式唤醒(Detach)。

3. 运行流程:从“滴答”到“切换”

上下文切换的宏观轨迹如下:

  1. System Tick (心跳):硬件定时器触发 OsSwitch
  2. 标记请求:调用 os_yield 挂起 PendSV(请回顾 Context 篇的异步机制)。
  3. 核心调度逻辑 (SchedulerSwitch)
    • 清理:检查是否有刚刚“辞职(Delete)”的任务需要回收内存。
    • 超时巡检:遍历 Blocked 环,唤醒时间到的任务。
    • 优先级裁决:如果发现了更高优先级的任务,立即把当前的 Ready 环降级到 Lowpriority,让高优先级任务上位。
    • 轮转:如果优先级一样,就切到环的下一个。

4. 高级特技:线程克隆 (Thread Clone)

WillOS 调度器中有一个极其罕见的特性:类似于 Unix 的 fork() 机理。

  • 机理:通过 ThreadClone,它能完整复制一个正在运行的任务。
  • 魔法核心:它不仅复制了任务的 TCB,还调用了 memcpy 复制了整个任务栈。
  • 伪造返回:它精细地手动构造了一个异常栈帧,让“子任务”醒来时,仿佛自己刚刚从 ThreadClone 函数中返回,并且拿到了返回值为 0 的结果。

5. 特色机理:优雅的任务退出与返回值捕获

在大多数 RTOS(如 FreeRTOS)中,任务函数是禁忌返回的。但在 WillOS 中,你可以像写普通 C 函数一样让任务执行完毕并返回。

5.1 为什么 FreeRTOS 任务不能返回?

在 FreeRTOS 中,如果你让一个任务函数 return,由于没有地方接收这个返回动作,CPU 会跑飞(跳转到不可预知的内存地址),通常会触发一个 configASSERT(pdFALSE)。因此你必须显式调用 vTaskDelete(NULL) 来“自杀”。

5.2 WillOS 的“自动善后”机理

WillOS 在初始化任务栈(InitTaskStack)时,玩了一个精妙的视觉欺骗:

  1. 伪造 LR(返回地址):它在栈里把任务的返回地址(LR 寄存器)改成了 TaskReturn 函数的地址。
  2. 捕获返回值:根据 C 调用约定,函数的返回值存在 R0 寄存器里。当任务函数结束跳转到 TaskReturn 时,我们通过一小段内联汇编捕获 R0 的值,存入 TCB。
  3. 自动回收:完成值捕获后,TaskReturn 会自动调用 Scheduler_Delete 将任务从调度环中摘除。

对比优势

  • 符合直觉:任务就像一个普通的线程或函数。
  • 结果传递:主线程可以通过 ThreadGetReturn 拿回子任务算出来的结果,这在执行复杂并行计算时非常方便。

6. 承上启下:从“任务状态”到“任务协作”

至此,调度器已经完美解决了“谁在跑”和“跑多久”的问题。它能让 CPU 在不同的任务间切换,也能让任务休眠。

但是,调度器还有一个致命的盲区:
它虽然知道任务在“等”,但它不知道任务在“等什么”。

  • 如果两个任务想抢同一个打印机(互斥锁)怎么办?
  • 如果任务 A 想给任务 B 发一封电子邮件(信号量/消息队列)怎么办?

调度器本身不负责处理这些错综复杂的“人际关系”。这些高级的同步与通信逻辑,被交给了下一层——Coordinator(协作器)

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇