os/kernel/scheduler/
mod.rs

1//! 调度器模块
2//!
3//! 定义了调度器接口和相关功能
4mod rr_scheduler;
5mod task_queue;
6mod wait_queue;
7
8use lazy_static::lazy_static;
9
10use crate::{
11    arch::kernel::{context::Context, switch},
12    kernel::{TaskStruct, current_task, scheduler::rr_scheduler::RRScheduler, task::SharedTask},
13    pr_debug,
14    sync::{SpinLock, SpinLockGuard},
15};
16
17pub use task_queue::TaskQueue;
18pub use wait_queue::WaitQueue;
19
20lazy_static! {
21    pub static ref SCHEDULER: SpinLock<RRScheduler> = SpinLock::new(RRScheduler::new());
22}
23
24/// 上下文切换计划结构体
25pub(crate) struct SwitchPlan {
26    pub old: *mut Context,
27    pub new: *const Context,
28}
29
30/// 调度器接口定义
31/// 调度器负责决策和准备下一个任务的运行。
32/// 具体来说,它负责以下几项工作:
33/// 1. 决策: 根据优先级、时间片、调度策略等算法,从运行队列中选择下一个要执行的任务。
34/// 2. 队列维护: 维护任务的运行队列(Run Queue)。
35pub trait Scheduler {
36    /// 构造函数
37    fn new() -> Self;
38    /// 添加任务到调度器
39    /// 参数:
40    /// * `task`: 需要添加的任务
41    fn add_task(&mut self, task: SharedTask);
42    /// 选择下一个要运行的任务
43    /// # 返回值
44    /// 如果要切换到下一个任务,返回切换计划 SwitchPlan;否则返回 None
45    fn next_task(&mut self) -> Option<SwitchPlan>;
46    /// 任务阻塞
47    /// 修改任务状态并从运行队列中移除
48    /// 参数:
49    /// * `task`: 需要阻塞的任务
50    /// * `receive_signal`: 是否可被信号中断
51    /// 注意: 该函数仅设置状态,不负责切换任务
52    fn sleep_task(&mut self, task: SharedTask, receive_signal: bool);
53    /// 唤醒任务
54    /// 修改任务状态并将其添加到运行队列
55    /// 参数:
56    /// * `task`: 需要唤醒的任务
57    fn wake_up(&mut self, task: SharedTask);
58    /// 任务终止
59    /// 修改任务状态并从调度器中移除
60    /// 参数:
61    /// * `task`: 需要终止的任务
62    fn exit_task(&mut self, task: SharedTask);
63    /// 带保护地阻塞任务
64    /// 修改任务状态并从运行队列中移除
65    /// 参数:
66    /// * `task`: 需要阻塞的任务(带锁保护)
67    /// * `stask`: 需要阻塞的任务(共享指针)
68    /// * `receive_signal`: 是否可被信号中断
69    /// HACK: 这个函数被设计用来避免信号处理过程中丢失唤醒的问题。
70    ///       尽量不要使用该函数,除非你非常清楚自己在做什么
71    fn sleep_task_with_guard(
72        &mut self,
73        task: &mut SpinLockGuard<'_, TaskStruct>,
74        stask: SharedTask,
75        receive_signal: bool,
76    );
77}
78
79/// 执行一次调度操作,切换到下一个任务
80pub fn schedule() {
81    let plan = {
82        let mut sched = SCHEDULER.lock();
83        // NOTE: next_task 内部会更新 current_task 与 current_memory_space 并切换页表
84        sched.next_task()
85    };
86
87    if let Some(plan) = plan {
88        pr_debug!("Switched to task {}", current_task().lock().tid);
89        // SAFETY: prepare_switch 生成的切换计划中的指针均合法
90        unsafe { switch(plan.old, plan.new) };
91        // 通常不会立即返回;返回时再继续当前上下文后续逻辑
92    }
93}
94
95/// 主动放弃 CPU
96/// 切换到下一个任务
97/// 如果调用该函数的任务仍可运行,将被放回运行队列末尾,等待下一次调度
98pub fn yield_task() {
99    schedule();
100}
101
102/// 任务阻塞
103/// 修改任务状态并从运行队列中移除
104/// 参数:
105/// * `task`: 需要阻塞的任务
106/// * `receive_signal`: 是否可被信号中断
107/// 注意: 该函数仅设置状态,不负责切换任务
108pub fn sleep_task_with_block(task: SharedTask, receive_signal: bool) {
109    SCHEDULER.lock().sleep_task(task, receive_signal);
110}
111
112/// 唤醒任务
113/// 修改任务状态并将其添加到运行队列
114/// 参数:
115/// * `task`: 需要唤醒的任务
116pub fn wake_up_with_block(task: SharedTask) {
117    SCHEDULER.lock().wake_up(task);
118}
119
120/// 任务终止
121/// 修改任务状态并从调度器中移除
122/// 参数:
123/// * `task`: 需要终止的任务
124pub fn exit_task_with_block(task: SharedTask) {
125    SCHEDULER.lock().exit_task(task);
126}
127
128/// 带保护地阻塞任务
129/// 修改任务状态并从运行队列中移除
130/// 参数:
131/// * `task`: 需要阻塞的任务(带锁保护)
132/// * `stask`: 需要阻塞的任务(共享指针)
133/// * `receive_signal`: 是否可被信号中断
134/// HACK: 这个函数被设计用来避免信号处理过程中丢失唤醒的问题。
135///       尽量不要使用该函数,除非你非常清楚自己在做什么
136pub fn sleep_task_with_guard_and_block(
137    task: &mut SpinLockGuard<'_, TaskStruct>,
138    stask: SharedTask,
139    receive_signal: bool,
140) {
141    SCHEDULER
142        .lock()
143        .sleep_task_with_guard(task, stask, receive_signal);
144}