From f86cd1e6bc151451e41508d8a1a6e37ac837b24f Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Mon, 31 Oct 2022 16:14:41 +0800 Subject: [PATCH] add basic support for signal --- src/kxos-std/src/prelude.rs | 3 +- src/kxos-std/src/process/clone.rs | 24 ++- src/kxos-std/src/process/mod.rs | 126 ++++++++++++--- src/kxos-std/src/process/process_filter.rs | 5 +- src/kxos-std/src/process/process_group.rs | 53 +++++++ src/kxos-std/src/process/signal/constants.rs | 57 +++++++ src/kxos-std/src/process/signal/mod.rs | 59 ++++++++ src/kxos-std/src/process/signal/sig_action.rs | 94 ++++++++++++ .../src/process/signal/sig_disposition.rs | 49 ++++++ src/kxos-std/src/process/signal/sig_mask.rs | 45 ++++++ src/kxos-std/src/process/signal/sig_num.rs | 28 ++++ src/kxos-std/src/process/signal/sig_queues.rs | 143 ++++++++++++++++++ .../src/process/signal/signals/mod.rs | 10 ++ .../src/process/signal/signals/user.rs | 49 ++++++ src/kxos-std/src/process/status.rs | 20 +++ src/kxos-std/src/process/table.rs | 26 +++- src/kxos-std/src/process/task.rs | 29 ++-- src/kxos-std/src/syscall/execve.rs | 2 + src/kxos-std/src/syscall/exit.rs | 6 +- src/kxos-std/src/syscall/exit_group.rs | 9 +- src/kxos-std/src/syscall/kill.rs | 55 +++++++ src/kxos-std/src/syscall/mod.rs | 21 ++- src/kxos-std/src/syscall/wait4.rs | 2 +- 23 files changed, 855 insertions(+), 60 deletions(-) create mode 100644 src/kxos-std/src/process/process_group.rs create mode 100644 src/kxos-std/src/process/signal/constants.rs create mode 100644 src/kxos-std/src/process/signal/mod.rs create mode 100644 src/kxos-std/src/process/signal/sig_action.rs create mode 100644 src/kxos-std/src/process/signal/sig_disposition.rs create mode 100644 src/kxos-std/src/process/signal/sig_mask.rs create mode 100644 src/kxos-std/src/process/signal/sig_num.rs create mode 100644 src/kxos-std/src/process/signal/sig_queues.rs create mode 100644 src/kxos-std/src/process/signal/signals/mod.rs create mode 100644 src/kxos-std/src/process/signal/signals/user.rs create mode 100644 src/kxos-std/src/syscall/kill.rs diff --git a/src/kxos-std/src/prelude.rs b/src/kxos-std/src/prelude.rs index c8500616c..a05ad1e94 100644 --- a/src/kxos-std/src/prelude.rs +++ b/src/kxos-std/src/prelude.rs @@ -2,18 +2,17 @@ pub(crate) use alloc::boxed::Box; pub(crate) use alloc::collections::BTreeMap; +pub(crate) use alloc::collections::LinkedList; pub(crate) use alloc::collections::VecDeque; pub(crate) use alloc::ffi::CString; pub(crate) use alloc::sync::Arc; pub(crate) use alloc::sync::Weak; -#[allow(unused)] pub(crate) use alloc::vec; pub(crate) use alloc::vec::Vec; pub(crate) use bitflags::bitflags; pub(crate) use core::ffi::CStr; pub(crate) use kxos_frame::config::PAGE_SIZE; pub(crate) use kxos_frame::vm::Vaddr; -#[allow(unused)] pub(crate) use kxos_frame::{debug, error, info, trace, warn}; pub(crate) use spin::Mutex; diff --git a/src/kxos-std/src/process/clone.rs b/src/kxos-std/src/process/clone.rs index c647f1d4e..cfaa97d31 100644 --- a/src/kxos-std/src/process/clone.rs +++ b/src/kxos-std/src/process/clone.rs @@ -6,7 +6,7 @@ use kxos_frame::{ use crate::{ prelude::*, - process::{new_pid, table, task::create_new_task}, + process::{new_pid, signal::sig_queues::SigQueues, table, task::create_new_task}, }; use super::Process; @@ -127,6 +127,13 @@ pub fn clone_child(parent_context: CpuContext, clone_args: CloneArgs) -> Result< debug!("child process pid: {}", child_pid); debug!("rip = 0x{:x}", child_cpu_context.gp_regs.rip); + // inherit parent's sig disposition + let child_sig_dispositions = current.sig_dispositions().lock().clone(); + // sig queue is set empty + let child_sig_queues = SigQueues::new(); + // inherit parent's sig mask + let child_sig_mask = current.sig_mask().lock().clone(); + let child = Arc::new_cyclic(|child_process_ref| { let weak_child_process = child_process_ref.clone(); let child_task = create_new_task(child_user_space.clone(), weak_child_process); @@ -136,8 +143,23 @@ pub fn clone_child(parent_context: CpuContext, clone_args: CloneArgs) -> Result< child_file_name, child_user_vm, Some(child_user_space), + None, + child_sig_dispositions, + child_sig_queues, + child_sig_mask, ) }); + // Inherit parent's process group + let parent_process_group = current + .process_group() + .lock() + .as_ref() + .map(|ppgrp| ppgrp.upgrade()) + .flatten() + .unwrap(); + parent_process_group.add_process(child.clone()); + child.set_process_group(Arc::downgrade(&parent_process_group)); + Process::current().add_child(child.clone()); table::add_process(child_pid, child.clone()); deal_with_clone_args(clone_args, &child)?; diff --git a/src/kxos-std/src/process/mod.rs b/src/kxos-std/src/process/mod.rs index f3dddd5c0..b34fca73c 100644 --- a/src/kxos-std/src/process/mod.rs +++ b/src/kxos-std/src/process/mod.rs @@ -5,9 +5,13 @@ use kxos_frame::sync::WaitQueue; use kxos_frame::{task::Task, user::UserSpace, vm::VmSpace}; use self::process_filter::ProcessFilter; +use self::process_group::ProcessGroup; use self::process_vm::mmap_area::MmapArea; use self::process_vm::user_heap::UserHeap; use self::process_vm::UserVm; +use self::signal::sig_disposition::SigDispositions; +use self::signal::sig_mask::SigMask; +use self::signal::sig_queues::SigQueues; use self::status::ProcessStatus; use self::task::create_user_task_from_elf; @@ -15,7 +19,9 @@ pub mod clone; pub mod elf; pub mod fifo_scheduler; pub mod process_filter; +pub mod process_group; pub mod process_vm; +pub mod signal; pub mod status; pub mod table; pub mod task; @@ -47,6 +53,14 @@ pub struct Process { parent: Mutex>>, /// Children processes children: Mutex>>, + /// Process group + process_group: Mutex>>, + + // Signal + sig_dispositions: Mutex, + sig_queues: Mutex, + /// Process-level sigmask + sig_mask: Mutex, } impl Process { @@ -69,6 +83,10 @@ impl Process { exec_filename: Option, user_vm: Option, user_space: Option>, + process_group: Option>, + sig_dispositions: SigDispositions, + sig_queues: SigQueues, + sig_mask: SigMask, ) -> Self { let parent = if pid == 0 { debug!("Init process does not has parent"); @@ -91,6 +109,10 @@ impl Process { status: Mutex::new(ProcessStatus::Runnable), parent: Mutex::new(parent), children: Mutex::new(children), + process_group: Mutex::new(process_group), + sig_dispositions: Mutex::new(sig_dispositions), + sig_queues: Mutex::new(sig_queues), + sig_mask: Mutex::new(sig_mask), } } @@ -124,8 +146,23 @@ impl Process { let task = create_user_task_from_elf(filename, elf_file_content, weak_process); let user_space = task.user_space().map(|user_space| user_space.clone()); let user_vm = UserVm::new(); - Process::new(pid, task, cloned_filename, Some(user_vm), user_space) + let sig_dispositions = SigDispositions::new(); + let sig_queues = SigQueues::new(); + let sig_mask = SigMask::new_empty(); + Process::new( + pid, + task, + cloned_filename, + Some(user_vm), + user_space, + None, + sig_dispositions, + sig_queues, + sig_mask, + ) }); + // Set process group + user_process.create_and_set_process_group(); table::add_process(pid, user_process.clone()); user_process } @@ -138,8 +175,22 @@ impl Process { let kernel_process = Arc::new_cyclic(|weak_process_ref| { let weak_process = weak_process_ref.clone(); let task = Task::new(task_fn, weak_process, None).expect("spawn kernel task failed"); - Process::new(pid, task, None, None, None) + let sig_dispositions = SigDispositions::new(); + let sig_queues = SigQueues::new(); + let sig_mask = SigMask::new_empty(); + Process::new( + pid, + task, + None, + None, + None, + None, + sig_dispositions, + sig_queues, + sig_mask, + ) }); + kernel_process.create_and_set_process_group(); table::add_process(pid, kernel_process.clone()); kernel_process } @@ -151,7 +202,21 @@ impl Process { /// returns the process group id of the process pub fn pgid(&self) -> Pgid { - todo!() + if let Some(process_group) = self + .process_group + .lock() + .as_ref() + .map(|process_group| process_group.upgrade()) + .flatten() + { + process_group.pgid() + } else { + 0 + } + } + + pub fn process_group(&self) -> &Mutex>> { + &self.process_group } /// add a child process @@ -165,6 +230,23 @@ impl Process { let _ = self.parent.lock().insert(parent); } + pub fn set_process_group(&self, process_group: Weak) { + if self.process_group.lock().is_none() { + let _ = self.process_group.lock().insert(process_group); + } else { + todo!("We should do something with old group") + } + } + + /// create a new process group for the process and add it to globle table. + /// Then set the process group for current process. + fn create_and_set_process_group(self: &Arc) { + let process_group = Arc::new(ProcessGroup::new(self.clone())); + let pgid = process_group.pgid(); + self.set_process_group(Arc::downgrade(&process_group)); + table::add_process_group(pgid, process_group); + } + fn parent(&self) -> Option> { self.parent .lock() @@ -173,17 +255,13 @@ impl Process { .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); - } - - /// 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) { + /// Exit current process. + /// Set the status of current process as Zombie and set exit code. + /// Move all children to init process. + /// Wake up the parent wait queue if parent is waiting for self. + pub fn exit(&self, exit_code: i32) { self.status.lock().set_zombie(); + self.exit_code.store(exit_code, Ordering::Relaxed); // move children to the init process let current_process = Process::current(); if !current_process.is_init_process() { @@ -267,15 +345,15 @@ impl 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); - table::delete_process(child_process.pid()); + assert!(child_process.status().lock().is_zombie()); + table::remove_process(child_process.pid()); 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() { + if child_process.status().lock().is_zombie() { return Some(child_process.clone()); } } @@ -295,8 +373,20 @@ impl Process { self.filename.as_ref() } - pub fn status(&self) -> ProcessStatus { - self.status.lock().clone() + pub fn status(&self) -> &Mutex { + &self.status + } + + pub fn sig_dispositions(&self) -> &Mutex { + &self.sig_dispositions + } + + pub fn sig_queues(&self) -> &Mutex { + &self.sig_queues + } + + pub fn sig_mask(&self) -> &Mutex { + &self.sig_mask } } diff --git a/src/kxos-std/src/process/process_filter.rs b/src/kxos-std/src/process/process_filter.rs index 6c7054d8c..4db223100 100644 --- a/src/kxos-std/src/process/process_filter.rs +++ b/src/kxos-std/src/process/process_filter.rs @@ -20,9 +20,10 @@ impl ProcessFilter { } } - // used for wait4 - pub fn from_wait_pid(wait_pid: isize) -> Self { + // used for wait4 and kill + pub fn from_id(wait_pid: isize) -> Self { // https://man7.org/linux/man-pages/man2/waitpid.2.html + // https://man7.org/linux/man-pages/man2/kill.2.html if wait_pid < -1 { // process group ID is equal to the absolute value of pid. ProcessFilter::WithPgid((-wait_pid) as Pgid) diff --git a/src/kxos-std/src/process/process_group.rs b/src/kxos-std/src/process/process_group.rs new file mode 100644 index 000000000..8444fe683 --- /dev/null +++ b/src/kxos-std/src/process/process_group.rs @@ -0,0 +1,53 @@ +use super::{Pgid, Pid, Process}; +use crate::prelude::*; + +pub struct ProcessGroup { + inner: Mutex, +} + +struct ProcessGroupInner { + pgid: Pgid, + processes: BTreeMap>, + leader_process: Option>, +} + +impl ProcessGroup { + fn default() -> Self { + ProcessGroup { + inner: Mutex::new(ProcessGroupInner { + pgid: 0, + processes: BTreeMap::new(), + leader_process: None, + }), + } + } + + pub fn new(process: Arc) -> Self { + let process_group = ProcessGroup::default(); + let pid = process.pid(); + process_group.set_pgid(pid); + process_group.add_process(process.clone()); + process_group.set_leader_process(process); + process_group + } + + pub fn set_pgid(&self, pgid: Pgid) { + self.inner.lock().pgid = pgid; + } + + pub fn set_leader_process(&self, leader_process: Arc) { + self.inner.lock().leader_process = Some(leader_process); + } + + pub fn add_process(&self, process: Arc) { + self.inner.lock().processes.insert(process.pid(), process); + } + + pub fn remove_process(&self, pid: Pid) { + self.inner.lock().processes.remove(&pid); + } + + pub fn pgid(&self) -> Pgid { + self.inner.lock().pgid + } +} diff --git a/src/kxos-std/src/process/signal/constants.rs b/src/kxos-std/src/process/signal/constants.rs new file mode 100644 index 000000000..7f2a3752c --- /dev/null +++ b/src/kxos-std/src/process/signal/constants.rs @@ -0,0 +1,57 @@ +/// Standard signals +pub(super) const MIN_STD_SIG_NUM: u8 = 1; +pub(super) const MAX_STD_SIG_NUM: u8 = 31; // inclusive +/// Real-time signals +pub(super) const MIN_RT_SIG_NUM: u8 = 32; +pub(super) const MAX_RT_SIG_NUM: u8 = 64; // inclusive +/// Count the number of signals +pub(super) const COUNT_STD_SIGS: usize = 31; +pub(super) const COUNT_RT_SIGS: usize = 33; +pub(super) const COUNT_ALL_SIGS: usize = 64; + +pub const SIG_DFL: usize = 0; +pub const SIG_IGN: usize = 1; + +use super::sig_num::SigNum; + +macro_rules! define_std_signums { + ( $( $name: ident = $num: expr ),+, ) => { + $( + pub const $name : SigNum = SigNum::from_u8($num); + )* + } +} + +define_std_signums! { + SIGHUP = 1, // Hangup detected on controlling terminal or death of controlling process + SIGINT = 2, // Interrupt from keyboard + SIGQUIT = 3, // Quit from keyboard + SIGILL = 4, // Illegal Instruction + SIGTRAP = 5, // Trace/breakpoint trap + SIGABRT = 6, // Abort signal from abort(3) + SIGBUS = 7, // Bus error (bad memory access) + SIGFPE = 8, // Floating-point exception + SIGKILL = 9, // Kill signal + SIGUSR1 = 10, // User-defined signal 1 + SIGSEGV = 11, // Invalid memory reference + SIGUSR2 = 12, // User-defined signal 2 + SIGPIPE = 13, // Broken pipe: write to pipe with no readers; see pipe(7) + SIGALRM = 14, // Timer signal from alarm(2) + SIGTERM = 15, // Termination signal + SIGSTKFLT = 16, // Stack fault on coprocessor (unused) + SIGCHLD = 17, // Child stopped or terminated + SIGCONT = 18, // Continue if stopped + SIGSTOP = 19, // Stop process + SIGTSTP = 20, // Stop typed at terminal + SIGTTIN = 21, // Terminal input for background process + SIGTTOU = 22, // Terminal output for background process + SIGURG = 23, // Urgent condition on socket (4.2BSD) + SIGXCPU = 24, // CPU time limit exceeded (4.2BSD); see setrlimit(2) + SIGXFSZ = 25, // File size limit exceeded (4.2BSD); see setrlimit(2) + SIGVTALRM = 26, // Virtual alarm clock (4.2BSD) + SIGPROF = 27, // Profiling timer expired + SIGWINCH = 28, // Window resize signal (4.3BSD, Sun) + SIGIO = 29, // I/O now possible (4.2BSD) + SIGPWR = 30, // Power failure (System V) + SIGSYS = 31, // Bad system call (SVr4); see also seccomp(2) +} diff --git a/src/kxos-std/src/process/signal/mod.rs b/src/kxos-std/src/process/signal/mod.rs new file mode 100644 index 000000000..2743e79b3 --- /dev/null +++ b/src/kxos-std/src/process/signal/mod.rs @@ -0,0 +1,59 @@ +pub mod constants; +pub mod sig_action; +pub mod sig_disposition; +pub mod sig_mask; +pub mod sig_num; +pub mod sig_queues; +pub mod signals; + +use crate::{ + prelude::*, + process::signal::sig_action::{SigAction, SigDefaultAction}, +}; + +/// Handle pending signal for current process +pub fn handle_pending_signal() { + let current = current!(); + let sig_queues = current.sig_queues(); + let mut sig_queues_guard = sig_queues.lock(); + let sig_mask = current.sig_mask().lock().clone(); + if let Some(signal) = sig_queues_guard.dequeue(&sig_mask) { + let sig_num = signal.num(); + debug!("sig_num = {:?}", sig_num); + let sig_action = current.sig_dispositions().lock().get(sig_num); + match sig_action { + SigAction::Ign => { + debug!("Ignore signal {:?}", sig_num); + } + SigAction::User { .. } => todo!(), + SigAction::Dfl => { + let sig_default_action = SigDefaultAction::from_signum(sig_num); + match sig_default_action { + SigDefaultAction::Core | SigDefaultAction::Term => { + // FIXME: How to set correct status if process is terminated + current.exit(1); + } + SigDefaultAction::Ign => {} + SigDefaultAction::Stop => { + let mut status_guard = current.status().lock(); + if status_guard.is_runnable() { + status_guard.set_suspend(); + } else { + panic!("Try to suspend a not running process.") + } + drop(status_guard); + } + SigDefaultAction::Cont => { + let mut status_guard = current.status().lock(); + if status_guard.is_suspend() { + status_guard.set_runnable(); + } else { + panic!("Try to continue a not suspended process.") + } + drop(status_guard); + } + } + } + } + } +} diff --git a/src/kxos-std/src/process/signal/sig_action.rs b/src/kxos-std/src/process/signal/sig_action.rs new file mode 100644 index 000000000..27a9e3a2a --- /dev/null +++ b/src/kxos-std/src/process/signal/sig_action.rs @@ -0,0 +1,94 @@ +use super::{constants::*, sig_mask::SigMask, sig_num::SigNum}; +use bitflags::bitflags; +use kxos_frame::warn; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum SigAction { + Dfl, // Default action + Ign, // Ignore this signal + User { + // User-given handler + handler_addr: usize, + flags: SigActionFlags, + restorer_addr: usize, + mask: SigMask, + }, +} + +impl Default for SigAction { + fn default() -> Self { + SigAction::Dfl + } +} + +bitflags! { + pub struct SigActionFlags: u32 { + const SA_NOCLDSTOP = 1; + const SA_NOCLDWAIT = 2; + const SA_SIGINFO = 4; + const SA_ONSTACK = 0x08000000; + const SA_RESTART = 0x10000000; + const SA_NODEFER = 0x40000000; + const SA_RESETHAND = 0x80000000; + const SA_RESTORER = 0x04000000; + } +} + +impl TryFrom for SigActionFlags { + type Error = &'static str; + + fn try_from(bits: u32) -> Result { + let flags = SigActionFlags::from_bits(bits).ok_or_else(|| "invalid sigaction flags")?; + if flags.contains(SigActionFlags::SA_RESTART) { + warn!("SA_RESTART is not supported"); + } + Ok(flags) + } +} + +impl SigActionFlags { + pub fn to_u32(&self) -> u32 { + self.bits() + } +} + +/// The default action to signals +#[derive(Debug, Copy, Clone)] +pub enum SigDefaultAction { + Term, // Default action is to terminate the process. + Ign, // Default action is to ignore the signal. + Core, // Default action is to terminate the process and dump core (see core(5)). + Stop, // Default action is to stop the process. + Cont, // Default action is to continue the process if it is currently stopped. +} + +impl SigDefaultAction { + pub fn from_signum(num: SigNum) -> SigDefaultAction { + match num { + SIGABRT | // = SIGIOT + SIGBUS | + SIGFPE | + SIGILL | + SIGQUIT | + SIGSEGV | + SIGSYS | // = SIGUNUSED + SIGTRAP | + SIGXCPU | + SIGXFSZ + => SigDefaultAction::Core, + SIGCHLD | + SIGURG | + SIGWINCH + => SigDefaultAction::Ign, + SIGCONT + => SigDefaultAction::Cont, + SIGSTOP | + SIGTSTP | + SIGTTIN | + SIGTTOU + => SigDefaultAction::Stop, + _ + => SigDefaultAction::Term, + } + } +} diff --git a/src/kxos-std/src/process/signal/sig_disposition.rs b/src/kxos-std/src/process/signal/sig_disposition.rs new file mode 100644 index 000000000..1c67904d2 --- /dev/null +++ b/src/kxos-std/src/process/signal/sig_disposition.rs @@ -0,0 +1,49 @@ +use super::{constants::*, sig_action::SigAction, sig_num::SigNum}; + +#[derive(Copy, Clone)] +pub struct SigDispositions { + // SigNum -> SigAction + map: [SigAction; COUNT_ALL_SIGS], +} + +impl SigDispositions { + pub fn new() -> Self { + Self { + map: [SigAction::default(); COUNT_ALL_SIGS], + } + } + + pub fn get(&self, num: SigNum) -> SigAction { + let idx = Self::num_to_idx(num); + self.map[idx] + } + + pub fn set(&mut self, num: SigNum, sa: SigAction) { + let idx = Self::num_to_idx(num); + self.map[idx] = sa; + } + + pub fn set_default(&mut self, num: SigNum) { + let idx = Self::num_to_idx(num); + self.map[idx] = SigAction::Dfl; + } + + /// man 7 signal: + /// When execve, the handled signals are reset to the default; the dispositions of + /// ignored signals are left unchanged. + /// This function should be used when execve. + pub fn inherit(&mut self) { + for sigaction in &mut self.map { + match sigaction { + SigAction::User { .. } => { + *sigaction = SigAction::Dfl; + } + _ => {} + } + } + } + + fn num_to_idx(num: SigNum) -> usize { + (num.as_u8() - MIN_STD_SIG_NUM) as usize + } +} diff --git a/src/kxos-std/src/process/signal/sig_mask.rs b/src/kxos-std/src/process/signal/sig_mask.rs new file mode 100644 index 000000000..0b0d998f2 --- /dev/null +++ b/src/kxos-std/src/process/signal/sig_mask.rs @@ -0,0 +1,45 @@ +use super::{constants::MIN_STD_SIG_NUM, sig_num::SigNum}; + +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] +pub struct SigMask { + bits: u64, +} + +impl SigMask { + pub const fn from_u64(bits: u64) -> Self { + SigMask { bits } + } + + pub const fn new_empty() -> Self { + SigMask::from_u64(0) + } + + pub const fn new_full() -> Self { + SigMask::from_u64(!0) + } + + pub const fn as_u64(&self) -> u64 { + self.bits + } + + pub const fn empty(&self) -> bool { + self.bits == 0 + } + + pub const fn full(&self) -> bool { + self.bits == !0 + } + + pub fn count(&self) -> usize { + self.bits.count_ones() as usize + } + + pub fn contains(&self, signum: SigNum) -> bool { + let idx = Self::num_to_idx(signum); + (self.bits & (1_u64 << idx)) != 0 + } + + fn num_to_idx(num: SigNum) -> usize { + (num.as_u8() - MIN_STD_SIG_NUM) as usize + } +} diff --git a/src/kxos-std/src/process/signal/sig_num.rs b/src/kxos-std/src/process/signal/sig_num.rs new file mode 100644 index 000000000..1facf40ab --- /dev/null +++ b/src/kxos-std/src/process/signal/sig_num.rs @@ -0,0 +1,28 @@ +use super::constants::*; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SigNum { + sig_num: u8, +} + +impl SigNum { + // Safety: This function should only be used when signum is ensured to be valid. + pub const fn from_u8(sig_num: u8) -> Self { + if sig_num > MAX_RT_SIG_NUM || sig_num < MIN_STD_SIG_NUM { + unreachable!() + } + SigNum { sig_num } + } + + pub const fn as_u8(&self) -> u8 { + self.sig_num + } + + pub fn is_std(&self) -> bool { + self.sig_num <= MAX_STD_SIG_NUM + } + + pub fn is_real_time(&self) -> bool { + self.sig_num >= MIN_RT_SIG_NUM + } +} diff --git a/src/kxos-std/src/process/signal/sig_queues.rs b/src/kxos-std/src/process/signal/sig_queues.rs new file mode 100644 index 000000000..eef4db07b --- /dev/null +++ b/src/kxos-std/src/process/signal/sig_queues.rs @@ -0,0 +1,143 @@ +use super::constants::*; +use crate::prelude::*; + +use super::sig_mask::SigMask; +use super::sig_num::SigNum; +use super::signals::Signal; + +pub struct SigQueues { + count: usize, + std_queues: Vec>>, + rt_queues: Vec>>, +} + +impl SigQueues { + pub fn new() -> Self { + let count = 0; + let std_queues = (0..COUNT_STD_SIGS).map(|_| None).collect(); + let rt_queues = (0..COUNT_RT_SIGS).map(|_| Default::default()).collect(); + // let notifier = Notifier::new(); + SigQueues { + count, + std_queues, + rt_queues, + } + } + + pub fn empty(&self) -> bool { + self.count == 0 + } + + pub fn enqueue(&mut self, signal: Box) { + let signum = signal.num(); + if signum.is_std() { + // Standard signals + // + // From signal(7): + // + // Standard signals do not queue. If multiple instances of a standard + // signal are generated while that signal is blocked, then only one + // instance of the signal is marked as pending (and the signal will be + // delivered just once when it is unblocked). In the case where a + // standard signal is already pending, the siginfo_t structure (see + // sigaction(2)) associated with that signal is not overwritten on + // arrival of subsequent instances of the same signal. Thus, the + // process will receive the information associated with the first + // instance of the signal. + let queue = self.get_std_queue_mut(signum); + if queue.is_some() { + // If there is already a signal pending, just ignore all subsequent signals + return; + } + *queue = Some(signal); + self.count += 1; + } else { + // Real-time signals + let queue = self.get_rt_queue_mut(signum); + queue.push_back(signal); + self.count += 1; + } + + // self.notifier.broadcast(&signum); + } + + pub fn dequeue(&mut self, blocked: &SigMask) -> Option> { + // Fast path for the common case of no pending signals + if self.empty() { + return None; + } + + // Deliver standard signals. + // + // According to signal(7): + // If both standard and real-time signals are pending for a process, + // POSIX leaves it unspecified which is delivered first. Linux, like + // many other implementations, gives priority to standard signals in + // this case. + + // POSIX leaves unspecified which to deliver first if there are multiple + // pending standard signals. So we are free to define our own. The + // principle is to give more urgent signals higher priority (like SIGKILL). + const ORDERED_STD_SIGS: [SigNum; COUNT_STD_SIGS] = [ + SIGKILL, SIGTERM, SIGSTOP, SIGCONT, SIGSEGV, SIGILL, SIGHUP, SIGINT, SIGQUIT, SIGTRAP, + SIGABRT, SIGBUS, SIGFPE, SIGUSR1, SIGUSR2, SIGPIPE, SIGALRM, SIGSTKFLT, SIGCHLD, + SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM, SIGPROF, SIGWINCH, + SIGIO, SIGPWR, SIGSYS, + ]; + for &signum in &ORDERED_STD_SIGS { + if blocked.contains(signum) { + continue; + } + + let queue = self.get_std_queue_mut(signum); + let signal = queue.take(); + if signal.is_some() { + self.count -= 1; + return signal; + } + } + + // If no standard signals, then deliver real-time signals. + // + // According to signal (7): + // Real-time signals are delivered in a guaranteed order. Multiple + // real-time signals of the same type are delivered in the order + // they were sent. If different real-time signals are sent to a + // process, they are delivered starting with the lowest-numbered + // signal. (I.e., low-numbered signals have highest priority.) + for signum in MIN_RT_SIG_NUM..=MAX_RT_SIG_NUM { + let signum = SigNum::from_u8(signum); + if blocked.contains(signum) { + continue; + } + + let queue = self.get_rt_queue_mut(signum); + let signal = queue.pop_front(); + if signal.is_some() { + self.count -= 1; + return signal; + } + } + + // There must be pending but blocked signals + None + } + + fn get_std_queue_mut(&mut self, signum: SigNum) -> &mut Option> { + debug_assert!(signum.is_std()); + let idx = (signum.as_u8() - MIN_STD_SIG_NUM) as usize; + &mut self.std_queues[idx] + } + + fn get_rt_queue_mut(&mut self, signum: SigNum) -> &mut VecDeque> { + debug_assert!(signum.is_real_time()); + let idx = (signum.as_u8() - MIN_RT_SIG_NUM) as usize; + &mut self.rt_queues[idx] + } +} + +impl Default for SigQueues { + fn default() -> Self { + Self::new() + } +} diff --git a/src/kxos-std/src/process/signal/signals/mod.rs b/src/kxos-std/src/process/signal/signals/mod.rs new file mode 100644 index 000000000..064f1c127 --- /dev/null +++ b/src/kxos-std/src/process/signal/signals/mod.rs @@ -0,0 +1,10 @@ +pub mod user; + +use core::fmt::Debug; + +use super::sig_num::SigNum; + +pub trait Signal: Send + Sync + Debug { + /// Returns the number of the signal. + fn num(&self) -> SigNum; +} diff --git a/src/kxos-std/src/process/signal/signals/user.rs b/src/kxos-std/src/process/signal/signals/user.rs new file mode 100644 index 000000000..cf0fa115f --- /dev/null +++ b/src/kxos-std/src/process/signal/signals/user.rs @@ -0,0 +1,49 @@ +use crate::process::{signal::sig_num::SigNum, Pid}; + +use super::Signal; + +pub type Uid = usize; + +#[derive(Debug, Clone, Copy)] +pub struct UserSignal { + num: SigNum, + pid: Pid, + uid: Uid, + kind: UserSignalKind, +} + +#[derive(Debug, Copy, Clone)] +pub enum UserSignalKind { + Kill, + Tkill, + Sigqueue, +} + +impl UserSignal { + pub fn new(num: SigNum, kind: UserSignalKind, pid: Pid, uid: Uid) -> Self { + Self { + num, + kind, + pid, + uid, + } + } + + pub fn pid(&self) -> Pid { + self.pid + } + + pub fn uid(&self) -> Uid { + self.uid + } + + pub fn kind(&self) -> UserSignalKind { + self.kind + } +} + +impl Signal for UserSignal { + fn num(&self) -> SigNum { + self.num + } +} diff --git a/src/kxos-std/src/process/status.rs b/src/kxos-std/src/process/status.rs index 7f60f7e63..f9c666ed1 100644 --- a/src/kxos-std/src/process/status.rs +++ b/src/kxos-std/src/process/status.rs @@ -2,7 +2,11 @@ #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ProcessStatus { + /// Can be scheduled to run Runnable, + /// Suspend until be woken by SIGCONT signal + SuspendSignalable, + /// Exit while not reaped by parent Zombie, } @@ -14,4 +18,20 @@ impl ProcessStatus { pub fn is_zombie(&self) -> bool { *self == ProcessStatus::Zombie } + + pub fn set_suspend(&mut self) { + *self = ProcessStatus::SuspendSignalable; + } + + pub fn is_suspend(&self) -> bool { + *self == ProcessStatus::SuspendSignalable + } + + pub fn set_runnable(&mut self) { + *self = ProcessStatus::Runnable; + } + + pub fn is_runnable(&self) -> bool { + *self == ProcessStatus::Runnable + } } diff --git a/src/kxos-std/src/process/table.rs b/src/kxos-std/src/process/table.rs index 386381cfb..7b06f0d3b 100644 --- a/src/kxos-std/src/process/table.rs +++ b/src/kxos-std/src/process/table.rs @@ -4,10 +4,12 @@ use crate::prelude::*; -use super::{Pid, Process}; +use super::{process_group::ProcessGroup, Pgid, Pid, Process}; lazy_static! { static ref PROCESS_TABLE: Mutex>> = Mutex::new(BTreeMap::new()); + static ref PROCESS_GROUP_TABLE: Mutex>> = + Mutex::new(BTreeMap::new()); } /// add a process to global table @@ -15,8 +17,8 @@ pub fn add_process(pid: Pid, process: Arc) { PROCESS_TABLE.lock().insert(pid, process); } -/// delete a process from global table -pub fn delete_process(pid: Pid) { +/// remove a process from global table +pub fn remove_process(pid: Pid) { PROCESS_TABLE.lock().remove(&pid); } @@ -36,3 +38,21 @@ pub fn get_all_processes() -> Vec> { .map(|(_, process)| process.clone()) .collect() } + +/// add process group to global table +pub fn add_process_group(pgid: Pgid, process_group: Arc) { + PROCESS_GROUP_TABLE.lock().insert(pgid, process_group); +} + +/// remove process group from global table +pub fn remove_process_group(pgid: Pgid) { + PROCESS_GROUP_TABLE.lock().remove(&pgid); +} + +/// get a process group with pgid +pub fn pgid_to_process_group(pgid: Pgid) -> Option> { + PROCESS_GROUP_TABLE + .lock() + .get(&pgid) + .map(|process_group| process_group.clone()) +} diff --git a/src/kxos-std/src/process/task.rs b/src/kxos-std/src/process/task.rs index c4cc4fbcd..286e4eea6 100644 --- a/src/kxos-std/src/process/task.rs +++ b/src/kxos-std/src/process/task.rs @@ -7,7 +7,7 @@ use kxos_frame::{ vm::VmSpace, }; -use crate::prelude::*; +use crate::{prelude::*, process::signal::handle_pending_signal}; use crate::syscall::syscall_handler; @@ -47,28 +47,33 @@ pub fn create_new_task(userspace: Arc, parent: Weak) -> Arc< loop { let user_event = user_mode.execute(); let context = user_mode.context_mut(); - if let HandlerResult::Exit = handle_user_event(user_event, context) { - // FIXME: How to set task status? How to set exit code of process? + // handle user event: + handle_user_event(user_event, context); + let current = current!(); + // should be do this comparison before handle signal? + if current.status().lock().is_zombie() { break; } - // debug!("before return to user space: {:#x?}", context); + handle_pending_signal(); + if current.status().lock().is_zombie() { + break; + } + // If current is suspended, wait for a signal to wake up self + while current.status().lock().is_suspend() { + Process::yield_now(); + debug!("{} is suspended.", current.pid()); + handle_pending_signal(); + } } - let current_process = Process::current(); - current_process.exit(); } Task::new(user_task_entry, parent, Some(userspace)).expect("spawn task failed") } -fn handle_user_event(user_event: UserEvent, context: &mut CpuContext) -> HandlerResult { +fn handle_user_event(user_event: UserEvent, context: &mut CpuContext) { match user_event { UserEvent::Syscall => syscall_handler(context), UserEvent::Fault => todo!(), UserEvent::Exception => todo!(), } } - -pub enum HandlerResult { - Exit, - Continue, -} diff --git a/src/kxos-std/src/syscall/execve.rs b/src/kxos-std/src/syscall/execve.rs index df6dc92da..fb2f6e1d1 100644 --- a/src/kxos-std/src/syscall/execve.rs +++ b/src/kxos-std/src/syscall/execve.rs @@ -35,6 +35,8 @@ pub fn sys_execve( let elf_load_info = load_elf_to_vm_space(filename, elf_file_content, &vm_space).expect("load elf failed"); debug!("load elf in execve succeeds"); + // set signal disposition to default + current.sig_dispositions().lock().inherit(); // set cpu context to default let defalut_content = CpuContext::default(); context.gp_regs = defalut_content.gp_regs; diff --git a/src/kxos-std/src/syscall/exit.rs b/src/kxos-std/src/syscall/exit.rs index 6c6e87d48..3d0682ff6 100644 --- a/src/kxos-std/src/syscall/exit.rs +++ b/src/kxos-std/src/syscall/exit.rs @@ -1,11 +1,11 @@ use crate::prelude::*; -use crate::{process::Process, syscall::SYS_EXIT}; +use crate::syscall::SYS_EXIT; use super::SyscallResult; pub fn sys_exit(exit_code: i32) -> SyscallResult { debug!("[syscall][id={}][SYS_EXIT]", SYS_EXIT); - Process::current().set_exit_code(exit_code); - SyscallResult::Exit(exit_code) + current!().exit(exit_code); + SyscallResult::NotReturn } diff --git a/src/kxos-std/src/syscall/exit_group.rs b/src/kxos-std/src/syscall/exit_group.rs index 5d292d30c..1ac7bd7cb 100644 --- a/src/kxos-std/src/syscall/exit_group.rs +++ b/src/kxos-std/src/syscall/exit_group.rs @@ -1,12 +1,9 @@ use crate::prelude::*; -use crate::{ - process::Process, - syscall::{SyscallResult, SYS_EXIT_GROUP}, -}; +use crate::syscall::{SyscallResult, SYS_EXIT_GROUP}; 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 _); - SyscallResult::Exit(exit_code as _) + current!().exit(exit_code as _); + SyscallResult::NotReturn } diff --git a/src/kxos-std/src/syscall/kill.rs b/src/kxos-std/src/syscall/kill.rs new file mode 100644 index 000000000..e4af4f220 --- /dev/null +++ b/src/kxos-std/src/syscall/kill.rs @@ -0,0 +1,55 @@ +use crate::prelude::*; + +use crate::process::signal::signals::user::{UserSignal, UserSignalKind}; +use crate::process::{table, Process}; +use crate::{ + process::{process_filter::ProcessFilter, signal::sig_num::SigNum}, + syscall::SYS_KILL, +}; + +use super::SyscallResult; + +pub fn sys_kill(process_filter: u64, sig_num: u64) -> SyscallResult { + debug!("[syscall][id={}][SYS_KILL]", SYS_KILL); + let process_filter = ProcessFilter::from_id(process_filter as _); + let sig_num = SigNum::from_u8(sig_num as _); + let _ = do_sys_kill(process_filter, sig_num); + SyscallResult::Return(0) +} + +pub fn do_sys_kill(process_filter: ProcessFilter, sig_num: SigNum) -> Result<()> { + let current = current!(); + let pid = current.pid(); + // FIXME: use the correct uid + let uid = 0; + let processes = get_processes(&process_filter)?; + for process in processes.iter() { + if process.status().lock().is_zombie() { + continue; + } + + let signal = Box::new(UserSignal::new(sig_num, UserSignalKind::Kill, pid, uid)); + let sig_queues = process.sig_queues(); + sig_queues.lock().enqueue(signal); + } + Ok(()) +} + +fn get_processes(filter: &ProcessFilter) -> Result>> { + let processes = match filter { + ProcessFilter::Any => { + let mut processes = table::get_all_processes(); + processes.retain(|process| process.pid() != 0); + processes + } + ProcessFilter::WithPid(pid) => { + let process = table::pid_to_process(*pid); + match process { + None => return_errno!(Errno::ESRCH), + Some(process) => vec![process], + } + } + ProcessFilter::WithPgid(_) => todo!(), + }; + Ok(processes) +} diff --git a/src/kxos-std/src/syscall/mod.rs b/src/kxos-std/src/syscall/mod.rs index d0ea5dc98..64e283354 100644 --- a/src/kxos-std/src/syscall/mod.rs +++ b/src/kxos-std/src/syscall/mod.rs @@ -3,10 +3,10 @@ use crate::prelude::*; use crate::syscall::clone::sys_clone; +use crate::syscall::kill::sys_kill; use alloc::borrow::ToOwned; use kxos_frame::cpu::CpuContext; -use crate::process::task::HandlerResult; use crate::syscall::access::sys_access; use crate::syscall::arch_prctl::sys_arch_prctl; use crate::syscall::brk::sys_brk; @@ -41,6 +41,7 @@ mod fstat; mod futex; mod getpid; mod gettid; +mod kill; mod mmap; mod mprotect; mod readlink; @@ -68,6 +69,7 @@ const SYS_FORK: u64 = 57; const SYS_EXECVE: u64 = 59; const SYS_EXIT: u64 = 60; const SYS_WAIT4: u64 = 61; +const SYS_KILL: u64 = 62; const SYS_UNAME: u64 = 63; const SYS_READLINK: u64 = 89; const SYS_GETUID: u64 = 102; @@ -87,9 +89,8 @@ pub struct SyscallArgument { } pub enum SyscallResult { - Exit(i32), Return(i32), - ReturnNothing, // execve return nothing + NotReturn, } impl SyscallArgument { @@ -109,19 +110,14 @@ impl SyscallArgument { } } -pub fn syscall_handler(context: &mut CpuContext) -> HandlerResult { +pub fn syscall_handler(context: &mut CpuContext) { let syscall_frame = SyscallArgument::new_from_context(context); let syscall_return = syscall_dispatch(syscall_frame.syscall_number, syscall_frame.args, context); - match syscall_return { - SyscallResult::Return(return_value) => { - // FIXME: set return value? - context.gp_regs.rax = return_value as u64; - HandlerResult::Continue - } - SyscallResult::Exit(exit_code) => HandlerResult::Exit, - SyscallResult::ReturnNothing => HandlerResult::Continue, + if let SyscallResult::Return(return_value) = syscall_return { + // FIXME: set return value? + context.gp_regs.rax = return_value as u64; } } @@ -153,6 +149,7 @@ pub fn syscall_dispatch( SYS_EXECVE => sys_execve(args[0] as _, args[1] as _, args[2] as _, context), SYS_EXIT => sys_exit(args[0] as _), SYS_WAIT4 => sys_wait4(args[0], args[1], args[2]), + SYS_KILL => sys_kill(args[0], args[1]), SYS_UNAME => sys_uname(args[0]), SYS_READLINK => sys_readlink(args[0], args[1], args[2]), SYS_GETUID => sys_getuid(), diff --git a/src/kxos-std/src/syscall/wait4.rs b/src/kxos-std/src/syscall/wait4.rs index 1610ee4b1..71f9d7b87 100644 --- a/src/kxos-std/src/syscall/wait4.rs +++ b/src/kxos-std/src/syscall/wait4.rs @@ -14,7 +14,7 @@ pub fn sys_wait4(wait_pid: u64, exit_status_ptr: u64, wait_options: u64) -> Sysc 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 process_filter = ProcessFilter::from_id(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);