From 25743a89a29cd8b8ed27e4ddf4ec024e40e8e7e0 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Tue, 1 Nov 2016 11:04:53 -0600 Subject: [PATCH] Correct init process, allow waiting on any children, reap zombies in init --- kernel/context/context.rs | 6 +- kernel/sync/wait_map.rs | 39 +++++++++++-- kernel/syscall/process.rs | 118 ++++++++++++++++++++++++++------------ programs/init/src/main.rs | 5 ++ 4 files changed, 122 insertions(+), 46 deletions(-) diff --git a/kernel/context/context.rs b/kernel/context/context.rs index 1563e6f..a8678d7 100644 --- a/kernel/context/context.rs +++ b/kernel/context/context.rs @@ -7,7 +7,7 @@ use arch; use context::file::File; use context::memory::{Grant, Memory, SharedMemory, Tls}; use syscall::data::Event; -use sync::{WaitCondition, WaitQueue}; +use sync::{WaitMap, WaitQueue}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Status { @@ -40,7 +40,7 @@ pub struct Context { /// Context is halting parent pub vfork: bool, /// Context is being waited on - pub waitpid: Arc, + pub waitpid: Arc>, /// Context should wake up at specified time pub wake: Option<(u64, u64)>, /// The architecture specific context @@ -85,7 +85,7 @@ impl Context { running: false, cpu_id: None, vfork: false, - waitpid: Arc::new(WaitCondition::new()), + waitpid: Arc::new(WaitMap::new()), wake: None, arch: arch::context::Context::new(), kfx: None, diff --git a/kernel/sync/wait_map.rs b/kernel/sync/wait_map.rs index 468ad8c..c0198a1 100644 --- a/kernel/sync/wait_map.rs +++ b/kernel/sync/wait_map.rs @@ -1,4 +1,5 @@ use collections::BTreeMap; +use core::mem; use spin::Mutex; use sync::WaitCondition; @@ -9,7 +10,7 @@ pub struct WaitMap { condition: WaitCondition } -impl WaitMap where K: Ord { +impl WaitMap where K: Clone + Ord { pub fn new() -> WaitMap { WaitMap { inner: Mutex::new(BTreeMap::new()), @@ -17,17 +18,45 @@ impl WaitMap where K: Ord { } } - pub fn send(&self, key: K, value: V) { - self.inner.lock().insert(key, value); - self.condition.notify(); + pub fn receive_nonblock(&self, key: &K) -> Option { + self.inner.lock().remove(key) } pub fn receive(&self, key: &K) -> V { loop { - if let Some(value) = self.inner.lock().remove(key) { + if let Some(value) = self.receive_nonblock(key) { return value; } self.condition.wait(); } } + + pub fn receive_any_nonblock(&self) -> Option<(K, V)> { + let mut inner = self.inner.lock(); + if let Some(key) = inner.keys().next().map(|key| key.clone()) { + inner.remove(&key).map(|value| (key, value)) + } else { + None + } + } + + pub fn receive_any(&self) -> (K, V) { + loop { + if let Some(entry) = self.receive_any_nonblock() { + return entry; + } + self.condition.wait(); + } + } + + pub fn receive_all(&self) -> BTreeMap { + let mut ret = BTreeMap::new(); + mem::swap(&mut ret, &mut *self.inner.lock()); + ret + } + + pub fn send(&self, key: K, value: V) { + self.inner.lock().insert(key, value); + self.condition.notify(); + } } diff --git a/kernel/syscall/process.rs b/kernel/syscall/process.rs index 6fa2fad..39ed2ad 100644 --- a/kernel/syscall/process.rs +++ b/kernel/syscall/process.rs @@ -691,13 +691,14 @@ pub fn exit(status: usize) -> ! { }; let mut close_files = Vec::new(); - { + let (pid, ppid) = { let mut context = context_lock.write(); if Arc::strong_count(&context.files) == 1 { mem::swap(context.files.lock().deref_mut(), &mut close_files); } context.files = Arc::new(Mutex::new(Vec::new())); - } + (context.id, context.ppid) + }; /// Files must be closed while context is valid so that messages can be passed for (fd, file_option) in close_files.drain(..).enumerate() { @@ -716,7 +717,19 @@ pub fn exit(status: usize) -> ! { } } - let (vfork, ppid) = { + /// Transfer child processes to parent + { + let contexts = context::contexts(); + for (_id, context_lock) in contexts.iter() { + let mut context = context_lock.write(); + if context.ppid == pid { + context.ppid = ppid; + context.vfork = false; + } + } + } + + let (vfork, children) = { let mut context = context_lock.write(); context.image.clear(); @@ -730,18 +743,28 @@ pub fn exit(status: usize) -> ! { context.status = context::Status::Exited(status); - context.waitpid.notify(); + let children = context.waitpid.receive_all(); - (vfork, context.ppid) + (vfork, children) }; - if vfork { + { let contexts = context::contexts(); if let Some(parent_lock) = contexts.get(ppid) { - let mut parent = parent_lock.write(); - if ! parent.unblock() { - println!("{} not blocked for exit vfork unblock", ppid); + let waitpid = { + let mut parent = parent_lock.write(); + if vfork { + if ! parent.unblock() { + println!("{} not blocked for exit vfork unblock", ppid); + } + } + parent.waitpid.clone() + }; + + for (c_pid, c_status) in children { + waitpid.send(c_pid, c_status); } + waitpid.send(pid, status); } else { println!("{} not found for exit vfork unblock", ppid); } @@ -977,45 +1000,64 @@ pub fn virttophys(virtual_address: usize) -> Result { } } -pub fn waitpid(pid: usize, status_ptr: usize, flags: usize) -> Result { - loop { - let mut exited = false; - let mut running; - let waitpid; +fn reap(pid: usize) -> Result { + // Spin until not running + let mut running = false; + while running { { let contexts = context::contexts(); let context_lock = contexts.get(pid).ok_or(Error::new(ESRCH))?; let context = context_lock.read(); - if let context::Status::Exited(status) = context.status { - if status_ptr != 0 { - let status_slice = validate_slice_mut(status_ptr as *mut usize, 1)?; - status_slice[0] = status; - } - exited = true; - } running = context.running; - waitpid = context.waitpid.clone(); } - if exited { - // Spin until not running - while running { - { - let contexts = context::contexts(); - let context_lock = contexts.get(pid).ok_or(Error::new(ESRCH))?; - let context = context_lock.read(); - running = context.running; - } + arch::interrupt::pause(); + } - arch::interrupt::pause(); + let mut contexts = context::contexts_mut(); + contexts.remove(pid).ok_or(Error::new(ESRCH)).and(Ok(pid)) +} + +pub fn waitpid(pid: usize, status_ptr: usize, flags: usize) -> Result { + let waitpid = { + let contexts = context::contexts(); + let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; + let context = context_lock.read(); + context.waitpid.clone() + }; + + let mut tmp = [0]; + let status_slice = if status_ptr != 0 { + validate_slice_mut(status_ptr as *mut usize, 1)? + } else { + &mut tmp + }; + + if pid == 0 { + if flags & WNOHANG == WNOHANG { + if let Some((w_pid, status)) = waitpid.receive_any_nonblock() { + status_slice[0] = status; + reap(w_pid) + } else { + Ok(0) } - - let mut contexts = context::contexts_mut(); - return contexts.remove(pid).ok_or(Error::new(ESRCH)).and(Ok(pid)); - } else if flags & WNOHANG == WNOHANG { - return Ok(0); } else { - waitpid.wait(); + let (w_pid, status) = waitpid.receive_any(); + status_slice[0] = status; + reap(w_pid) + } + } else { + if flags & WNOHANG == WNOHANG { + if let Some(status) = waitpid.receive_nonblock(&pid) { + status_slice[0] = status; + reap(pid) + } else { + Ok(0) + } + } else { + let status = waitpid.receive(&pid); + status_slice[0] = status; + reap(pid) } } } diff --git a/programs/init/src/main.rs b/programs/init/src/main.rs index f6e6a98..1b010ce 100644 --- a/programs/init/src/main.rs +++ b/programs/init/src/main.rs @@ -89,4 +89,9 @@ pub fn main() { if let Err(err) = run("initfs:etc/init.rc") { println!("init: failed to run initfs:etc/init.rc: {}", err); } + + loop { + let mut status = 0; + syscall::waitpid(0, &mut status, 0).unwrap(); + } }