os/task/
mod.rs

1mod context;
2mod id;
3mod manager;
4mod process;
5mod processor;
6mod signal;
7mod switch;
8#[allow(clippy::module_inception)]
9mod task;
10
11use self::id::TaskUserRes;
12use crate::fs::{OpenFlags, open_file};
13use crate::sbi::shutdown;
14use crate::timer::remove_timer;
15use alloc::{sync::Arc, vec::Vec};
16use lazy_static::*;
17use manager::fetch_task;
18use process::ProcessControlBlock;
19use switch::__switch;
20
21pub use context::TaskContext;
22pub use id::{IDLE_PID, KernelStack, PidHandle, kstack_alloc, pid_alloc};
23pub use manager::{add_task, pid2process, remove_from_pid2process, remove_task, wakeup_task};
24pub use processor::{
25    current_kstack_top, current_process, current_task, current_trap_cx, current_trap_cx_user_va,
26    current_user_token, run_tasks, schedule, take_current_task,
27};
28pub use signal::SignalFlags;
29pub use task::{TaskControlBlock, TaskStatus};
30
31pub fn suspend_current_and_run_next() {
32    // There must be an application running.
33    let task = take_current_task().unwrap();
34
35    // ---- access current TCB exclusively
36    let mut task_inner = task.inner_exclusive_access();
37    let task_cx_ptr = &mut task_inner.task_cx as *mut TaskContext;
38    // Change status to Ready
39    task_inner.task_status = TaskStatus::Ready;
40    drop(task_inner);
41    // ---- release current TCB
42
43    // push back to ready queue.
44    add_task(task);
45    // jump to scheduling cycle
46    schedule(task_cx_ptr);
47}
48
49pub fn block_current_and_run_next() {
50    let task = take_current_task().unwrap();
51    let mut task_inner = task.inner_exclusive_access();
52    let task_cx_ptr = &mut task_inner.task_cx as *mut TaskContext;
53    task_inner.task_status = TaskStatus::Blocked;
54    drop(task_inner);
55    schedule(task_cx_ptr);
56}
57
58/// Exit the current 'Running' task and run the next task in task list.
59pub fn exit_current_and_run_next(exit_code: i32) {
60    let task = take_current_task().unwrap();
61    let mut task_inner = task.inner_exclusive_access();
62    let process = task.process.upgrade().unwrap();
63    let tid = task_inner.res.as_ref().unwrap().tid;
64    // record exit code
65    task_inner.exit_code = Some(exit_code);
66    task_inner.res = None;
67    // here we do not remove the thread since we are still using the kstack
68    // it will be deallocated when sys_waittid is called
69    drop(task_inner);
70    drop(task);
71    // however, if this is the main thread of current process
72    // the process should terminate at once
73    if tid == 0 {
74        let pid = process.getpid();
75        if pid == IDLE_PID {
76            println!(
77                "[kernel] Idle process exit with exit_code {} ...",
78                exit_code
79            );
80            if exit_code != 0 {
81                //crate::sbi::shutdown(255); //255 == -1 for err hint
82                shutdown(true);
83            } else {
84                //crate::sbi::shutdown(0); //0 for success hint
85                shutdown(false);
86            }
87        }
88        remove_from_pid2process(pid);
89        let mut process_inner = process.inner_exclusive_access();
90        // mark this process as a zombie process
91        process_inner.is_zombie = true;
92        // record exit code of main process
93        process_inner.exit_code = exit_code;
94
95        {
96            // move all child processes under init process
97            let mut initproc_inner = INITPROC.inner_exclusive_access();
98            for child in process_inner.children.iter() {
99                child.inner_exclusive_access().parent = Some(Arc::downgrade(&INITPROC));
100                initproc_inner.children.push(child.clone());
101            }
102        }
103
104        // deallocate user res (including tid/trap_cx/ustack) of all threads
105        // it has to be done before we dealloc the whole memory_set
106        // otherwise they will be deallocated twice
107        let mut recycle_res = Vec::<TaskUserRes>::new();
108        for task in process_inner.tasks.iter().filter(|t| t.is_some()) {
109            let task = task.as_ref().unwrap();
110            // if other tasks are Ready in TaskManager or waiting for a timer to be
111            // expired, we should remove them.
112            //
113            // Mention that we do not need to consider Mutex/Semaphore since they
114            // are limited in a single process. Therefore, the blocked tasks are
115            // removed when the PCB is deallocated.
116            remove_inactive_task(Arc::clone(&task));
117            let mut task_inner = task.inner_exclusive_access();
118            if let Some(res) = task_inner.res.take() {
119                recycle_res.push(res);
120            }
121        }
122        // dealloc_tid and dealloc_user_res require access to PCB inner, so we
123        // need to collect those user res first, then release process_inner
124        // for now to avoid deadlock/double borrow problem.
125        drop(process_inner);
126        recycle_res.clear();
127
128        let mut process_inner = process.inner_exclusive_access();
129        process_inner.children.clear();
130        // deallocate other data in user space i.e. program code/data section
131        process_inner.memory_set.recycle_data_pages();
132        // drop file descriptors
133        process_inner.fd_table.clear();
134        // Remove all tasks except for the main thread itself.
135        // This is because we are still using the kstack under the TCB
136        // of the main thread. This TCB, including its kstack, will be
137        // deallocated when the process is reaped via waitpid.
138        while process_inner.tasks.len() > 1 {
139            process_inner.tasks.pop();
140        }
141    }
142    drop(process);
143    // we do not have to save task context
144    let mut _unused = TaskContext::zero_init();
145    schedule(&mut _unused as *mut _);
146}
147
148lazy_static! {
149    pub static ref INITPROC: Arc<ProcessControlBlock> = {
150        let inode = open_file("initproc", OpenFlags::RDONLY).unwrap();
151        let v = inode.read_all();
152        ProcessControlBlock::new(v.as_slice())
153    };
154}
155
156pub fn add_initproc() {
157    let _initproc = INITPROC.clone();
158}
159
160pub fn check_signals_of_current() -> Option<(i32, &'static str)> {
161    let process = current_process();
162    let process_inner = process.inner_exclusive_access();
163    process_inner.signals.check_error()
164}
165
166pub fn current_add_signal(signal: SignalFlags) {
167    let process = current_process();
168    let mut process_inner = process.inner_exclusive_access();
169    process_inner.signals |= signal;
170}
171
172pub fn remove_inactive_task(task: Arc<TaskControlBlock>) {
173    remove_task(Arc::clone(&task));
174    remove_timer(Arc::clone(&task));
175}