add basic support for signal

This commit is contained in:
Jianfeng Jiang 2022-10-31 16:14:41 +08:00
parent 7a5660ed5d
commit f86cd1e6bc
23 changed files with 855 additions and 60 deletions

View File

@ -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;

View File

@ -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)?;

View File

@ -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<Option<Weak<Process>>>,
/// Children processes
children: Mutex<BTreeMap<usize, Arc<Process>>>,
/// Process group
process_group: Mutex<Option<Weak<ProcessGroup>>>,
// Signal
sig_dispositions: Mutex<SigDispositions>,
sig_queues: Mutex<SigQueues>,
/// Process-level sigmask
sig_mask: Mutex<SigMask>,
}
impl Process {
@ -69,6 +83,10 @@ impl Process {
exec_filename: Option<CString>,
user_vm: Option<UserVm>,
user_space: Option<Arc<UserSpace>>,
process_group: Option<Weak<ProcessGroup>>,
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<Option<Weak<ProcessGroup>>> {
&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<ProcessGroup>) {
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<Self>) {
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<Arc<Process>> {
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<Arc<Process>> {
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<ProcessStatus> {
&self.status
}
pub fn sig_dispositions(&self) -> &Mutex<SigDispositions> {
&self.sig_dispositions
}
pub fn sig_queues(&self) -> &Mutex<SigQueues> {
&self.sig_queues
}
pub fn sig_mask(&self) -> &Mutex<SigMask> {
&self.sig_mask
}
}

View File

@ -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)

View File

@ -0,0 +1,53 @@
use super::{Pgid, Pid, Process};
use crate::prelude::*;
pub struct ProcessGroup {
inner: Mutex<ProcessGroupInner>,
}
struct ProcessGroupInner {
pgid: Pgid,
processes: BTreeMap<Pid, Arc<Process>>,
leader_process: Option<Arc<Process>>,
}
impl ProcessGroup {
fn default() -> Self {
ProcessGroup {
inner: Mutex::new(ProcessGroupInner {
pgid: 0,
processes: BTreeMap::new(),
leader_process: None,
}),
}
}
pub fn new(process: Arc<Process>) -> 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<Process>) {
self.inner.lock().leader_process = Some(leader_process);
}
pub fn add_process(&self, process: Arc<Process>) {
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
}
}

View File

@ -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)
}

View File

@ -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);
}
}
}
}
}
}

View File

@ -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<u32> for SigActionFlags {
type Error = &'static str;
fn try_from(bits: u32) -> Result<Self, Self::Error> {
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,
}
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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<Option<Box<dyn Signal>>>,
rt_queues: Vec<VecDeque<Box<dyn Signal>>>,
}
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<dyn Signal>) {
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<Box<dyn Signal>> {
// 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<Box<dyn Signal>> {
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<Box<dyn Signal>> {
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()
}
}

View File

@ -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;
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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<BTreeMap<Pid, Arc<Process>>> = Mutex::new(BTreeMap::new());
static ref PROCESS_GROUP_TABLE: Mutex<BTreeMap<Pgid, Arc<ProcessGroup>>> =
Mutex::new(BTreeMap::new());
}
/// add a process to global table
@ -15,8 +17,8 @@ pub fn add_process(pid: Pid, process: Arc<Process>) {
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<Arc<Process>> {
.map(|(_, process)| process.clone())
.collect()
}
/// add process group to global table
pub fn add_process_group(pgid: Pgid, process_group: Arc<ProcessGroup>) {
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<Arc<ProcessGroup>> {
PROCESS_GROUP_TABLE
.lock()
.get(&pgid)
.map(|process_group| process_group.clone())
}

View File

@ -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<UserSpace>, parent: Weak<Process>) -> 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,
}

View File

@ -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;

View File

@ -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
}

View File

@ -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
}

View File

@ -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<Vec<Arc<Process>>> {
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)
}

View File

@ -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(),

View File

@ -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);