proc: WaitProcess + Spawn
This commit is contained in:
parent
461bfb2791
commit
b31e066590
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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<usize, Error>
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<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.
|
||||
pub fn get_current() -> Option<Rc<Self>> {
|
||||
let queue = Cpu::local().queue();
|
||||
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
|
||||
/// running process (e.g. the call itself comes from a process).
|
||||
pub fn current() -> Rc<Self> {
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user