|
|
|
@ -1,3 +1,14 @@
|
|
|
|
|
//! Task management implementation
|
|
|
|
|
//!
|
|
|
|
|
//! Everything about task management, like starting and switching tasks is
|
|
|
|
|
//! implemented here.
|
|
|
|
|
//!
|
|
|
|
|
//! A single global instance of [`TaskManager`] called `TASK_MANAGER` controls
|
|
|
|
|
//! all the tasks in the operating system.
|
|
|
|
|
//!
|
|
|
|
|
//! Be careful when you see [`__switch`]. Control flow around this function
|
|
|
|
|
//! might not be what you expect.
|
|
|
|
|
|
|
|
|
|
mod context;
|
|
|
|
|
mod switch;
|
|
|
|
|
#[allow(clippy::module_inception)]
|
|
|
|
@ -13,17 +24,32 @@ use task::{TaskControlBlock, TaskStatus};
|
|
|
|
|
|
|
|
|
|
pub use context::TaskContext;
|
|
|
|
|
|
|
|
|
|
/// The task manager, where all the tasks are managed.
|
|
|
|
|
///
|
|
|
|
|
/// Functions implemented on `TaskManager` deals with all task state transitions
|
|
|
|
|
/// and task context switching. For convenience, you can find wrappers around it
|
|
|
|
|
/// in the module level.
|
|
|
|
|
///
|
|
|
|
|
/// Most of `TaskManager` are hidden behind the field `inner`, to defer
|
|
|
|
|
/// borrowing checks to runtime. You can see examples on how to use `inner` in
|
|
|
|
|
/// existing functions on `TaskManager`.
|
|
|
|
|
pub struct TaskManager {
|
|
|
|
|
/// total number of tasks
|
|
|
|
|
num_app: usize,
|
|
|
|
|
/// use inner value to get mutable access
|
|
|
|
|
inner: UPSafeCell<TaskManagerInner>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The task manager inner in 'UPSafeCell'
|
|
|
|
|
struct TaskManagerInner {
|
|
|
|
|
/// task list
|
|
|
|
|
tasks: Vec<TaskControlBlock>,
|
|
|
|
|
/// id of current `Running` task
|
|
|
|
|
current_task: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lazy_static! {
|
|
|
|
|
/// a `TaskManager` instance through lazy_static!
|
|
|
|
|
pub static ref TASK_MANAGER: TaskManager = {
|
|
|
|
|
println!("init TASK_MANAGER");
|
|
|
|
|
let num_app = get_num_app();
|
|
|
|
@ -45,6 +71,10 @@ lazy_static! {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TaskManager {
|
|
|
|
|
/// Run the first task in task list.
|
|
|
|
|
///
|
|
|
|
|
/// Generally, the first task in task list is an idle task (we call it zero process later).
|
|
|
|
|
/// But in ch4, we load apps statically, so the first task is a real app.
|
|
|
|
|
fn run_first_task(&self) -> ! {
|
|
|
|
|
let mut inner = self.inner.exclusive_access();
|
|
|
|
|
let next_task = &mut inner.tasks[0];
|
|
|
|
@ -59,18 +89,23 @@ impl TaskManager {
|
|
|
|
|
panic!("unreachable in run_first_task!");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Change the status of current `Running` task into `Ready`.
|
|
|
|
|
fn mark_current_suspended(&self) {
|
|
|
|
|
let mut inner = self.inner.exclusive_access();
|
|
|
|
|
let cur = inner.current_task;
|
|
|
|
|
inner.tasks[cur].task_status = TaskStatus::Ready;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Change the status of current `Running` task into `Exited`.
|
|
|
|
|
fn mark_current_exited(&self) {
|
|
|
|
|
let mut inner = self.inner.exclusive_access();
|
|
|
|
|
let cur = inner.current_task;
|
|
|
|
|
inner.tasks[cur].task_status = TaskStatus::Exited;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Find next task to run and return task id.
|
|
|
|
|
///
|
|
|
|
|
/// In this case, we only return the first `Ready` task in task list.
|
|
|
|
|
fn find_next_task(&self) -> Option<usize> {
|
|
|
|
|
let inner = self.inner.exclusive_access();
|
|
|
|
|
let current = inner.current_task;
|
|
|
|
@ -79,16 +114,20 @@ impl TaskManager {
|
|
|
|
|
.find(|id| inner.tasks[*id].task_status == TaskStatus::Ready)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get the current 'Running' task's token.
|
|
|
|
|
fn get_current_token(&self) -> usize {
|
|
|
|
|
let inner = self.inner.exclusive_access();
|
|
|
|
|
inner.tasks[inner.current_task].get_user_token()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get the current 'Running' task's trap contexts.
|
|
|
|
|
fn get_current_trap_cx(&self) -> &'static mut TrapContext {
|
|
|
|
|
let inner = self.inner.exclusive_access();
|
|
|
|
|
inner.tasks[inner.current_task].get_trap_cx()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Switch current `Running` task to the task we have found,
|
|
|
|
|
/// or there is no `Ready` task and we can exit with all applications completed
|
|
|
|
|
fn run_next_task(&self) {
|
|
|
|
|
if let Some(next) = self.find_next_task() {
|
|
|
|
|
let mut inner = self.inner.exclusive_access();
|
|
|
|
@ -109,36 +148,45 @@ impl TaskManager {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Run the first task in task list.
|
|
|
|
|
pub fn run_first_task() {
|
|
|
|
|
TASK_MANAGER.run_first_task();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Switch current `Running` task to the task we have found,
|
|
|
|
|
/// or there is no `Ready` task and we can exit with all applications completed
|
|
|
|
|
fn run_next_task() {
|
|
|
|
|
TASK_MANAGER.run_next_task();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Change the status of current `Running` task into `Ready`.
|
|
|
|
|
fn mark_current_suspended() {
|
|
|
|
|
TASK_MANAGER.mark_current_suspended();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Change the status of current `Running` task into `Exited`.
|
|
|
|
|
fn mark_current_exited() {
|
|
|
|
|
TASK_MANAGER.mark_current_exited();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Suspend the current 'Running' task and run the next task in task list.
|
|
|
|
|
pub fn suspend_current_and_run_next() {
|
|
|
|
|
mark_current_suspended();
|
|
|
|
|
run_next_task();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Exit the current 'Running' task and run the next task in task list.
|
|
|
|
|
pub fn exit_current_and_run_next() {
|
|
|
|
|
mark_current_exited();
|
|
|
|
|
run_next_task();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get the current 'Running' task's token.
|
|
|
|
|
pub fn current_user_token() -> usize {
|
|
|
|
|
TASK_MANAGER.get_current_token()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get the current 'Running' task's trap contexts.
|
|
|
|
|
pub fn current_trap_cx() -> &'static mut TrapContext {
|
|
|
|
|
TASK_MANAGER.get_current_trap_cx()
|
|
|
|
|
}
|
|
|
|
|