os/task/
mod.rs

1//! Task management implementation
2//!
3//! Everything about task management, like starting and switching tasks is
4//! implemented here.
5//!
6//! A single global instance of [`TaskManager`] called `TASK_MANAGER` controls
7//! all the tasks in the whole operating system.
8//!
9//! A single global instance of [`Processor`] called `PROCESSOR` monitors running
10//! task(s) for each core.
11//!
12//! A single global instance of [`PidAllocator`] called `PID_ALLOCATOR` allocates
13//! pid for user apps.
14//!
15//! Be careful when you see `__switch` ASM function in `switch.S`. Control flow around this function
16//! might not be what you expect.
17mod context;
18mod manager;
19mod pid;
20mod processor;
21mod switch;
22#[allow(clippy::module_inception)]
23mod task;
24
25use crate::loader::get_app_data_by_name;
26use crate::sbi::shutdown;
27use alloc::sync::Arc;
28use lazy_static::*;
29pub use manager::{TaskManager, fetch_task};
30use switch::__switch;
31use task::{TaskControlBlock, TaskStatus};
32
33pub use context::TaskContext;
34pub use manager::add_task;
35pub use pid::{KernelStack, PidAllocator, PidHandle, pid_alloc};
36pub use processor::{
37    Processor, current_task, current_trap_cx, current_user_token, run_tasks, schedule,
38    take_current_task,
39};
40/// Suspend the current 'Running' task and run the next task in task list.
41pub fn suspend_current_and_run_next() {
42    // There must be an application running.
43    let task = take_current_task().unwrap();
44
45    // ---- access current TCB exclusively
46    let mut task_inner = task.inner_exclusive_access();
47    let task_cx_ptr = &mut task_inner.task_cx as *mut TaskContext;
48    // Change status to Ready
49    task_inner.task_status = TaskStatus::Ready;
50    drop(task_inner);
51    // ---- release current PCB
52
53    // push back to ready queue.
54    add_task(task);
55    // jump to scheduling cycle
56    schedule(task_cx_ptr);
57}
58
59/// pid of usertests app in make run TEST=1
60pub const IDLE_PID: usize = 0;
61
62/// Exit the current 'Running' task and run the next task in task list.
63pub fn exit_current_and_run_next(exit_code: i32) {
64    // take from Processor
65    let task = take_current_task().unwrap();
66
67    let pid = task.getpid();
68    if pid == IDLE_PID {
69        println!(
70            "[kernel] Idle process exit with exit_code {} ...",
71            exit_code
72        );
73        if exit_code != 0 {
74            //crate::sbi::shutdown(255); //255 == -1 for err hint
75            shutdown(true)
76        } else {
77            //crate::sbi::shutdown(0); //0 for success hint
78            shutdown(false)
79        }
80    }
81
82    // **** access current TCB exclusively
83    let mut inner = task.inner_exclusive_access();
84    // Change status to Zombie
85    inner.task_status = TaskStatus::Zombie;
86    // Record exit code
87    inner.exit_code = exit_code;
88    // do not move to its parent but under initproc
89
90    // ++++++ access initproc TCB exclusively
91    {
92        let mut initproc_inner = INITPROC.inner_exclusive_access();
93        for child in inner.children.iter() {
94            child.inner_exclusive_access().parent = Some(Arc::downgrade(&INITPROC));
95            initproc_inner.children.push(child.clone());
96        }
97    }
98    // ++++++ release parent PCB
99
100    inner.children.clear();
101    // deallocate user space
102    inner.memory_set.recycle_data_pages();
103    drop(inner);
104    // **** release current PCB
105    // drop task manually to maintain rc correctly
106    drop(task);
107    // we do not have to save task context
108    let mut _unused = TaskContext::zero_init();
109    schedule(&mut _unused as *mut _);
110}
111
112lazy_static! {
113    ///Globle process that init user shell
114    pub static ref INITPROC: Arc<TaskControlBlock> = Arc::new(TaskControlBlock::new(
115        get_app_data_by_name("initproc").unwrap()
116    ));
117}
118///Add init process to the manager
119pub fn add_initproc() {
120    add_task(INITPROC.clone());
121}