第五节 系统任务 - Gc 和 Idle 任务

上一节看到,对于任意一个应用任务,可以有若干个任务阻塞等待它的退出。实际上,更确切的说,对于任意一个应用任务,至少有一个任务在等待它退出。这个就是 Gc 任务,它负责回收已经退出的任务的资源。任务的处理逻辑:

fn gc_entry() {
    loop {
        let n = EXITED_TASKS.lock().len();
        for _ in 0..n {
            let task = EXITED_TASKS.lock().pop_front();
            if let Some(task) = task {
                if Arc::strong_count(&task) == 1 {
                    drop(task);
                } else {
                    EXITED_TASKS.lock().push_back(task);
                }
            }
        }
        WAIT_FOR_EXIT.wait();
    }
}

另外,还有一个特殊的系统任务 Idle,当没有任何其它任务可以调度时,Idle 将临时充当 CPU 当前任务:

pub fn run_idle() -> ! {
    loop {
        yield_now();
    }
}

可以看到,它在不断尝试让出执行权。

这两个任务与主任务 MainTask 一起,都是在 run_queue 的 init 方法中被初始化建立的:

// axtask/src/run_queue.rs
pub(crate) fn init() {
    const IDLE_TASK_STACK_SIZE: usize = 4096;
    let idle_task = Task::new(|| run_idle(), "idle".into(), IDLE_TASK_STACK_SIZE);
    IDLE_TASK.init(idle_task.clone());

    let gc_task = Task::new(gc_entry, "gc".into(), axconfig::TASK_STACK_SIZE);
    RUN_QUEUE.lock().add_task(gc_task);

    let main_task = Task::new_init("main".into());
    main_task.set_state(TaskState::Running);

    unsafe { CurrentTask::init_current(main_task) }
}

此外,为 Idle 任务的使用,在 resched(...) 方法中打个补丁:

impl AxRunQueue {
    fn resched(&mut self, preempt: bool) {
        ... ....
        let next = self.pick_next_task().unwrap_or_else(|| IDLE_TASK.get().clone());
        self.switch_to(prev, next);
    }
}

注意:调用 pick_next_task 那一行,当找不到可以运行的下个任务时,就调度Idle这个任务。

至此,多任务支持的基本框架已经建立,打开 debug 日志测试一下,执行 make run LOG=debug 跟踪各个任务的运行日志,如下。

测试成功!