mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-10 05:46:48 +00:00
add syscall wait4 and waitid
This commit is contained in:
parent
21290f1cff
commit
e29bb58d45
@ -89,7 +89,7 @@ impl<P> Drop for RcuReclaimer<P> {
|
|||||||
wq.wake_one();
|
wq.wake_one();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
wq.wait_until(|| true);
|
wq.wait_until(None::<u8>, || Some(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,69 +1,123 @@
|
|||||||
|
use alloc::collections::VecDeque;
|
||||||
|
use spin::mutex::Mutex;
|
||||||
|
|
||||||
|
use crate::{debug, task::Task};
|
||||||
|
|
||||||
/// A wait queue.
|
/// A wait queue.
|
||||||
///
|
///
|
||||||
/// One may wait on a wait queue to put its executing thread to sleep.
|
/// One may wait on a wait queue to put its executing thread to sleep.
|
||||||
/// Multiple threads may be the waiters of a wait queue.
|
/// Multiple threads may be the waiters of a wait queue.
|
||||||
/// Other threads may invoke the `wake`-family methods of a wait queue to
|
/// Other threads may invoke the `wake`-family methods of a wait queue to
|
||||||
/// wake up one or many waiter threads.
|
/// wake up one or many waiter threads.
|
||||||
pub struct WaitQueue {}
|
pub struct WaitQueue<D: Clone + Eq + PartialEq> {
|
||||||
|
waiters: Mutex<VecDeque<Waiter<D>>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl WaitQueue {
|
impl<D: Clone + Eq + PartialEq> WaitQueue<D> {
|
||||||
/// Creates a new instance.
|
/// Creates a new instance.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
todo!()
|
WaitQueue {
|
||||||
|
waiters: Mutex::new(VecDeque::new()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wait until some condition becomes true.
|
/// Wait until some condition becomes true.
|
||||||
///
|
///
|
||||||
/// This method takes a closure that tests a user-given condition.
|
/// 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
|
/// A waker thread should first make the condition true, then invoke the
|
||||||
/// `wake`-family method. This ordering is important to ensure that waiter
|
/// `wake`-family method. This ordering is important to ensure that waiter
|
||||||
/// threads do not lose any wakeup notifiations.
|
/// threads do not lose any wakeup notifiations.
|
||||||
///
|
///
|
||||||
/// By taking a condition closure, this wait-wakeup mechanism becomes
|
/// By taking a condition closure, this wait-wakeup mechanism becomes
|
||||||
/// more efficient and robust.
|
/// more efficient and robust.
|
||||||
pub fn wait_until<F>(&self, mut cond: F)
|
pub fn wait_until<F, R>(&self, data: D, mut cond: F) -> R
|
||||||
where
|
where
|
||||||
F: FnMut() -> bool,
|
F: FnMut() -> Option<R>,
|
||||||
{
|
{
|
||||||
let waiter = Waiter::new();
|
let waiter = Waiter::new(data);
|
||||||
self.enqueue(&waiter);
|
self.enqueue(&waiter);
|
||||||
loop {
|
loop {
|
||||||
if (cond)() {
|
if let Some(r) = cond() {
|
||||||
self.dequeue(&waiter);
|
self.dequeue(&waiter);
|
||||||
break;
|
return r;
|
||||||
}
|
}
|
||||||
waiter.wait();
|
waiter.wait();
|
||||||
}
|
}
|
||||||
self.dequeue(&waiter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wake one waiter thread, if there is one.
|
/// Wake one waiter thread, if there is one.
|
||||||
pub fn wake_one(&self) {
|
pub fn wake_one(&self) {
|
||||||
todo!()
|
if let Some(waiter) = self.waiters.lock().front_mut() {
|
||||||
|
waiter.wake_up();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wake all waiter threads.
|
/// Wake all waiter threads.
|
||||||
pub fn wake_all(&self) {
|
pub fn wake_all(&self) {
|
||||||
todo!()
|
self.waiters.lock().iter_mut().for_each(|waiter| {
|
||||||
|
waiter.wake_up();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enqueue(&self, waiter: &Waiter) {
|
/// Wake all waiters if given condition returns true.
|
||||||
todo!()
|
/// The condition will check the data carried by waiter if it satisfy some relation with cond_data
|
||||||
|
pub fn wake_all_on_condition<F, C>(&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<D>) {
|
||||||
|
self.waiters.lock().push_back(waiter.clone());
|
||||||
|
}
|
||||||
|
fn dequeue(&self, waiter: &Waiter<D>) {
|
||||||
|
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<D: Clone + Eq + PartialEq> {
|
||||||
|
is_woken_up: bool,
|
||||||
|
data: D,
|
||||||
|
}
|
||||||
|
|
||||||
impl Waiter {
|
impl<D: Clone + Eq + PartialEq> Waiter<D> {
|
||||||
pub fn new() -> Self {
|
pub fn new(data: D) -> Self {
|
||||||
todo!()
|
Waiter {
|
||||||
|
is_woken_up: false,
|
||||||
|
data,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait(&self) {
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,9 +55,9 @@ pub unsafe trait Pod: Copy + Sized + Debug {
|
|||||||
/// FIXME: use derive instead
|
/// FIXME: use derive instead
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! impl_pod_for {
|
macro_rules! impl_pod_for {
|
||||||
($($token:tt),*/* define the input */) => {
|
($($pod_ty:ty),*/* define the input */) => {
|
||||||
/* define the expansion */
|
/* define the expansion */
|
||||||
$(unsafe impl Pod for $token {})*
|
$(unsafe impl Pod for $pod_ty {})*
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
#![allow(unused_variables)]
|
#![allow(unused_variables)]
|
||||||
#![feature(const_btree_new)]
|
#![feature(const_btree_new)]
|
||||||
#![feature(cstr_from_bytes_until_nul)]
|
#![feature(cstr_from_bytes_until_nul)]
|
||||||
|
#![feature(half_open_range_patterns)]
|
||||||
|
#![feature(exclusive_range_pattern)]
|
||||||
|
#![feature(btree_drain_filter)]
|
||||||
|
|
||||||
use alloc::ffi::CString;
|
use alloc::ffi::CString;
|
||||||
use kxos_frame::{debug, info, println};
|
use kxos_frame::{debug, info, println};
|
||||||
|
@ -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");
|
.expect("[Internal error]Current should have vm space to write bytes to user");
|
||||||
vm_space.write_bytes(dest, src).expect("write bytes failed")
|
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<T: Pod>(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");
|
||||||
|
}
|
||||||
|
@ -1,38 +1,43 @@
|
|||||||
use core::sync::atomic::{AtomicI32, AtomicUsize, Ordering};
|
use core::sync::atomic::{AtomicI32, AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
use alloc::collections::BTreeMap;
|
||||||
use alloc::ffi::CString;
|
use alloc::ffi::CString;
|
||||||
use alloc::{
|
use alloc::sync::{Arc, Weak};
|
||||||
sync::{Arc, Weak},
|
use kxos_frame::sync::WaitQueue;
|
||||||
vec::Vec,
|
|
||||||
};
|
|
||||||
use kxos_frame::{debug, task::Task, user::UserSpace, vm::VmSpace};
|
use kxos_frame::{debug, task::Task, user::UserSpace, vm::VmSpace};
|
||||||
use spin::Mutex;
|
use spin::Mutex;
|
||||||
|
|
||||||
use crate::memory::mmap_area::MmapArea;
|
use crate::memory::mmap_area::MmapArea;
|
||||||
use crate::memory::user_heap::UserHeap;
|
use crate::memory::user_heap::UserHeap;
|
||||||
|
|
||||||
|
use self::process_filter::ProcessFilter;
|
||||||
use self::status::ProcessStatus;
|
use self::status::ProcessStatus;
|
||||||
use self::task::create_user_task_from_elf;
|
use self::task::create_user_task_from_elf;
|
||||||
use self::user_vm_data::UserVm;
|
use self::user_vm_data::UserVm;
|
||||||
|
|
||||||
pub mod fifo_scheduler;
|
pub mod fifo_scheduler;
|
||||||
|
pub mod process_filter;
|
||||||
pub mod status;
|
pub mod status;
|
||||||
pub mod task;
|
pub mod task;
|
||||||
pub mod user_vm_data;
|
pub mod user_vm_data;
|
||||||
|
pub mod wait;
|
||||||
|
|
||||||
static PID_ALLOCATOR: AtomicUsize = AtomicUsize::new(0);
|
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.
|
/// Process stands for a set of tasks that shares the same userspace.
|
||||||
/// Currently, we only support one task inside a process.
|
/// Currently, we only support one task inside a process.
|
||||||
pub struct Process {
|
pub struct Process {
|
||||||
// Immutable Part
|
// Immutable Part
|
||||||
pid: usize,
|
pid: Pid,
|
||||||
task: Arc<Task>,
|
task: Arc<Task>,
|
||||||
filename: Option<CString>,
|
filename: Option<CString>,
|
||||||
user_space: Option<Arc<UserSpace>>,
|
user_space: Option<Arc<UserSpace>>,
|
||||||
user_vm: Option<UserVm>,
|
user_vm: Option<UserVm>,
|
||||||
|
waiting_children: WaitQueue<ProcessFilter>,
|
||||||
|
|
||||||
// Mutable Part
|
// Mutable Part
|
||||||
/// The exit code
|
/// The exit code
|
||||||
@ -42,7 +47,7 @@ pub struct Process {
|
|||||||
/// Parent process
|
/// Parent process
|
||||||
parent: Mutex<Option<Weak<Process>>>,
|
parent: Mutex<Option<Weak<Process>>>,
|
||||||
/// Children processes
|
/// Children processes
|
||||||
children: Mutex<Vec<Arc<Process>>>,
|
children: Mutex<BTreeMap<usize, Arc<Process>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Process {
|
impl Process {
|
||||||
@ -52,7 +57,7 @@ impl Process {
|
|||||||
let process = task
|
let process = task
|
||||||
.data()
|
.data()
|
||||||
.downcast_ref::<Weak<Process>>()
|
.downcast_ref::<Weak<Process>>()
|
||||||
.expect("[Internal Error] Task data should points to weak<process>");
|
.expect("[Internal Error] task data should points to weak<process>");
|
||||||
process
|
process
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.expect("[Internal Error] current process cannot be None")
|
.expect("[Internal Error] current process cannot be None")
|
||||||
@ -60,7 +65,7 @@ impl Process {
|
|||||||
|
|
||||||
/// create a new process(not schedule it)
|
/// create a new process(not schedule it)
|
||||||
pub fn new(
|
pub fn new(
|
||||||
pid: usize,
|
pid: Pid,
|
||||||
task: Arc<Task>,
|
task: Arc<Task>,
|
||||||
exec_filename: Option<CString>,
|
exec_filename: Option<CString>,
|
||||||
user_vm: Option<UserVm>,
|
user_vm: Option<UserVm>,
|
||||||
@ -74,13 +79,15 @@ impl Process {
|
|||||||
let current_process = Process::current();
|
let current_process = Process::current();
|
||||||
Some(Arc::downgrade(¤t_process))
|
Some(Arc::downgrade(¤t_process))
|
||||||
};
|
};
|
||||||
let children = Vec::with_capacity(CHILDREN_CAPACITY);
|
let children = BTreeMap::new();
|
||||||
|
let waiting_children = WaitQueue::new();
|
||||||
Self {
|
Self {
|
||||||
pid,
|
pid,
|
||||||
task,
|
task,
|
||||||
filename: exec_filename,
|
filename: exec_filename,
|
||||||
user_space,
|
user_space,
|
||||||
user_vm,
|
user_vm,
|
||||||
|
waiting_children,
|
||||||
exit_code: AtomicI32::new(0),
|
exit_code: AtomicI32::new(0),
|
||||||
status: Mutex::new(ProcessStatus::Runnable),
|
status: Mutex::new(ProcessStatus::Runnable),
|
||||||
parent: Mutex::new(parent),
|
parent: Mutex::new(parent),
|
||||||
@ -88,6 +95,10 @@ impl Process {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn waiting_children(&self) -> &WaitQueue<ProcessFilter> {
|
||||||
|
&self.waiting_children
|
||||||
|
}
|
||||||
|
|
||||||
/// init a user process and send the process to scheduler
|
/// init a user process and send the process to scheduler
|
||||||
pub fn spawn_user_process(filename: CString, elf_file_content: &'static [u8]) -> Arc<Self> {
|
pub fn spawn_user_process(filename: CString, elf_file_content: &'static [u8]) -> Arc<Self> {
|
||||||
let process = Process::create_user_process(filename, elf_file_content);
|
let process = Process::create_user_process(filename, elf_file_content);
|
||||||
@ -130,21 +141,35 @@ impl Process {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns the pid
|
/// returns the pid of the process
|
||||||
pub fn pid(&self) -> usize {
|
pub fn pid(&self) -> Pid {
|
||||||
self.pid
|
self.pid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// returns the process group id of the process
|
||||||
|
pub fn pgid(&self) -> Pgid {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
/// add a child process
|
/// add a child process
|
||||||
pub fn add_child(&self, child: Arc<Process>) {
|
pub fn add_child(&self, child: Arc<Process>) {
|
||||||
debug!("process: {}, add child: {} ", self.pid(), child.pid());
|
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<Process>) {
|
fn set_parent(&self, parent: Weak<Process>) {
|
||||||
let _ = self.parent.lock().insert(parent);
|
let _ = self.parent.lock().insert(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parent(&self) -> Option<Arc<Process>> {
|
||||||
|
self.parent
|
||||||
|
.lock()
|
||||||
|
.as_ref()
|
||||||
|
.map(|parent| parent.upgrade())
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the exit code when calling exit or exit_group
|
/// Set the exit code when calling exit or exit_group
|
||||||
pub fn set_exit_code(&self, exit_code: i32) {
|
pub fn set_exit_code(&self, exit_code: i32) {
|
||||||
self.exit_code.store(exit_code, Ordering::Relaxed);
|
self.exit_code.store(exit_code, Ordering::Relaxed);
|
||||||
@ -153,17 +178,27 @@ impl Process {
|
|||||||
/// Exit current process
|
/// Exit current process
|
||||||
/// Set the status of current process as Zombie
|
/// Set the status of current process as Zombie
|
||||||
/// Move all children to init process
|
/// Move all children to init process
|
||||||
|
/// Wake up the parent wait queue if parent is waiting for self
|
||||||
pub fn exit(&self) {
|
pub fn exit(&self) {
|
||||||
self.status.lock().set_zombie();
|
self.status.lock().set_zombie();
|
||||||
// move children to the init process
|
// move children to the init process
|
||||||
let current_process = Process::current();
|
let current_process = Process::current();
|
||||||
if !current_process.is_init_process() {
|
if !current_process.is_init_process() {
|
||||||
let init_process = get_init_process();
|
let init_process = get_init_process();
|
||||||
for child in self.children.lock().drain(..) {
|
for (_, child_process) in self.children.lock().drain_filter(|_, _| true) {
|
||||||
child.set_parent(Arc::downgrade(&init_process));
|
child_process.set_parent(Arc::downgrade(&init_process));
|
||||||
init_process.add_child(child);
|
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
|
/// 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<Arc<Process>> {
|
||||||
|
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<Arc<Process>> {
|
||||||
|
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
|
/// whether the process has child process
|
||||||
pub fn has_child(&self) -> bool {
|
pub fn has_child(&self) -> bool {
|
||||||
self.children.lock().len() != 0
|
self.children.lock().len() != 0
|
||||||
@ -223,6 +290,10 @@ impl Process {
|
|||||||
pub fn filename(&self) -> Option<&CString> {
|
pub fn filename(&self) -> Option<&CString> {
|
||||||
self.filename.as_ref()
|
self.filename.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn status(&self) -> ProcessStatus {
|
||||||
|
self.status.lock().clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the init process
|
/// Get the init process
|
||||||
@ -242,6 +313,6 @@ pub fn get_init_process() -> Arc<Process> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// allocate a new pid for new process
|
/// allocate a new pid for new process
|
||||||
pub fn new_pid() -> usize {
|
pub fn new_pid() -> Pid {
|
||||||
PID_ALLOCATOR.fetch_add(1, Ordering::Release)
|
PID_ALLOCATOR.fetch_add(1, Ordering::Release)
|
||||||
}
|
}
|
||||||
|
49
src/kxos-std/src/process/process_filter.rs
Normal file
49
src/kxos-std/src/process/process_filter.rs
Normal file
@ -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!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
#[derive(Debug, Clone, Copy)]
|
//! The process status
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum ProcessStatus {
|
pub enum ProcessStatus {
|
||||||
Runnable,
|
Runnable,
|
||||||
Zombie,
|
Zombie,
|
||||||
@ -8,4 +10,8 @@ impl ProcessStatus {
|
|||||||
pub fn set_zombie(&mut self) {
|
pub fn set_zombie(&mut self) {
|
||||||
*self = ProcessStatus::Zombie;
|
*self = ProcessStatus::Zombie;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_zombie(&self) -> bool {
|
||||||
|
*self == ProcessStatus::Zombie
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
58
src/kxos-std/src/process/wait.rs
Normal file
58
src/kxos-std/src/process/wait.rs
Normal file
@ -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)
|
||||||
|
}
|
@ -31,7 +31,7 @@ pub fn sys_arch_prctl(code: u64, addr: u64, context: &mut CpuContext) -> Syscall
|
|||||||
Err(_) => SyscallResult::Return(-1),
|
Err(_) => SyscallResult::Return(-1),
|
||||||
Ok(code) => {
|
Ok(code) => {
|
||||||
let res = do_arch_prctl(code, addr, context).unwrap();
|
let res = do_arch_prctl(code, addr, context).unwrap();
|
||||||
SyscallResult::Return(res as i32)
|
SyscallResult::Return(res as _)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,5 +23,5 @@ pub fn sys_brk(heap_end: u64) -> SyscallResult {
|
|||||||
.expect("brk should work on process with user space");
|
.expect("brk should work on process with user space");
|
||||||
let new_heap_end = user_heap.brk(new_heap_end, vm_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 _)
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,6 @@ use crate::{
|
|||||||
|
|
||||||
pub fn sys_exit_group(exit_code: u64) -> SyscallResult {
|
pub fn sys_exit_group(exit_code: u64) -> SyscallResult {
|
||||||
debug!("[syscall][id={}][SYS_EXIT_GROUP]", SYS_EXIT_GROUP);
|
debug!("[syscall][id={}][SYS_EXIT_GROUP]", SYS_EXIT_GROUP);
|
||||||
Process::current().set_exit_code(exit_code as i32);
|
Process::current().set_exit_code(exit_code as _);
|
||||||
SyscallResult::Exit(exit_code as i32)
|
SyscallResult::Exit(exit_code as _)
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ use super::SyscallResult;
|
|||||||
pub fn sys_fork(parent_context: CpuContext) -> SyscallResult {
|
pub fn sys_fork(parent_context: CpuContext) -> SyscallResult {
|
||||||
debug!("[syscall][id={}][SYS_FORK]", SYS_FORK);
|
debug!("[syscall][id={}][SYS_FORK]", SYS_FORK);
|
||||||
let child_process = fork(parent_context);
|
let child_process = fork(parent_context);
|
||||||
SyscallResult::Return(child_process.pid() as i32)
|
SyscallResult::Return(child_process.pid() as _)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fork a child process
|
/// Fork a child process
|
||||||
|
@ -8,5 +8,5 @@ pub fn sys_getpid() -> SyscallResult {
|
|||||||
debug!("[syscall][id={}][SYS_GETPID]", SYS_GETPID);
|
debug!("[syscall][id={}][SYS_GETPID]", SYS_GETPID);
|
||||||
let pid = Process::current().pid();
|
let pid = Process::current().pid();
|
||||||
info!("[sys_getpid]: pid = {}", pid);
|
info!("[sys_getpid]: pid = {}", pid);
|
||||||
SyscallResult::Return(pid as i32)
|
SyscallResult::Return(pid as _)
|
||||||
}
|
}
|
||||||
|
@ -8,5 +8,5 @@ pub fn sys_gettid() -> SyscallResult {
|
|||||||
debug!("[syscall][id={}][SYS_GETTID]", SYS_GETTID);
|
debug!("[syscall][id={}][SYS_GETTID]", SYS_GETTID);
|
||||||
// For single-thread process, tid is equal to pid
|
// For single-thread process, tid is equal to pid
|
||||||
let tid = Process::current().pid();
|
let tid = Process::current().pid();
|
||||||
SyscallResult::Return(tid as i32)
|
SyscallResult::Return(tid as _)
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ pub fn sys_mmap(
|
|||||||
fd as usize,
|
fd as usize,
|
||||||
offset as usize,
|
offset as usize,
|
||||||
);
|
);
|
||||||
SyscallResult::Return(res as i32)
|
SyscallResult::Return(res as _)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_sys_mmap(
|
pub fn do_sys_mmap(
|
||||||
|
@ -19,6 +19,8 @@ use crate::syscall::mprotect::sys_mprotect;
|
|||||||
use crate::syscall::readlink::sys_readlink;
|
use crate::syscall::readlink::sys_readlink;
|
||||||
use crate::syscall::tgkill::sys_tgkill;
|
use crate::syscall::tgkill::sys_tgkill;
|
||||||
use crate::syscall::uname::sys_uname;
|
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::write::sys_write;
|
||||||
use crate::syscall::writev::sys_writev;
|
use crate::syscall::writev::sys_writev;
|
||||||
|
|
||||||
@ -36,6 +38,8 @@ mod readlink;
|
|||||||
mod sched_yield;
|
mod sched_yield;
|
||||||
mod tgkill;
|
mod tgkill;
|
||||||
mod uname;
|
mod uname;
|
||||||
|
mod wait4;
|
||||||
|
mod waitid;
|
||||||
mod write;
|
mod write;
|
||||||
mod writev;
|
mod writev;
|
||||||
|
|
||||||
@ -51,6 +55,7 @@ const SYS_SCHED_YIELD: u64 = 24;
|
|||||||
const SYS_GETPID: u64 = 39;
|
const SYS_GETPID: u64 = 39;
|
||||||
const SYS_FORK: u64 = 57;
|
const SYS_FORK: u64 = 57;
|
||||||
const SYS_EXIT: u64 = 60;
|
const SYS_EXIT: u64 = 60;
|
||||||
|
const SYS_WAIT4: u64 = 61;
|
||||||
const SYS_UNAME: u64 = 63;
|
const SYS_UNAME: u64 = 63;
|
||||||
const SYS_READLINK: u64 = 89;
|
const SYS_READLINK: u64 = 89;
|
||||||
const SYS_GETUID: u64 = 102;
|
const SYS_GETUID: u64 = 102;
|
||||||
@ -61,6 +66,7 @@ const SYS_ARCH_PRCTL: u64 = 158;
|
|||||||
const SYS_GETTID: u64 = 186;
|
const SYS_GETTID: u64 = 186;
|
||||||
const SYS_EXIT_GROUP: u64 = 231;
|
const SYS_EXIT_GROUP: u64 = 231;
|
||||||
const SYS_TGKILL: u64 = 234;
|
const SYS_TGKILL: u64 = 234;
|
||||||
|
const SYS_WAITID: u64 = 247;
|
||||||
|
|
||||||
pub struct SyscallArgument {
|
pub struct SyscallArgument {
|
||||||
syscall_number: u64,
|
syscall_number: u64,
|
||||||
@ -121,6 +127,7 @@ pub fn syscall_dispatch(
|
|||||||
SYS_GETPID => sys_getpid(),
|
SYS_GETPID => sys_getpid(),
|
||||||
SYS_FORK => sys_fork(context.to_owned()),
|
SYS_FORK => sys_fork(context.to_owned()),
|
||||||
SYS_EXIT => sys_exit(args[0] as _),
|
SYS_EXIT => sys_exit(args[0] as _),
|
||||||
|
SYS_WAIT4 => sys_wait4(args[0], args[1], args[2]),
|
||||||
SYS_UNAME => sys_uname(args[0]),
|
SYS_UNAME => sys_uname(args[0]),
|
||||||
SYS_READLINK => sys_readlink(args[0], args[1], args[2]),
|
SYS_READLINK => sys_readlink(args[0], args[1], args[2]),
|
||||||
SYS_GETUID => sys_getuid(),
|
SYS_GETUID => sys_getuid(),
|
||||||
@ -131,6 +138,7 @@ pub fn syscall_dispatch(
|
|||||||
SYS_GETTID => sys_gettid(),
|
SYS_GETTID => sys_gettid(),
|
||||||
SYS_EXIT_GROUP => sys_exit_group(args[0]),
|
SYS_EXIT_GROUP => sys_exit_group(args[0]),
|
||||||
SYS_TGKILL => sys_tgkill(args[0], args[1], args[2]),
|
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),
|
_ => panic!("Unsupported syscall number: {}", syscall_number),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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_ptr as Vaddr,
|
||||||
user_buf_len as usize,
|
user_buf_len as usize,
|
||||||
);
|
);
|
||||||
SyscallResult::Return(res as i32)
|
SyscallResult::Return(res as _)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// do sys readlink
|
/// do sys readlink
|
||||||
|
24
src/kxos-std/src/syscall/wait4.rs
Normal file
24
src/kxos-std/src/syscall/wait4.rs
Normal file
@ -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 _)
|
||||||
|
}
|
18
src/kxos-std/src/syscall/waitid.rs
Normal file
18
src/kxos-std/src/syscall/waitid.rs
Normal file
@ -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)
|
||||||
|
}
|
@ -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);
|
info!("Error message from user mode: {:?}", content);
|
||||||
}
|
}
|
||||||
|
|
||||||
SyscallResult::Return(user_buf_len as i32)
|
SyscallResult::Return(user_buf_len as _)
|
||||||
} else {
|
} else {
|
||||||
panic!("Unsupported fd number {}", fd);
|
panic!("Unsupported fd number {}", fd);
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ pub struct IoVec {
|
|||||||
pub fn sys_writev(fd: u64, io_vec_addr: u64, io_vec_count: u64) -> SyscallResult {
|
pub fn sys_writev(fd: u64, io_vec_addr: u64, io_vec_count: u64) -> SyscallResult {
|
||||||
debug!("[syscall][id={}][SYS_WRITEV]", SYS_WRITEV);
|
debug!("[syscall][id={}][SYS_WRITEV]", SYS_WRITEV);
|
||||||
let res = do_sys_writev(fd, io_vec_addr as Vaddr, io_vec_count as usize);
|
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 {
|
pub fn do_sys_writev(fd: u64, io_vec_addr: Vaddr, io_vec_count: usize) -> usize {
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:6d1d5aa2ef1105eb716219002763836a47e95cef22997064f6f60bfd4c7a43de
|
oid sha256:cea612414dc19fcd03b563607ea9a453a3d3390b9f3b229ef8e56b08e4d4c8c5
|
||||||
size 9528
|
size 9592
|
||||||
|
@ -10,6 +10,7 @@ _start:
|
|||||||
je _child # child process
|
je _child # child process
|
||||||
jmp _parent # parent process
|
jmp _parent # parent process
|
||||||
_parent:
|
_parent:
|
||||||
|
call wait_child
|
||||||
call get_pid
|
call get_pid
|
||||||
call print_parent_message
|
call print_parent_message
|
||||||
call exit
|
call exit
|
||||||
@ -17,6 +18,16 @@ _child:
|
|||||||
call get_pid
|
call get_pid
|
||||||
call print_child_message
|
call print_child_message
|
||||||
call exit
|
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:
|
exit:
|
||||||
mov $60, %rax # syscall number of exit
|
mov $60, %rax # syscall number of exit
|
||||||
mov $0, %rdi # exit code
|
mov $0, %rdi # exit code
|
||||||
|
Loading…
x
Reference in New Issue
Block a user