proc: WaitProcess + Spawn

This commit is contained in:
Mark Poliakov 2023-07-25 10:49:11 +03:00
parent 461bfb2791
commit b31e066590
5 changed files with 121 additions and 10 deletions

View File

@ -67,7 +67,7 @@ pub fn kernel_main() {
let file = node.open(OpenOptions::READ, FileMode::empty()).unwrap(); 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(); let mut io = user_init.io.lock();
io.set_ioctx(ioctx); io.set_ioctx(ioctx);
drop(io); drop(io);

View File

@ -66,4 +66,8 @@ impl ProcessIo {
pub fn ioctx(&mut self) -> &mut IoContext { pub fn ioctx(&mut self) -> &mut IoContext {
self.ioctx.as_mut().unwrap() self.ioctx.as_mut().unwrap()
} }
pub fn handle_exit(&mut self) {
self.files.clear();
}
} }

View File

@ -32,6 +32,8 @@ struct Timeout {
deadline: Duration, deadline: Duration,
} }
pub static PROCESS_EXIT_WAIT: Wait = Wait::new("process-exit");
impl Wait { impl Wait {
/// Constructs a new wait notification channel /// Constructs a new wait notification channel
pub const fn new(name: &'static str) -> Self { pub const fn new(name: &'static str) -> Self {

View File

@ -4,10 +4,11 @@ use core::{mem::MaybeUninit, time::Duration};
use abi::{ use abi::{
error::Error, error::Error,
io::{DirectoryEntry, FileAttr, FileMode, OpenOptions, RawFd, SeekFrom}, io::{DirectoryEntry, FileAttr, FileMode, OpenOptions, RawFd, SeekFrom},
process::{SpawnOption, SpawnOptions},
syscall::SyscallFunction, syscall::SyscallFunction,
}; };
use alloc::rc::Rc; use alloc::rc::Rc;
use vfs::{Read, ReadDirectory, Seek, VnodeKind, VnodeRef, Write}; use vfs::{IoContext, Read, ReadDirectory, Seek, VnodeKind, VnodeRef, Write};
use yggdrasil_abi::{ use yggdrasil_abi::{
error::SyscallResult, error::SyscallResult,
io::{MountOptions, UnmountOptions}, io::{MountOptions, UnmountOptions},
@ -16,9 +17,13 @@ use yggdrasil_abi::{
use crate::{ use crate::{
fs, fs,
mem::table::{PageAttributes, VirtualMemoryManager}, mem::table::{PageAttributes, VirtualMemoryManager},
proc::{io::ProcessIo, wait}, proc::{
self,
io::ProcessIo,
wait::{self, PROCESS_EXIT_WAIT},
},
sync::IrqSafeSpinlockGuard, sync::IrqSafeSpinlockGuard,
task::process::Process, task::{process::Process, ProcessId},
}; };
mod arg; mod arg;
@ -267,6 +272,58 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
file_borrow.seek(pos).map(|v| v as usize) file_borrow.seek(pos).map(|v| v as usize)
}) })
} }
SyscallFunction::Spawn => {
let options = arg_user_ref::<SpawnOptions>(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::<i32>(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!(),
}
}
}
} }
} }

View File

@ -9,7 +9,7 @@ use crate::{
mem::table::AddressSpace, mem::table::AddressSpace,
proc::{ proc::{
io::ProcessIo, io::ProcessIo,
wait::{Wait, WaitStatus}, wait::{Wait, WaitStatus, PROCESS_EXIT_WAIT},
}, },
sync::{IrqGuard, IrqSafeSpinlock}, sync::{IrqGuard, IrqSafeSpinlock},
util::OneTimeInit, util::OneTimeInit,
@ -34,6 +34,7 @@ pub enum ProcessState {
struct ProcessInner { struct ProcessInner {
pending_wait: Option<&'static Wait>, pending_wait: Option<&'static Wait>,
wait_status: WaitStatus, wait_status: WaitStatus,
exit_status: i32,
} }
/// Process data and state structure /// Process data and state structure
@ -65,6 +66,7 @@ impl Process {
inner: IrqSafeSpinlock::new(ProcessInner { inner: IrqSafeSpinlock::new(ProcessInner {
pending_wait: None, pending_wait: None,
wait_status: WaitStatus::Done, wait_status: WaitStatus::Done,
exit_status: 0,
}), }),
space, space,
io: IrqSafeSpinlock::new(ProcessIo::new()), io: IrqSafeSpinlock::new(ProcessIo::new()),
@ -115,6 +117,11 @@ impl Process {
self.space.as_ref().unwrap() 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. /// Selects a suitable CPU queue and submits the process for execution.
/// ///
/// # Panics /// # Panics
@ -212,28 +219,69 @@ impl Process {
self.inner.lock().wait_status = status; self.inner.lock().wait_status = status;
} }
pub fn get_exit_status(&self) -> Option<i32> {
if self.state() == ProcessState::Terminated {
Some(self.inner.lock().exit_status)
} else {
None
}
}
/// Returns the [Process] currently executing on local CPU, None if idling. /// Returns the [Process] currently executing on local CPU, None if idling.
pub fn get_current() -> Option<Rc<Self>> { pub fn get_current() -> Option<Rc<Self>> {
let queue = Cpu::local().queue(); let queue = Cpu::local().queue();
queue.current_process() queue.current_process()
} }
pub fn get(pid: ProcessId) -> Option<Rc<Self>> {
PROCESSES.lock().get(pid).cloned()
}
/// Wraps [Process::get_current()] for cases when the caller is absolutely sure there is a /// 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). /// running process (e.g. the call itself comes from a process).
pub fn current() -> Rc<Self> { pub fn current() -> Rc<Self> {
Self::get_current().unwrap() Self::get_current().unwrap()
} }
/// Terminate a process pub fn handle_exit(&self) {
pub fn exit(&self, status: usize) { // Queue lock is still held
let current_state = self.state.swap(ProcessState::Terminated, Ordering::SeqCst); 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); debugln!("Process {} exited with code {}", self.id(), status);
match current_state { match current_state {
ProcessState::Suspended => (), ProcessState::Suspended => {
todo!();
}
ProcessState::Ready => 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!(), ProcessState::Terminated => todo!(),
} }
} }