第五节 系统任务 - 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
跟踪各个任务的运行日志,如下。
测试成功!