Skip to main content

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