Correct init process, allow waiting on any children, reap zombies in init

This commit is contained in:
Jeremy Soller 2016-11-01 11:04:53 -06:00
parent ff227737bf
commit 25743a89a2
4 changed files with 122 additions and 46 deletions

View file

@ -7,7 +7,7 @@ use arch;
use context::file::File; use context::file::File;
use context::memory::{Grant, Memory, SharedMemory, Tls}; use context::memory::{Grant, Memory, SharedMemory, Tls};
use syscall::data::Event; use syscall::data::Event;
use sync::{WaitCondition, WaitQueue}; use sync::{WaitMap, WaitQueue};
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Status { pub enum Status {
@ -40,7 +40,7 @@ pub struct Context {
/// Context is halting parent /// Context is halting parent
pub vfork: bool, pub vfork: bool,
/// Context is being waited on /// Context is being waited on
pub waitpid: Arc<WaitCondition>, pub waitpid: Arc<WaitMap<usize, usize>>,
/// Context should wake up at specified time /// Context should wake up at specified time
pub wake: Option<(u64, u64)>, pub wake: Option<(u64, u64)>,
/// The architecture specific context /// The architecture specific context
@ -85,7 +85,7 @@ impl Context {
running: false, running: false,
cpu_id: None, cpu_id: None,
vfork: false, vfork: false,
waitpid: Arc::new(WaitCondition::new()), waitpid: Arc::new(WaitMap::new()),
wake: None, wake: None,
arch: arch::context::Context::new(), arch: arch::context::Context::new(),
kfx: None, kfx: None,

View file

@ -1,4 +1,5 @@
use collections::BTreeMap; use collections::BTreeMap;
use core::mem;
use spin::Mutex; use spin::Mutex;
use sync::WaitCondition; use sync::WaitCondition;
@ -9,7 +10,7 @@ pub struct WaitMap<K, V> {
condition: WaitCondition condition: WaitCondition
} }
impl<K, V> WaitMap<K, V> where K: Ord { impl<K, V> WaitMap<K, V> where K: Clone + Ord {
pub fn new() -> WaitMap<K, V> { pub fn new() -> WaitMap<K, V> {
WaitMap { WaitMap {
inner: Mutex::new(BTreeMap::new()), inner: Mutex::new(BTreeMap::new()),
@ -17,17 +18,45 @@ impl<K, V> WaitMap<K, V> where K: Ord {
} }
} }
pub fn send(&self, key: K, value: V) { pub fn receive_nonblock(&self, key: &K) -> Option<V> {
self.inner.lock().insert(key, value); self.inner.lock().remove(key)
self.condition.notify();
} }
pub fn receive(&self, key: &K) -> V { pub fn receive(&self, key: &K) -> V {
loop { loop {
if let Some(value) = self.inner.lock().remove(key) { if let Some(value) = self.receive_nonblock(key) {
return value; return value;
} }
self.condition.wait(); 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<K, V> {
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();
}
} }

View file

@ -691,13 +691,14 @@ pub fn exit(status: usize) -> ! {
}; };
let mut close_files = Vec::new(); let mut close_files = Vec::new();
{ let (pid, ppid) = {
let mut context = context_lock.write(); let mut context = context_lock.write();
if Arc::strong_count(&context.files) == 1 { if Arc::strong_count(&context.files) == 1 {
mem::swap(context.files.lock().deref_mut(), &mut close_files); mem::swap(context.files.lock().deref_mut(), &mut close_files);
} }
context.files = Arc::new(Mutex::new(Vec::new())); 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 /// Files must be closed while context is valid so that messages can be passed
for (fd, file_option) in close_files.drain(..).enumerate() { 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(); let mut context = context_lock.write();
context.image.clear(); context.image.clear();
@ -730,18 +743,28 @@ pub fn exit(status: usize) -> ! {
context.status = context::Status::Exited(status); 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(); let contexts = context::contexts();
if let Some(parent_lock) = contexts.get(ppid) { if let Some(parent_lock) = contexts.get(ppid) {
let mut parent = parent_lock.write(); let waitpid = {
if ! parent.unblock() { let mut parent = parent_lock.write();
println!("{} not blocked for exit vfork unblock", ppid); 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 { } else {
println!("{} not found for exit vfork unblock", ppid); println!("{} not found for exit vfork unblock", ppid);
} }
@ -977,45 +1000,64 @@ pub fn virttophys(virtual_address: usize) -> Result<usize> {
} }
} }
pub fn waitpid(pid: usize, status_ptr: usize, flags: usize) -> Result<usize> { fn reap(pid: usize) -> Result<usize> {
loop { // Spin until not running
let mut exited = false; let mut running = false;
let mut running; while running {
let waitpid;
{ {
let contexts = context::contexts(); let contexts = context::contexts();
let context_lock = contexts.get(pid).ok_or(Error::new(ESRCH))?; let context_lock = contexts.get(pid).ok_or(Error::new(ESRCH))?;
let context = context_lock.read(); 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; running = context.running;
waitpid = context.waitpid.clone();
} }
if exited { arch::interrupt::pause();
// 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(); 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<usize> {
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 { } 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)
} }
} }
} }

View file

@ -89,4 +89,9 @@ pub fn main() {
if let Err(err) = run("initfs:etc/init.rc") { if let Err(err) = run("initfs:etc/init.rc") {
println!("init: failed to run initfs:etc/init.rc: {}", err); println!("init: failed to run initfs:etc/init.rc: {}", err);
} }
loop {
let mut status = 0;
syscall::waitpid(0, &mut status, 0).unwrap();
}
} }