diff --git a/src/kxos-frame/src/sync/rcu/mod.rs b/src/kxos-frame/src/sync/rcu/mod.rs index 1141c536..09f3d97c 100644 --- a/src/kxos-frame/src/sync/rcu/mod.rs +++ b/src/kxos-frame/src/sync/rcu/mod.rs @@ -89,7 +89,7 @@ impl

Drop for RcuReclaimer

{ wq.wake_one(); } }); - wq.wait_until(|| true); + wq.wait_until(None::, || Some(0)); } } diff --git a/src/kxos-frame/src/sync/wait.rs b/src/kxos-frame/src/sync/wait.rs index 1cb07418..11d4dc3f 100644 --- a/src/kxos-frame/src/sync/wait.rs +++ b/src/kxos-frame/src/sync/wait.rs @@ -1,69 +1,123 @@ +use alloc::collections::VecDeque; +use spin::mutex::Mutex; + +use crate::{debug, task::Task}; + /// A wait queue. /// /// One may wait on a wait queue to put its executing thread to sleep. /// Multiple threads may be the waiters of a wait queue. /// Other threads may invoke the `wake`-family methods of a wait queue to /// wake up one or many waiter threads. -pub struct WaitQueue {} +pub struct WaitQueue { + waiters: Mutex>>, +} -impl WaitQueue { +impl WaitQueue { /// Creates a new instance. pub fn new() -> Self { - todo!() + WaitQueue { + waiters: Mutex::new(VecDeque::new()), + } } /// Wait until some condition becomes true. /// /// This method takes a closure that tests a user-given condition. - /// The method only returns if the condition becomes true. + /// The method only returns if the condition returns Some(_). /// A waker thread should first make the condition true, then invoke the /// `wake`-family method. This ordering is important to ensure that waiter /// threads do not lose any wakeup notifiations. /// /// By taking a condition closure, this wait-wakeup mechanism becomes /// more efficient and robust. - pub fn wait_until(&self, mut cond: F) + pub fn wait_until(&self, data: D, mut cond: F) -> R where - F: FnMut() -> bool, + F: FnMut() -> Option, { - let waiter = Waiter::new(); + let waiter = Waiter::new(data); self.enqueue(&waiter); loop { - if (cond)() { + if let Some(r) = cond() { self.dequeue(&waiter); - break; + return r; } waiter.wait(); } - self.dequeue(&waiter); } /// Wake one waiter thread, if there is one. pub fn wake_one(&self) { - todo!() + if let Some(waiter) = self.waiters.lock().front_mut() { + waiter.wake_up(); + } } /// Wake all waiter threads. pub fn wake_all(&self) { - todo!() + self.waiters.lock().iter_mut().for_each(|waiter| { + waiter.wake_up(); + }); } - fn enqueue(&self, waiter: &Waiter) { - todo!() + /// Wake all waiters if given condition returns true. + /// The condition will check the data carried by waiter if it satisfy some relation with cond_data + pub fn wake_all_on_condition(&self, cond_data: &C, cond: F) + where + F: Fn(&D, &C) -> bool, + { + self.waiters.lock().iter_mut().for_each(|waiter| { + if cond(waiter.data(), cond_data) { + waiter.wake_up() + } + }) } - fn dequeue(&self, waiter: &Waiter) { - todo!() + + fn enqueue(&self, waiter: &Waiter) { + self.waiters.lock().push_back(waiter.clone()); + } + fn dequeue(&self, waiter: &Waiter) { + let mut waiters_lock = self.waiters.lock(); + let len = waiters_lock.len(); + let mut index = 0; + for i in 0..len { + if waiters_lock[i] == *waiter { + index = i; + break; + } + } + waiters_lock.remove(index); + drop(waiters_lock); } } -struct Waiter {} +#[derive(Debug, Clone, PartialEq, Eq)] +struct Waiter { + is_woken_up: bool, + data: D, +} -impl Waiter { - pub fn new() -> Self { - todo!() +impl Waiter { + pub fn new(data: D) -> Self { + Waiter { + is_woken_up: false, + data, + } } pub fn wait(&self) { - todo!() + while !self.is_woken_up { + // yield the execution, to allow other task to contine + debug!("Waiter: wait"); + Task::yield_now(); + } + } + + pub fn wake_up(&mut self) { + self.is_woken_up = true; + } + + pub fn data(&self) -> &D { + &self.data } } diff --git a/src/kxos-frame/src/vm/pod.rs b/src/kxos-frame/src/vm/pod.rs index b4fcb2df..63396ec8 100644 --- a/src/kxos-frame/src/vm/pod.rs +++ b/src/kxos-frame/src/vm/pod.rs @@ -55,9 +55,9 @@ pub unsafe trait Pod: Copy + Sized + Debug { /// FIXME: use derive instead #[macro_export] macro_rules! impl_pod_for { - ($($token:tt),*/* define the input */) => { + ($($pod_ty:ty),*/* define the input */) => { /* define the expansion */ - $(unsafe impl Pod for $token {})* + $(unsafe impl Pod for $pod_ty {})* }; } diff --git a/src/kxos-std/src/lib.rs b/src/kxos-std/src/lib.rs index 97c2e811..ed14a631 100644 --- a/src/kxos-std/src/lib.rs +++ b/src/kxos-std/src/lib.rs @@ -5,6 +5,9 @@ #![allow(unused_variables)] #![feature(const_btree_new)] #![feature(cstr_from_bytes_until_nul)] +#![feature(half_open_range_patterns)] +#![feature(exclusive_range_pattern)] +#![feature(btree_drain_filter)] use alloc::ffi::CString; use kxos_frame::{debug, info, println}; diff --git a/src/kxos-std/src/memory/mod.rs b/src/kxos-std/src/memory/mod.rs index 5444ac23..38b1219c 100644 --- a/src/kxos-std/src/memory/mod.rs +++ b/src/kxos-std/src/memory/mod.rs @@ -58,3 +58,12 @@ pub fn write_bytes_to_user(dest: Vaddr, src: &[u8]) { .expect("[Internal error]Current should have vm space to write bytes to user"); vm_space.write_bytes(dest, src).expect("write bytes failed") } + +/// write val (Plain of Data type) to user space of current process. +pub fn write_val_to_user(dest: Vaddr, val: T) { + let current = Process::current(); + let vm_space = current + .vm_space() + .expect("[Internal error]Current should have vm space to write val to user"); + vm_space.write_val(dest, &val).expect("write val failed"); +} diff --git a/src/kxos-std/src/process/mod.rs b/src/kxos-std/src/process/mod.rs index 53955c35..4d300ce7 100644 --- a/src/kxos-std/src/process/mod.rs +++ b/src/kxos-std/src/process/mod.rs @@ -1,38 +1,43 @@ use core::sync::atomic::{AtomicI32, AtomicUsize, Ordering}; +use alloc::collections::BTreeMap; use alloc::ffi::CString; -use alloc::{ - sync::{Arc, Weak}, - vec::Vec, -}; +use alloc::sync::{Arc, Weak}; +use kxos_frame::sync::WaitQueue; use kxos_frame::{debug, task::Task, user::UserSpace, vm::VmSpace}; use spin::Mutex; use crate::memory::mmap_area::MmapArea; use crate::memory::user_heap::UserHeap; +use self::process_filter::ProcessFilter; use self::status::ProcessStatus; use self::task::create_user_task_from_elf; use self::user_vm_data::UserVm; pub mod fifo_scheduler; +pub mod process_filter; pub mod status; pub mod task; pub mod user_vm_data; +pub mod wait; static PID_ALLOCATOR: AtomicUsize = AtomicUsize::new(0); -const CHILDREN_CAPACITY: usize = 16; +pub type Pid = usize; +pub type Pgid = usize; +pub type ExitCode = i32; /// Process stands for a set of tasks that shares the same userspace. /// Currently, we only support one task inside a process. pub struct Process { // Immutable Part - pid: usize, + pid: Pid, task: Arc, filename: Option, user_space: Option>, user_vm: Option, + waiting_children: WaitQueue, // Mutable Part /// The exit code @@ -42,7 +47,7 @@ pub struct Process { /// Parent process parent: Mutex>>, /// Children processes - children: Mutex>>, + children: Mutex>>, } impl Process { @@ -52,7 +57,7 @@ impl Process { let process = task .data() .downcast_ref::>() - .expect("[Internal Error] Task data should points to weak"); + .expect("[Internal Error] task data should points to weak"); process .upgrade() .expect("[Internal Error] current process cannot be None") @@ -60,7 +65,7 @@ impl Process { /// create a new process(not schedule it) pub fn new( - pid: usize, + pid: Pid, task: Arc, exec_filename: Option, user_vm: Option, @@ -74,13 +79,15 @@ impl Process { let current_process = Process::current(); Some(Arc::downgrade(¤t_process)) }; - let children = Vec::with_capacity(CHILDREN_CAPACITY); + let children = BTreeMap::new(); + let waiting_children = WaitQueue::new(); Self { pid, task, filename: exec_filename, user_space, user_vm, + waiting_children, exit_code: AtomicI32::new(0), status: Mutex::new(ProcessStatus::Runnable), parent: Mutex::new(parent), @@ -88,6 +95,10 @@ impl Process { } } + pub fn waiting_children(&self) -> &WaitQueue { + &self.waiting_children + } + /// init a user process and send the process to scheduler pub fn spawn_user_process(filename: CString, elf_file_content: &'static [u8]) -> Arc { let process = Process::create_user_process(filename, elf_file_content); @@ -130,21 +141,35 @@ impl Process { }) } - /// returns the pid - pub fn pid(&self) -> usize { + /// returns the pid of the process + pub fn pid(&self) -> Pid { self.pid } + /// returns the process group id of the process + pub fn pgid(&self) -> Pgid { + todo!() + } + /// add a child process pub fn add_child(&self, child: Arc) { debug!("process: {}, add child: {} ", self.pid(), child.pid()); - self.children.lock().push(child); + let child_pid = child.pid(); + self.children.lock().insert(child_pid, child); } fn set_parent(&self, parent: Weak) { let _ = self.parent.lock().insert(parent); } + fn parent(&self) -> Option> { + self.parent + .lock() + .as_ref() + .map(|parent| parent.upgrade()) + .flatten() + } + /// Set the exit code when calling exit or exit_group pub fn set_exit_code(&self, exit_code: i32) { self.exit_code.store(exit_code, Ordering::Relaxed); @@ -153,17 +178,27 @@ impl Process { /// Exit current process /// Set the status of current process as Zombie /// Move all children to init process + /// Wake up the parent wait queue if parent is waiting for self pub fn exit(&self) { self.status.lock().set_zombie(); // move children to the init process let current_process = Process::current(); if !current_process.is_init_process() { let init_process = get_init_process(); - for child in self.children.lock().drain(..) { - child.set_parent(Arc::downgrade(&init_process)); - init_process.add_child(child); + for (_, child_process) in self.children.lock().drain_filter(|_, _| true) { + child_process.set_parent(Arc::downgrade(&init_process)); + init_process.add_child(child_process); } } + + // wake up parent waiting children, if any + if let Some(parent) = current_process.parent() { + parent + .waiting_children() + .wake_all_on_condition(¤t_process.pid(), |filter, pid| { + filter.contains_pid(*pid) + }); + } } /// if the current process is init process @@ -215,6 +250,38 @@ impl Process { } } + /// Get child process with given pid + pub fn get_child_by_pid(&self, pid: Pid) -> Option> { + for (child_pid, child_process) in self.children.lock().iter() { + if *child_pid == pid { + return Some(child_process.clone()); + } + } + None + } + + /// free zombie child with pid, returns the exit code of child process + /// We current just remove the child from the children map. + pub fn reap_zombie_child(&self, pid: Pid) -> i32 { + let child_process = self.children.lock().remove(&pid).unwrap(); + assert!(child_process.status() == ProcessStatus::Zombie); + child_process.exit_code() + } + + /// Get any zombie child + pub fn get_zombie_child(&self) -> Option> { + for (_, child_process) in self.children.lock().iter() { + if child_process.status().is_zombie() { + return Some(child_process.clone()); + } + } + None + } + + pub fn exit_code(&self) -> i32 { + self.exit_code.load(Ordering::Relaxed) + } + /// whether the process has child process pub fn has_child(&self) -> bool { self.children.lock().len() != 0 @@ -223,6 +290,10 @@ impl Process { pub fn filename(&self) -> Option<&CString> { self.filename.as_ref() } + + pub fn status(&self) -> ProcessStatus { + self.status.lock().clone() + } } /// Get the init process @@ -242,6 +313,6 @@ pub fn get_init_process() -> Arc { } /// allocate a new pid for new process -pub fn new_pid() -> usize { +pub fn new_pid() -> Pid { PID_ALLOCATOR.fetch_add(1, Ordering::Release) } diff --git a/src/kxos-std/src/process/process_filter.rs b/src/kxos-std/src/process/process_filter.rs new file mode 100644 index 00000000..6c7054d8 --- /dev/null +++ b/src/kxos-std/src/process/process_filter.rs @@ -0,0 +1,49 @@ +use super::{Pgid, Pid, Process}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ProcessFilter { + Any, + WithPid(Pid), + WithPgid(Pgid), +} + +impl ProcessFilter { + // used for waitid + pub fn from_which_and_id(which: u64, id: u64) -> Self { + // Does not support PID_FD now(which = 3) + // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/wait.h#L20 + match which { + 0 => ProcessFilter::Any, + 1 => ProcessFilter::WithPid(id as Pid), + 2 => ProcessFilter::WithPgid(id as Pgid), + _ => panic!("Unknown id type"), + } + } + + // used for wait4 + pub fn from_wait_pid(wait_pid: isize) -> Self { + // https://man7.org/linux/man-pages/man2/waitpid.2.html + if wait_pid < -1 { + // process group ID is equal to the absolute value of pid. + ProcessFilter::WithPgid((-wait_pid) as Pgid) + } else if wait_pid == -1 { + // wait for any child process + ProcessFilter::Any + } else if wait_pid == 0 { + // wait for any child process with same process group ID + let pgid = Process::current().pgid(); + ProcessFilter::WithPgid(pgid) + } else { + // pid > 0. wait for the child whose process ID is equal to the value of pid. + ProcessFilter::WithPid(wait_pid as Pid) + } + } + + pub fn contains_pid(&self, pid: Pid) -> bool { + match self { + ProcessFilter::Any => true, + ProcessFilter::WithPid(filter_pid) => *filter_pid == pid, + ProcessFilter::WithPgid(_) => todo!(), + } + } +} diff --git a/src/kxos-std/src/process/status.rs b/src/kxos-std/src/process/status.rs index be41b700..7f60f7e6 100644 --- a/src/kxos-std/src/process/status.rs +++ b/src/kxos-std/src/process/status.rs @@ -1,4 +1,6 @@ -#[derive(Debug, Clone, Copy)] +//! The process status + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ProcessStatus { Runnable, Zombie, @@ -8,4 +10,8 @@ impl ProcessStatus { pub fn set_zombie(&mut self) { *self = ProcessStatus::Zombie; } + + pub fn is_zombie(&self) -> bool { + *self == ProcessStatus::Zombie + } } diff --git a/src/kxos-std/src/process/wait.rs b/src/kxos-std/src/process/wait.rs new file mode 100644 index 00000000..d3a54c47 --- /dev/null +++ b/src/kxos-std/src/process/wait.rs @@ -0,0 +1,58 @@ +use bitflags::bitflags; + +use super::{process_filter::ProcessFilter, ExitCode, Pid, Process}; + +// The definition of WaitOptions is from Occlum +bitflags! { + pub struct WaitOptions: u32 { + const WNOHANG = 0x1; + //Note: Below flags are not supported yet + const WSTOPPED = 0x2; // Same as WUNTRACED + const WEXITED = 0x4; + const WCONTINUED = 0x8; + const WNOWAIT = 0x01000000; + } +} + +impl WaitOptions { + pub fn supported(&self) -> bool { + let unsupported_flags = WaitOptions::all() - WaitOptions::WNOHANG; + !self.intersects(unsupported_flags) + } +} + +pub fn wait_child_exit( + process_filter: ProcessFilter, + wait_options: WaitOptions, +) -> (Pid, ExitCode) { + let current = Process::current(); + + let (pid, exit_code) = current.waiting_children().wait_until(process_filter, || { + let waited_child_process = match process_filter { + ProcessFilter::Any => current.get_zombie_child(), + ProcessFilter::WithPid(pid) => current.get_child_by_pid(pid as Pid), + ProcessFilter::WithPgid(pgid) => todo!(), + }; + + // some child process is exited + if let Some(waited_child_process) = waited_child_process { + let wait_pid = waited_child_process.pid(); + let exit_code = waited_child_process.exit_code(); + if wait_options.contains(WaitOptions::WNOWAIT) { + // does not reap child, directly return + return Some((wait_pid, exit_code)); + } else { + let exit_code = current.reap_zombie_child(wait_pid); + return Some((wait_pid, exit_code)); + } + } + + if wait_options.contains(WaitOptions::WNOHANG) { + return Some((0, 0)); + } + + None + }); + + (pid, exit_code) +} diff --git a/src/kxos-std/src/syscall/arch_prctl.rs b/src/kxos-std/src/syscall/arch_prctl.rs index e53a6b58..d75a84b0 100644 --- a/src/kxos-std/src/syscall/arch_prctl.rs +++ b/src/kxos-std/src/syscall/arch_prctl.rs @@ -31,7 +31,7 @@ pub fn sys_arch_prctl(code: u64, addr: u64, context: &mut CpuContext) -> Syscall Err(_) => SyscallResult::Return(-1), Ok(code) => { let res = do_arch_prctl(code, addr, context).unwrap(); - SyscallResult::Return(res as i32) + SyscallResult::Return(res as _) } } } diff --git a/src/kxos-std/src/syscall/brk.rs b/src/kxos-std/src/syscall/brk.rs index 3954f4cb..3c13ec8c 100644 --- a/src/kxos-std/src/syscall/brk.rs +++ b/src/kxos-std/src/syscall/brk.rs @@ -23,5 +23,5 @@ pub fn sys_brk(heap_end: u64) -> SyscallResult { .expect("brk should work on process with user space"); let new_heap_end = user_heap.brk(new_heap_end, vm_space); - SyscallResult::Return(new_heap_end as i32) + SyscallResult::Return(new_heap_end as _) } diff --git a/src/kxos-std/src/syscall/exit_group.rs b/src/kxos-std/src/syscall/exit_group.rs index 12d385fd..421f1a9a 100644 --- a/src/kxos-std/src/syscall/exit_group.rs +++ b/src/kxos-std/src/syscall/exit_group.rs @@ -7,6 +7,6 @@ use crate::{ pub fn sys_exit_group(exit_code: u64) -> SyscallResult { debug!("[syscall][id={}][SYS_EXIT_GROUP]", SYS_EXIT_GROUP); - Process::current().set_exit_code(exit_code as i32); - SyscallResult::Exit(exit_code as i32) + Process::current().set_exit_code(exit_code as _); + SyscallResult::Exit(exit_code as _) } diff --git a/src/kxos-std/src/syscall/fork.rs b/src/kxos-std/src/syscall/fork.rs index c1936966..6a45beb4 100644 --- a/src/kxos-std/src/syscall/fork.rs +++ b/src/kxos-std/src/syscall/fork.rs @@ -16,7 +16,7 @@ use super::SyscallResult; pub fn sys_fork(parent_context: CpuContext) -> SyscallResult { debug!("[syscall][id={}][SYS_FORK]", SYS_FORK); let child_process = fork(parent_context); - SyscallResult::Return(child_process.pid() as i32) + SyscallResult::Return(child_process.pid() as _) } /// Fork a child process diff --git a/src/kxos-std/src/syscall/getpid.rs b/src/kxos-std/src/syscall/getpid.rs index 8972bbe5..ac510c18 100644 --- a/src/kxos-std/src/syscall/getpid.rs +++ b/src/kxos-std/src/syscall/getpid.rs @@ -8,5 +8,5 @@ pub fn sys_getpid() -> SyscallResult { debug!("[syscall][id={}][SYS_GETPID]", SYS_GETPID); let pid = Process::current().pid(); info!("[sys_getpid]: pid = {}", pid); - SyscallResult::Return(pid as i32) + SyscallResult::Return(pid as _) } diff --git a/src/kxos-std/src/syscall/gettid.rs b/src/kxos-std/src/syscall/gettid.rs index 1211b0c8..97579169 100644 --- a/src/kxos-std/src/syscall/gettid.rs +++ b/src/kxos-std/src/syscall/gettid.rs @@ -8,5 +8,5 @@ pub fn sys_gettid() -> SyscallResult { debug!("[syscall][id={}][SYS_GETTID]", SYS_GETTID); // For single-thread process, tid is equal to pid let tid = Process::current().pid(); - SyscallResult::Return(tid as i32) + SyscallResult::Return(tid as _) } diff --git a/src/kxos-std/src/syscall/mmap.rs b/src/kxos-std/src/syscall/mmap.rs index ba446ce1..41d9964e 100644 --- a/src/kxos-std/src/syscall/mmap.rs +++ b/src/kxos-std/src/syscall/mmap.rs @@ -61,7 +61,7 @@ pub fn sys_mmap( fd as usize, offset as usize, ); - SyscallResult::Return(res as i32) + SyscallResult::Return(res as _) } pub fn do_sys_mmap( diff --git a/src/kxos-std/src/syscall/mod.rs b/src/kxos-std/src/syscall/mod.rs index c180e761..d6350de0 100644 --- a/src/kxos-std/src/syscall/mod.rs +++ b/src/kxos-std/src/syscall/mod.rs @@ -19,6 +19,8 @@ use crate::syscall::mprotect::sys_mprotect; use crate::syscall::readlink::sys_readlink; use crate::syscall::tgkill::sys_tgkill; use crate::syscall::uname::sys_uname; +use crate::syscall::wait4::sys_wait4; +use crate::syscall::waitid::sys_waitid; use crate::syscall::write::sys_write; use crate::syscall::writev::sys_writev; @@ -36,6 +38,8 @@ mod readlink; mod sched_yield; mod tgkill; mod uname; +mod wait4; +mod waitid; mod write; mod writev; @@ -51,6 +55,7 @@ const SYS_SCHED_YIELD: u64 = 24; const SYS_GETPID: u64 = 39; const SYS_FORK: u64 = 57; const SYS_EXIT: u64 = 60; +const SYS_WAIT4: u64 = 61; const SYS_UNAME: u64 = 63; const SYS_READLINK: u64 = 89; const SYS_GETUID: u64 = 102; @@ -61,6 +66,7 @@ const SYS_ARCH_PRCTL: u64 = 158; const SYS_GETTID: u64 = 186; const SYS_EXIT_GROUP: u64 = 231; const SYS_TGKILL: u64 = 234; +const SYS_WAITID: u64 = 247; pub struct SyscallArgument { syscall_number: u64, @@ -121,6 +127,7 @@ pub fn syscall_dispatch( SYS_GETPID => sys_getpid(), SYS_FORK => sys_fork(context.to_owned()), SYS_EXIT => sys_exit(args[0] as _), + SYS_WAIT4 => sys_wait4(args[0], args[1], args[2]), SYS_UNAME => sys_uname(args[0]), SYS_READLINK => sys_readlink(args[0], args[1], args[2]), SYS_GETUID => sys_getuid(), @@ -131,6 +138,7 @@ pub fn syscall_dispatch( SYS_GETTID => sys_gettid(), SYS_EXIT_GROUP => sys_exit_group(args[0]), SYS_TGKILL => sys_tgkill(args[0], args[1], args[2]), + SYS_WAITID => sys_waitid(args[0], args[1], args[2], args[3], args[4]), _ => panic!("Unsupported syscall number: {}", syscall_number), } } diff --git a/src/kxos-std/src/syscall/readlink.rs b/src/kxos-std/src/syscall/readlink.rs index f9f8f945..07245f45 100644 --- a/src/kxos-std/src/syscall/readlink.rs +++ b/src/kxos-std/src/syscall/readlink.rs @@ -20,7 +20,7 @@ pub fn sys_readlink(filename_ptr: u64, user_buf_ptr: u64, user_buf_len: u64) -> user_buf_ptr as Vaddr, user_buf_len as usize, ); - SyscallResult::Return(res as i32) + SyscallResult::Return(res as _) } /// do sys readlink diff --git a/src/kxos-std/src/syscall/wait4.rs b/src/kxos-std/src/syscall/wait4.rs new file mode 100644 index 00000000..6e0574fc --- /dev/null +++ b/src/kxos-std/src/syscall/wait4.rs @@ -0,0 +1,24 @@ +use crate::{ + memory::write_val_to_user, + process::{process_filter::ProcessFilter, wait::wait_child_exit}, + syscall::SYS_WAIT4, +}; + +use super::SyscallResult; +use crate::process::wait::WaitOptions; +use kxos_frame::debug; + +pub fn sys_wait4(wait_pid: u64, exit_status_ptr: u64, wait_options: u64) -> SyscallResult { + debug!("[syscall][id={}][SYS_WAIT4]", SYS_WAIT4); + let wait_options = WaitOptions::from_bits(wait_options as u32).expect("Unknown wait options"); + debug!("pid = {}", wait_pid as isize); + debug!("exit_status_ptr = {}", exit_status_ptr); + debug!("wait_options: {:?}", wait_options); + let process_filter = ProcessFilter::from_wait_pid(wait_pid as _); + let (return_pid, exit_code) = wait_child_exit(process_filter, wait_options); + if return_pid != 0 && exit_status_ptr != 0 { + write_val_to_user(exit_status_ptr as _, exit_code); + } + + SyscallResult::Return(return_pid as _) +} diff --git a/src/kxos-std/src/syscall/waitid.rs b/src/kxos-std/src/syscall/waitid.rs new file mode 100644 index 00000000..f7e1d77f --- /dev/null +++ b/src/kxos-std/src/syscall/waitid.rs @@ -0,0 +1,18 @@ +use crate::process::{process_filter::ProcessFilter, wait::wait_child_exit}; + +use super::SyscallResult; +use crate::process::wait::WaitOptions; + +pub fn sys_waitid( + which: u64, + upid: u64, + infoq_addr: u64, + options: u64, + rusage_addr: u64, +) -> SyscallResult { + // FIXME: what does infoq and rusage use for? + let process_filter = ProcessFilter::from_which_and_id(which, upid); + let wait_options = WaitOptions::from_bits(options as u32).expect("Unknown wait options"); + let (exit_code, pid) = wait_child_exit(process_filter, wait_options); + SyscallResult::Return(pid) +} diff --git a/src/kxos-std/src/syscall/write.rs b/src/kxos-std/src/syscall/write.rs index 07a83957..ab85154e 100644 --- a/src/kxos-std/src/syscall/write.rs +++ b/src/kxos-std/src/syscall/write.rs @@ -22,7 +22,7 @@ pub fn sys_write(fd: u64, user_buf_ptr: u64, user_buf_len: u64) -> SyscallResult info!("Error message from user mode: {:?}", content); } - SyscallResult::Return(user_buf_len as i32) + SyscallResult::Return(user_buf_len as _) } else { panic!("Unsupported fd number {}", fd); } diff --git a/src/kxos-std/src/syscall/writev.rs b/src/kxos-std/src/syscall/writev.rs index f97a566b..77f101a3 100644 --- a/src/kxos-std/src/syscall/writev.rs +++ b/src/kxos-std/src/syscall/writev.rs @@ -18,7 +18,7 @@ pub struct IoVec { pub fn sys_writev(fd: u64, io_vec_addr: u64, io_vec_count: u64) -> SyscallResult { debug!("[syscall][id={}][SYS_WRITEV]", SYS_WRITEV); let res = do_sys_writev(fd, io_vec_addr as Vaddr, io_vec_count as usize); - SyscallResult::Return(res as i32) + SyscallResult::Return(res as _) } pub fn do_sys_writev(fd: u64, io_vec_addr: Vaddr, io_vec_count: usize) -> usize { diff --git a/src/kxos-user/fork/fork b/src/kxos-user/fork/fork index 5203ceb5..b89ac2d4 100755 --- a/src/kxos-user/fork/fork +++ b/src/kxos-user/fork/fork @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d1d5aa2ef1105eb716219002763836a47e95cef22997064f6f60bfd4c7a43de -size 9528 +oid sha256:cea612414dc19fcd03b563607ea9a453a3d3390b9f3b229ef8e56b08e4d4c8c5 +size 9592 diff --git a/src/kxos-user/fork/fork.s b/src/kxos-user/fork/fork.s index 3441629a..d818c628 100644 --- a/src/kxos-user/fork/fork.s +++ b/src/kxos-user/fork/fork.s @@ -10,6 +10,7 @@ _start: je _child # child process jmp _parent # parent process _parent: + call wait_child call get_pid call print_parent_message call exit @@ -17,6 +18,16 @@ _child: call get_pid call print_child_message call exit +wait_child: + mov %rax, %rdi # child process id +_loop: + mov $61, %rax # syscall number of wait4 + mov $0, %rsi # exit status address + mov $1, %rdx # WNOHANG + syscall + cmp %rdi, %rax # The return value is the pid of child + jne _loop + ret exit: mov $60, %rax # syscall number of exit mov $0, %rdi # exit code