diff --git a/kernel/src/process/structs.rs b/kernel/src/process/structs.rs index 6a92710..dc26a6e 100644 --- a/kernel/src/process/structs.rs +++ b/kernel/src/process/structs.rs @@ -71,7 +71,7 @@ impl fmt::Debug for FileLike { /// Pid type /// For strong type separation -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Pid(Option); impl Pid { @@ -79,10 +79,6 @@ impl Pid { Pid(None) } - pub fn no_one() -> Self { - Pid(Some(0)) - } - /// Return if it was uninitialized before this call /// When returning true, it usually means this is the first thread pub fn set_if_uninitialized(&mut self, tid: Tid) -> bool { @@ -109,15 +105,21 @@ impl fmt::Display for Pid { } pub struct Process { + // resources pub memory_set: MemorySet, pub files: BTreeMap, pub cwd: String, + futexes: BTreeMap>, + + // relationship pub pid: Pid, // i.e. tgid, usually the tid of first thread - pub ppid: Pid, // the pid of the parent process + pub parent: Option>>, + pub children: Vec>>, pub threads: Vec, // threads in the same process - pub exit_cond: Condvar, // notified when the whole process is going to terminate - pub exit_code: Option, // only available when last thread exits - futexes: BTreeMap>, + + // for waiting child + pub child_exit: Arc, // notified when the a child process is going to terminate + pub child_exit_code: BTreeMap, // child process store its exit code here } /// Records the mapping between pid and Process struct. @@ -125,11 +127,6 @@ lazy_static! { pub static ref PROCESSES: RwLock>>> = RwLock::new(BTreeMap::new()); } -/// Records the list of child processes -lazy_static! { - pub static ref CHILD_PROCESSES: RwLock>>>> = RwLock::new(BTreeMap::new()); -} - /// Let `rcore_thread` can switch between our `Thread` impl rcore_thread::Context for Thread { unsafe fn switch_to(&mut self, target: &mut rcore_thread::Context) { @@ -144,13 +141,9 @@ impl rcore_thread::Context for Thread { if proc.pid.set_if_uninitialized(tid) { // first thread in the process // link to its ppid - match CHILD_PROCESSES.write().entry(proc.ppid.get()) { - Entry::Vacant(entry) => { - entry.insert(vec![self.proc.clone()]); - } - Entry::Occupied(mut entry) => { - entry.get_mut().push(self.proc.clone()); - } + if let Some(parent) = &proc.parent { + let mut parent = parent.lock(); + parent.children.push(Arc::downgrade(&self.proc)); } } // add it to threads @@ -161,6 +154,7 @@ impl rcore_thread::Context for Thread { impl Thread { /// Make a struct for the init thread + /// TODO: remove this, we only need `Context::null()` pub unsafe fn new_init() -> Box { Box::new(Thread { context: Context::null(), @@ -172,10 +166,11 @@ impl Thread { cwd: String::from("/"), futexes: BTreeMap::default(), pid: Pid::uninitialized(), - ppid: Pid::no_one(), - exit_cond: Condvar::new(), + parent: None, + children: Vec::new(), threads: Vec::new(), - exit_code: None + child_exit: Arc::new(Condvar::new()), + child_exit_code: BTreeMap::new(), })), }) } @@ -188,16 +183,18 @@ impl Thread { context: unsafe { Context::new_kernel_thread(entry, arg, kstack.top(), memory_set.token()) }, kstack, clear_child_tid: 0, + // TODO: kernel thread should not have a process proc: Arc::new(Mutex::new(Process { memory_set, files: BTreeMap::default(), cwd: String::from("/"), futexes: BTreeMap::default(), pid: Pid::uninitialized(), - ppid: Pid::no_one(), - exit_cond: Condvar::new(), + parent: None, + children: Vec::new(), threads: Vec::new(), - exit_code: None + child_exit: Arc::new(Condvar::new()), + child_exit_code: BTreeMap::new() })), }) } @@ -286,10 +283,11 @@ impl Thread { cwd: String::from("/"), futexes: BTreeMap::default(), pid: Pid::uninitialized(), - ppid: Pid::no_one(), - exit_cond: Condvar::new(), + parent: None, + children: Vec::new(), threads: Vec::new(), - exit_code: None + child_exit: Arc::new(Condvar::new()), + child_exit_code: BTreeMap::new() })), }) } @@ -300,7 +298,7 @@ impl Thread { let memory_set = self.proc.lock().memory_set.clone(); let files = self.proc.lock().files.clone(); let cwd = self.proc.lock().cwd.clone(); - let ppid = self.proc.lock().pid.clone(); + let parent = Some(self.proc.clone()); debug!("fork: finish clone MemorySet"); // MMU: copy data to the new space @@ -335,10 +333,11 @@ impl Thread { cwd, futexes: BTreeMap::default(), pid: Pid::uninitialized(), - ppid, - exit_cond: Condvar::new(), + parent, + children: Vec::new(), threads: Vec::new(), - exit_code: None + child_exit: Arc::new(Condvar::new()), + child_exit_code: BTreeMap::new() })), }) } @@ -366,6 +365,13 @@ impl Process { } self.futexes.get(&uaddr).unwrap().clone() } + pub fn report_exit_to_parent(&mut self, exit_code: usize) { + if let Some(parent) = &self.parent { + let mut parent = parent.lock(); + parent.child_exit_code.insert(self.pid.get(), exit_code); + parent.child_exit.notify_one(); + } + } } diff --git a/kernel/src/syscall/proc.rs b/kernel/src/syscall/proc.rs index 3d5c330..eabc6a1 100644 --- a/kernel/src/syscall/proc.rs +++ b/kernel/src/syscall/proc.rs @@ -1,7 +1,6 @@ //! Syscalls for process use super::*; -use crate::process::{PROCESSES, CHILD_PROCESSES}; use crate::sync::Condvar; /// Fork the current process. Return the child's PID. @@ -40,11 +39,10 @@ pub fn sys_clone(flags: usize, newsp: usize, parent_tid: *mut u32, child_tid: *m Ok(tid) } -/// Wait the process exit. -/// Return the PID. Store exit code to `code` if it's not null. +/// Wait for the process exit. +/// Return the PID. Store exit code to `wstatus` if it's not null. pub fn sys_wait4(pid: isize, wstatus: *mut i32) -> SysResult { info!("wait4: pid: {}, code: {:?}", pid, wstatus); - let cur_pid = process().pid.get(); if !wstatus.is_null() { process().memory_set.check_mut_ptr(wstatus)?; } @@ -59,42 +57,37 @@ pub fn sys_wait4(pid: isize, wstatus: *mut i32) -> SysResult { _ => unimplemented!(), }; loop { - use alloc::vec; - let all_child: Vec<_> = CHILD_PROCESSES.read().get(&cur_pid).unwrap().clone(); - let wait_procs = match target { - WaitFor::AnyChild => all_child, - WaitFor::Pid(pid) => { - // check if pid is a child - if let Some(proc) = all_child.iter().find(|p| p.lock().pid.get() == pid) { - vec![proc.clone()] - } else { - vec![] - } - } + let mut proc = process(); + // check child_exit_code + let find = match target { + WaitFor::AnyChild => proc.child_exit_code + .iter().next().map(|(&pid, &code)| (pid, code)), + WaitFor::Pid(pid) => proc.child_exit_code + .get(&pid).map(|&code| (pid, code)), }; - if wait_procs.is_empty() { - return Err(SysError::ECHILD); - } - - for proc_lock in wait_procs.iter() { - let proc = proc_lock.lock(); - if let Some(exit_code) = proc.exit_code { - // recycle process - let pid = proc.pid.get(); - drop(proc); - - let mut child_processes = CHILD_PROCESSES.write(); - child_processes.get_mut(&cur_pid).unwrap().retain(|p| p.lock().pid.get() != pid); - child_processes.remove(&pid); - return Ok(pid); + // if found, return + if let Some((pid, exit_code)) = find { + proc.child_exit_code.remove(&pid); + if !wstatus.is_null() { + unsafe { wstatus.write(exit_code as i32); } } + return Ok(pid); } - info!("wait: {} -> {:?}, sleep", thread::current().id(), target); - - for proc in wait_procs.iter() { - proc.lock().exit_cond.add_to_wait_queue(); + // if not, check pid + let children: Vec<_> = proc.children.iter() + .filter_map(|weak| weak.upgrade()) + .collect(); + let invalid = match target { + WaitFor::AnyChild => children.len() == 0, + WaitFor::Pid(pid) => children.iter().find(|p| p.lock().pid.get() == pid).is_none(), + }; + if invalid { + return Err(SysError::ECHILD); } - thread::park(); + info!("wait: thread {} -> {:?}, sleep", thread::current().id(), target); + let condvar = proc.child_exit.clone(); + drop(proc); // must release lock of current process + condvar._wait(); } } @@ -177,7 +170,7 @@ pub fn sys_gettid() -> SysResult { /// Get the parent process id pub fn sys_getppid() -> SysResult { - Ok(process().ppid.get()) + Ok(process().parent.as_ref().unwrap().lock().pid.get()) } /// Exit the current thread @@ -188,8 +181,7 @@ pub fn sys_exit(exit_code: usize) -> ! { proc.threads.retain(|&id| id != tid); if proc.threads.len() == 0 { // last thread - proc.exit_code = Some(exit_code); - proc.exit_cond.notify_all(); + proc.report_exit_to_parent(exit_code); } drop(proc); @@ -209,7 +201,7 @@ pub fn sys_exit(exit_code: usize) -> ! { unreachable!(); } -/// Exit the current thread group (i.e. progress) +/// Exit the current thread group (i.e. process) pub fn sys_exit_group(exit_code: usize) -> ! { let mut proc = process(); info!("exit_group: {}, code: {}", proc.pid, exit_code); @@ -218,8 +210,7 @@ pub fn sys_exit_group(exit_code: usize) -> ! { for tid in proc.threads.iter() { processor().manager().exit(*tid, exit_code); } - proc.exit_code = Some(exit_code); - proc.exit_cond.notify_all(); + proc.report_exit_to_parent(exit_code); drop(proc); processor().yield_now();