diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index dc764d443..0b781332e 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -168,7 +168,7 @@ fn init_thread() { } // TODO: exit via qemu isa debug device should not be the only way. - let exit_code = if initproc.exit_code().unwrap() == 0 { + let exit_code = if initproc.exit_code() == 0 { QemuExitCode::Success } else { QemuExitCode::Failed diff --git a/kernel/src/process/kill.rs b/kernel/src/process/kill.rs index c510bb9f6..3ed3f5e4b 100644 --- a/kernel/src/process/kill.rs +++ b/kernel/src/process/kill.rs @@ -3,7 +3,11 @@ use super::{ posix_thread::PosixThreadExt, process_table, - signal::signals::{user::UserSignal, Signal}, + signal::{ + constants::SIGCONT, + sig_num::SigNum, + signals::{user::UserSignal, Signal}, + }, Pgid, Pid, Process, Sid, Uid, }; use crate::{ @@ -18,11 +22,27 @@ use crate::{ /// /// If `signal` is `None`, this method will only check permission without sending /// any signal. -pub fn kill(pid: Pid, signal: Option) -> Result<()> { +pub fn kill(pid: Pid, signal: Option, ctx: &Context) -> Result<()> { + // Fast path: If the signal is sent to self, we can skip most check. + if pid == ctx.process.pid() { + let Some(signal) = signal else { + return Ok(()); + }; + + if !ctx.posix_thread.has_signal_blocked(signal.num()) { + ctx.posix_thread.enqueue_signal(Box::new(signal)); + return Ok(()); + } + + return kill_process(ctx.process, Some(signal), ctx); + } + + // Slow path + let process = process_table::get_process(pid) .ok_or_else(|| Error::with_message(Errno::ESRCH, "the target process does not exist"))?; - kill_process(&process, signal) + kill_process(&process, signal, ctx) } /// Sends a signal to all processes in a group, using the current process @@ -33,13 +53,13 @@ pub fn kill(pid: Pid, signal: Option) -> Result<()> { /// /// If `signal` is `None`, this method will only check permission without sending /// any signal. -pub fn kill_group(pgid: Pgid, signal: Option) -> Result<()> { +pub fn kill_group(pgid: Pgid, signal: Option, ctx: &Context) -> Result<()> { let process_group = process_table::get_process_group(&pgid) .ok_or_else(|| Error::with_message(Errno::ESRCH, "target group does not exist"))?; let inner = process_group.inner.lock(); for process in inner.processes.values() { - kill_process(process, signal)?; + kill_process(process, signal, ctx)?; } Ok(()) @@ -50,7 +70,7 @@ pub fn kill_group(pgid: Pgid, signal: Option) -> Result<()> { /// /// If `signal` is `None`, this method will only check permission without sending /// any signal. -pub fn tgkill(tid: Tid, tgid: Pid, signal: Option) -> Result<()> { +pub fn tgkill(tid: Tid, tgid: Pid, signal: Option, ctx: &Context) -> Result<()> { let thread = thread_table::get_thread(tid) .ok_or_else(|| Error::with_message(Errno::ESRCH, "target thread does not exist"))?; @@ -71,7 +91,7 @@ pub fn tgkill(tid: Tid, tgid: Pid, signal: Option) -> Result<()> { // Check permission let signum = signal.map(|signal| signal.num()); - let sender = current_thread_sender_ids(); + let sender = current_thread_sender_ids(signum.as_ref(), ctx); posix_thread.check_signal_perm(signum.as_ref(), &sender)?; if let Some(signal) = signal { @@ -86,66 +106,74 @@ pub fn tgkill(tid: Tid, tgid: Pid, signal: Option) -> Result<()> { /// /// The credentials of the current process will be checked to determine /// if it is authorized to send the signal to the target group. -pub fn kill_all(signal: Option) -> Result<()> { +pub fn kill_all(signal: Option, ctx: &Context) -> Result<()> { let current = current!(); for process in process_table::process_table().iter() { if Arc::ptr_eq(¤t, process) || process.is_init_process() { continue; } - kill_process(process, signal)?; + kill_process(process, signal, ctx)?; } Ok(()) } -fn kill_process(process: &Process, signal: Option) -> Result<()> { +fn kill_process(process: &Process, signal: Option, ctx: &Context) -> Result<()> { let threads = process.threads().lock(); - let posix_threads = threads - .iter() - .map(|thread| thread.as_posix_thread().unwrap()); - // First check permission let signum = signal.map(|signal| signal.num()); - let sender_ids = current_thread_sender_ids(); - let mut permitted_threads = { - posix_threads.clone().filter(|posix_thread| { - posix_thread - .check_signal_perm(signum.as_ref(), &sender_ids) - .is_ok() - }) - }; + let sender_ids = current_thread_sender_ids(signum.as_ref(), ctx); - if permitted_threads.clone().count() == 0 { - return_errno_with_message!(Errno::EPERM, "cannot send signal to the target process"); - } + let mut permitted_thread = None; + for thread in threads.iter() { + let posix_thread = thread.as_posix_thread().unwrap(); - let Some(signal) = signal else { return Ok(()) }; + // First check permission + if posix_thread + .check_signal_perm(signum.as_ref(), &sender_ids) + .is_ok() + { + let Some(ref signum) = signum else { + // If signal is None, only permission check is required + return Ok(()); + }; - // Send signal to any thread that does not blocks the signal. - for thread in permitted_threads.clone() { - if !thread.has_signal_blocked(&signal) { - thread.enqueue_signal(Box::new(signal)); - return Ok(()); + if !posix_thread.has_signal_blocked(*signum) { + // Send signal to any thread that does not blocks the signal. + let signal = signal.unwrap(); + posix_thread.enqueue_signal(Box::new(signal)); + return Ok(()); + } else if permitted_thread.is_none() { + permitted_thread = Some(posix_thread); + } } } + let Some(permitted_thread) = permitted_thread else { + return_errno_with_message!(Errno::EPERM, "cannot send signal to the target process"); + }; + + // If signal is None, only permission check is required + let Some(signal) = signal else { return Ok(()) }; + // If all threads block the signal, send signal to the first thread. - let first_thread = permitted_threads.next().unwrap(); - first_thread.enqueue_signal(Box::new(signal)); + permitted_thread.enqueue_signal(Box::new(signal)); Ok(()) } -fn current_thread_sender_ids() -> SignalSenderIds { - let current_thread = current_thread!(); - let current_posix_thread = current_thread.as_posix_thread().unwrap(); - let current_process = current_posix_thread.process(); - - let credentials = current_posix_thread.credentials(); +fn current_thread_sender_ids(signum: Option<&SigNum>, ctx: &Context) -> SignalSenderIds { + let credentials = ctx.posix_thread.credentials(); let ruid = credentials.ruid(); let euid = credentials.euid(); - let sid = current_process.session().unwrap().sid(); + let sid = signum.and_then(|signum| { + if *signum == SIGCONT { + Some(ctx.process.session().unwrap().sid()) + } else { + None + } + }); SignalSenderIds::new(ruid, euid, sid) } @@ -156,11 +184,11 @@ fn current_thread_sender_ids() -> SignalSenderIds { pub(super) struct SignalSenderIds { ruid: Uid, euid: Uid, - sid: Sid, + sid: Option, } impl SignalSenderIds { - fn new(ruid: Uid, euid: Uid, sid: Sid) -> Self { + fn new(ruid: Uid, euid: Uid, sid: Option) -> Self { Self { ruid, euid, sid } } @@ -172,7 +200,7 @@ impl SignalSenderIds { self.euid } - pub(super) fn sid(&self) -> Sid { + pub(super) fn sid(&self) -> Option { self.sid } } diff --git a/kernel/src/process/posix_thread/mod.rs b/kernel/src/process/posix_thread/mod.rs index 8c93343a3..17a7d2f84 100644 --- a/kernel/src/process/posix_thread/mod.rs +++ b/kernel/src/process/posix_thread/mod.rs @@ -117,8 +117,9 @@ impl PosixThread { } /// Returns whether the signal is blocked by the thread. - pub(in crate::process) fn has_signal_blocked(&self, signal: &dyn Signal) -> bool { - self.sig_mask.contains(signal.num(), Ordering::Relaxed) + pub(in crate::process) fn has_signal_blocked(&self, signum: SigNum) -> bool { + // FIMXE: Some signals cannot be blocked, even set in sig_mask. + self.sig_mask.contains(signum, Ordering::Relaxed) } /// Checks whether the signal can be delivered to the thread. @@ -141,7 +142,7 @@ impl PosixThread { && *signum == SIGCONT { let receiver_sid = self.process().session().unwrap().sid(); - if receiver_sid == sender.sid() { + if receiver_sid == sender.sid().unwrap() { return Ok(()); } diff --git a/kernel/src/process/process/mod.rs b/kernel/src/process/process/mod.rs index 0b3a0ff46..a82912dfa 100644 --- a/kernel/src/process/process/mod.rs +++ b/kernel/src/process/process/mod.rs @@ -69,7 +69,7 @@ pub struct Process { /// The threads threads: Mutex>>, /// Process status - status: Mutex, + status: ProcessStatus, /// Parent process pub(super) parent: Mutex>, /// Children processes @@ -140,7 +140,7 @@ impl Process { executable_path: RwLock::new(executable_path), process_vm, children_pauser, - status: Mutex::new(ProcessStatus::Uninit), + status: ProcessStatus::new_uninit(), parent: Mutex::new(parent), children: Mutex::new(BTreeMap::new()), process_group: Mutex::new(Weak::new()), @@ -589,7 +589,7 @@ impl Process { let threads = self.threads.lock(); for thread in threads.iter() { let posix_thread = thread.as_posix_thread().unwrap(); - if !posix_thread.has_signal_blocked(&signal) { + if !posix_thread.has_signal_blocked(signal.num()) { posix_thread.enqueue_signal(Box::new(signal)); return; } @@ -622,26 +622,23 @@ impl Process { // ******************* Status ******************** fn set_runnable(&self) { - self.status.lock().set_runnable(); + self.status.set_runnable(); } fn is_runnable(&self) -> bool { - self.status.lock().is_runnable() + self.status.is_runnable() } pub fn is_zombie(&self) -> bool { - self.status.lock().is_zombie() + self.status.is_zombie() } pub fn set_zombie(&self, term_status: TermStatus) { - *self.status.lock() = ProcessStatus::Zombie(term_status); + self.status.set_zombie(term_status); } - pub fn exit_code(&self) -> Option { - match &*self.status.lock() { - ProcessStatus::Runnable | ProcessStatus::Uninit => None, - ProcessStatus::Zombie(term_status) => Some(term_status.as_u32()), - } + pub fn exit_code(&self) -> ExitCode { + self.status.exit_code() } } diff --git a/kernel/src/process/status.rs b/kernel/src/process/status.rs index 5ae6ddd87..2e00d1356 100644 --- a/kernel/src/process/status.rs +++ b/kernel/src/process/status.rs @@ -4,32 +4,56 @@ //! The process status -use super::TermStatus; +use core::sync::atomic::{AtomicU64, Ordering}; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ProcessStatus { - // Not ready to run - Uninit, - /// Can be scheduled to run - Runnable, - /// Exit while not reaped by parent - Zombie(TermStatus), +use super::{ExitCode, TermStatus}; + +/// The status of process. +/// +/// The `ProcessStatus` can be viewed as two parts, +/// the highest 32 bits is the value of `TermStatus`, if any, +/// the lowest 32 bits is the value of status. +#[derive(Debug)] +pub struct ProcessStatus(AtomicU64); + +#[repr(u8)] +enum Status { + Uninit = 0, + Runnable = 1, + Zombie = 2, } impl ProcessStatus { - pub fn set_zombie(&mut self, term_status: TermStatus) { - *self = ProcessStatus::Zombie(term_status); + const LOW_MASK: u64 = 0xffff_ffff; + + pub fn new_uninit() -> Self { + Self(AtomicU64::new(Status::Uninit as u64)) + } + + pub fn set_zombie(&self, term_status: TermStatus) { + let new_val = (term_status.as_u32() as u64) << 32 | Status::Zombie as u64; + self.0.store(new_val, Ordering::Relaxed); } pub fn is_zombie(&self) -> bool { - matches!(self, ProcessStatus::Zombie(_)) + self.0.load(Ordering::Relaxed) & Self::LOW_MASK == Status::Zombie as u64 } - pub fn set_runnable(&mut self) { - *self = ProcessStatus::Runnable; + pub fn set_runnable(&self) { + let new_val = Status::Runnable as u64; + self.0.store(new_val, Ordering::Relaxed); } pub fn is_runnable(&self) -> bool { - *self == ProcessStatus::Runnable + self.0.load(Ordering::Relaxed) & Self::LOW_MASK == Status::Runnable as u64 + } + + /// Returns the exit code. + /// + /// If the process is not exited, the exit code is zero. + /// But if exit code is zero, the process may or may not exit. + pub fn exit_code(&self) -> ExitCode { + let val = self.0.load(Ordering::Relaxed); + (val >> 32 & Self::LOW_MASK) as ExitCode } } diff --git a/kernel/src/process/wait.rs b/kernel/src/process/wait.rs index 5d550523d..e7b3c0a08 100644 --- a/kernel/src/process/wait.rs +++ b/kernel/src/process/wait.rs @@ -109,5 +109,5 @@ fn reap_zombie_child(process: &Process, pid: Pid) -> ExitCode { } process_table_mut.remove(&child_process.pid()); - child_process.exit_code().unwrap() + child_process.exit_code() } diff --git a/kernel/src/syscall/kill.rs b/kernel/src/syscall/kill.rs index 12082f894..bceedff20 100644 --- a/kernel/src/syscall/kill.rs +++ b/kernel/src/syscall/kill.rs @@ -36,9 +36,9 @@ pub fn do_sys_kill(filter: ProcessFilter, sig_num: Option, ctx: &Context }); match filter { - ProcessFilter::Any => kill_all(signal)?, - ProcessFilter::WithPid(pid) => kill(pid, signal)?, - ProcessFilter::WithPgid(pgid) => kill_group(pgid, signal)?, + ProcessFilter::Any => kill_all(signal, ctx)?, + ProcessFilter::WithPid(pid) => kill(pid, signal, ctx)?, + ProcessFilter::WithPgid(pgid) => kill_group(pgid, signal, ctx)?, } Ok(()) } diff --git a/kernel/src/syscall/tgkill.rs b/kernel/src/syscall/tgkill.rs index 15b7c9f39..71d077b6c 100644 --- a/kernel/src/syscall/tgkill.rs +++ b/kernel/src/syscall/tgkill.rs @@ -28,6 +28,6 @@ pub fn sys_tgkill(tgid: Pid, tid: Tid, sig_num: u8, ctx: &Context) -> Result { if handle_page_fault(root_vmar.vm_space(), trap_info).is_err() { - generate_fault_signal(trap_info); + generate_fault_signal(trap_info, ctx); } } _ => { // We current do nothing about other exceptions - generate_fault_signal(trap_info); + generate_fault_signal(trap_info, ctx); } } } @@ -42,13 +42,14 @@ pub(crate) fn handle_page_fault( trap_info.error_code, page_fault_addr ); + let not_present = trap_info.error_code & PAGE_NOT_PRESENT_ERROR_MASK == 0; let write = trap_info.error_code & WRITE_ACCESS_MASK != 0; if not_present || write { - // If page is not present or due to write access, we should ask the vmar try to commit this page let current = current!(); let root_vmar = current.root_vmar(); + // If page is not present or due to write access, we should ask the vmar try to commit this page debug_assert_eq!( Arc::as_ptr(root_vmar.vm_space()), vm_space as *const VmSpace @@ -69,10 +70,9 @@ pub(crate) fn handle_page_fault( } /// generate a fault signal for current process. -fn generate_fault_signal(trap_info: &CpuExceptionInfo) { - let current = current!(); +fn generate_fault_signal(trap_info: &CpuExceptionInfo, ctx: &Context) { let signal = FaultSignal::new(trap_info); - current.enqueue_signal(signal); + ctx.posix_thread.enqueue_signal(Box::new(signal)); } macro_rules! log_trap_common { diff --git a/test/benchmark/lmbench-signal-prot/run.sh b/test/benchmark/lmbench-signal-prot/run.sh index 319b52d09..7a6540134 100644 --- a/test/benchmark/lmbench-signal-prot/run.sh +++ b/test/benchmark/lmbench-signal-prot/run.sh @@ -7,4 +7,4 @@ set -e echo "*** Running the LMbench protection fault latency test ***" dd if=/dev/zero of=/ext2/test_file bs=1M count=256 -/benchmark/bin/lmbench/lat_sig prot /ext2/test_file \ No newline at end of file +/benchmark/bin/lmbench/lat_sig -N 100 prot /ext2/test_file \ No newline at end of file