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 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);
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user