From b31e066590c94c1d05569421536130b21444da4e Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Tue, 25 Jul 2023 10:49:11 +0300 Subject: [PATCH] proc: WaitProcess + Spawn --- src/main.rs | 2 +- src/proc/io.rs | 4 +++ src/proc/wait.rs | 2 ++ src/syscall/mod.rs | 63 ++++++++++++++++++++++++++++++++++++++++++--- src/task/process.rs | 60 +++++++++++++++++++++++++++++++++++++----- 5 files changed, 121 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9ed9318e..653a2198 100644 --- a/src/main.rs +++ b/src/main.rs @@ -67,7 +67,7 @@ pub fn kernel_main() { let file = node.open(OpenOptions::READ, FileMode::empty()).unwrap(); { - let user_init = proc::exec::load_elf(file, &["/init"]).unwrap(); + let user_init = proc::exec::load_elf(file, &["/init", "xxx"]).unwrap(); let mut io = user_init.io.lock(); io.set_ioctx(ioctx); drop(io); diff --git a/src/proc/io.rs b/src/proc/io.rs index 3dda86b5..eba36cbe 100644 --- a/src/proc/io.rs +++ b/src/proc/io.rs @@ -66,4 +66,8 @@ impl ProcessIo { pub fn ioctx(&mut self) -> &mut IoContext { self.ioctx.as_mut().unwrap() } + + pub fn handle_exit(&mut self) { + self.files.clear(); + } } diff --git a/src/proc/wait.rs b/src/proc/wait.rs index 70c2de0f..b08ed1f4 100644 --- a/src/proc/wait.rs +++ b/src/proc/wait.rs @@ -32,6 +32,8 @@ struct Timeout { deadline: Duration, } +pub static PROCESS_EXIT_WAIT: Wait = Wait::new("process-exit"); + impl Wait { /// Constructs a new wait notification channel pub const fn new(name: &'static str) -> Self { diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index 1af0f4d5..070bdedd 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -4,10 +4,11 @@ use core::{mem::MaybeUninit, time::Duration}; use abi::{ error::Error, io::{DirectoryEntry, FileAttr, FileMode, OpenOptions, RawFd, SeekFrom}, + process::{SpawnOption, SpawnOptions}, syscall::SyscallFunction, }; use alloc::rc::Rc; -use vfs::{Read, ReadDirectory, Seek, VnodeKind, VnodeRef, Write}; +use vfs::{IoContext, Read, ReadDirectory, Seek, VnodeKind, VnodeRef, Write}; use yggdrasil_abi::{ error::SyscallResult, io::{MountOptions, UnmountOptions}, @@ -16,9 +17,13 @@ use yggdrasil_abi::{ use crate::{ fs, mem::table::{PageAttributes, VirtualMemoryManager}, - proc::{io::ProcessIo, wait}, + proc::{ + self, + io::ProcessIo, + wait::{self, PROCESS_EXIT_WAIT}, + }, sync::IrqSafeSpinlockGuard, - task::process::Process, + task::{process::Process, ProcessId}, }; mod arg; @@ -267,6 +272,58 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result file_borrow.seek(pos).map(|v| v as usize) }) } + SyscallFunction::Spawn => { + let options = arg_user_ref::(args[0] as usize)?; + + debugln!("Spawn {:#?}", options); + + run_with_io(|mut io| { + let node = io.ioctx().find(None, options.program, true, true)?; + + // Setup a new process from the file + let file = node.open(OpenOptions::READ, FileMode::empty())?; + let child = proc::exec::load_elf(file, options.arguments)?; + let pid = child.id() as u32; + + // Inherit root from the creator + let child_ioctx = IoContext::new(io.ioctx().root().clone()); + + { + let mut child_io = child.io.lock(); + + for opt in options.optional { + if let SpawnOption::InheritFile { source, child } = opt { + let src_file = io.file(*source)?; + child_io.set_file(*child, src_file)?; + } + } + } + + child.enqueue_somewhere(); + + Ok(pid as _) + }) + } + SyscallFunction::WaitProcess => { + let pid = args[0] as ProcessId; + let status = arg_user_mut::(args[1] as _)?; + debugln!("WaitProcess #{}", pid); + + let target = Process::get(pid).ok_or(Error::DoesNotExist)?; + + loop { + if let Some(exit_status) = target.get_exit_status() { + *status = exit_status; + break Ok(0); + } + + // Suspend and wait for signal + match PROCESS_EXIT_WAIT.wait(Some(Duration::from_secs(3))) { + Ok(()) | Err(Error::TimedOut) => (), + Err(_) => todo!(), + } + } + } } } diff --git a/src/task/process.rs b/src/task/process.rs index e0c25def..dc5ef608 100644 --- a/src/task/process.rs +++ b/src/task/process.rs @@ -9,7 +9,7 @@ use crate::{ mem::table::AddressSpace, proc::{ io::ProcessIo, - wait::{Wait, WaitStatus}, + wait::{Wait, WaitStatus, PROCESS_EXIT_WAIT}, }, sync::{IrqGuard, IrqSafeSpinlock}, util::OneTimeInit, @@ -34,6 +34,7 @@ pub enum ProcessState { struct ProcessInner { pending_wait: Option<&'static Wait>, wait_status: WaitStatus, + exit_status: i32, } /// Process data and state structure @@ -65,6 +66,7 @@ impl Process { inner: IrqSafeSpinlock::new(ProcessInner { pending_wait: None, wait_status: WaitStatus::Done, + exit_status: 0, }), space, io: IrqSafeSpinlock::new(ProcessIo::new()), @@ -115,6 +117,11 @@ impl Process { self.space.as_ref().unwrap() } + /// Returns the address space of the task, if one is set + pub fn get_address_space(&self) -> Option<&AddressSpace> { + self.space.as_ref() + } + /// Selects a suitable CPU queue and submits the process for execution. /// /// # Panics @@ -212,28 +219,69 @@ impl Process { self.inner.lock().wait_status = status; } + pub fn get_exit_status(&self) -> Option { + if self.state() == ProcessState::Terminated { + Some(self.inner.lock().exit_status) + } else { + None + } + } + /// Returns the [Process] currently executing on local CPU, None if idling. pub fn get_current() -> Option> { let queue = Cpu::local().queue(); queue.current_process() } + pub fn get(pid: ProcessId) -> Option> { + PROCESSES.lock().get(pid).cloned() + } + /// Wraps [Process::get_current()] for cases when the caller is absolutely sure there is a /// running process (e.g. the call itself comes from a process). pub fn current() -> Rc { Self::get_current().unwrap() } - /// Terminate a process - pub fn exit(&self, status: usize) { - let current_state = self.state.swap(ProcessState::Terminated, Ordering::SeqCst); + pub fn handle_exit(&self) { + // Queue lock is still held + assert_eq!(self.state(), ProcessState::Terminated); + // TODO cancel Wait if a process was killed while suspended? + { + let inner = self.inner.lock(); + debugln!( + "Handling exit of #{} with status {}", + self.id(), + inner.exit_status + ); + + // TODO cleanup address space + // if let Some(space) = self.get_address_space() { + // } + + self.io.lock().handle_exit(); + } + + // Notify any waiters we're done + PROCESS_EXIT_WAIT.wakeup_all(); + } + + /// Terminate a process + pub fn exit(&self, status: i32) { + self.inner.lock().exit_status = status; + let current_state = self.state.swap(ProcessState::Terminated, Ordering::SeqCst); debugln!("Process {} exited with code {}", self.id(), status); match current_state { - ProcessState::Suspended => (), + ProcessState::Suspended => { + todo!(); + } ProcessState::Ready => todo!(), - ProcessState::Running => unsafe { Cpu::local().queue().yield_cpu() }, + ProcessState::Running => { + self.handle_exit(); + unsafe { Cpu::local().queue().yield_cpu() } + } ProcessState::Terminated => todo!(), } }