mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-20 21:36:31 +00:00
Support parent death signal & Refactor do_exit
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
e8c8c027c5
commit
57fc6a5402
@ -181,8 +181,6 @@ fn clone_child_thread(parent_context: &UserContext, clone_args: CloneArgs) -> Re
|
||||
|
||||
let child_tid = allocate_tid();
|
||||
let child_thread = {
|
||||
let is_main_thread = child_tid == current.pid();
|
||||
|
||||
let credentials = {
|
||||
let credentials = credentials();
|
||||
Credentials::new_from(&credentials)
|
||||
@ -190,8 +188,7 @@ fn clone_child_thread(parent_context: &UserContext, clone_args: CloneArgs) -> Re
|
||||
|
||||
let thread_builder = PosixThreadBuilder::new(child_tid, child_user_space, credentials)
|
||||
.process(Arc::downgrade(¤t))
|
||||
.sig_mask(sig_mask)
|
||||
.is_main_thread(is_main_thread);
|
||||
.sig_mask(sig_mask);
|
||||
thread_builder.build()
|
||||
};
|
||||
|
||||
|
@ -4,7 +4,7 @@ use super::{process_table, Pid, Process, TermStatus};
|
||||
use crate::{
|
||||
prelude::*,
|
||||
process::{
|
||||
posix_thread::PosixThreadExt,
|
||||
posix_thread::do_exit,
|
||||
signal::{constants::SIGCHLD, signals::kernel::KernelSignal},
|
||||
},
|
||||
};
|
||||
@ -20,17 +20,22 @@ pub fn do_exit_group(term_status: TermStatus) {
|
||||
// Exit all threads
|
||||
let threads = current.threads().lock().clone();
|
||||
for thread in threads {
|
||||
if thread.status().is_exited() {
|
||||
continue;
|
||||
if let Err(e) = do_exit(thread, term_status) {
|
||||
debug!("Ignore error when call exit: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
thread.exit();
|
||||
if let Some(posix_thread) = thread.as_posix_thread() {
|
||||
let tid = thread.tid();
|
||||
if let Err(e) = posix_thread.exit(tid, term_status) {
|
||||
debug!("Ignore error when call exit: {:?}", e);
|
||||
}
|
||||
}
|
||||
// Sends parent-death signal
|
||||
// FIXME: according to linux spec, the signal should be sent when a posix thread which
|
||||
// creates child process exits, not when the whole process exits group.
|
||||
for (_, child) in current.children().lock().iter() {
|
||||
let Some(signum) = child.parent_death_signal() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// FIXME: set pid of the signal
|
||||
let signal = KernelSignal::new(signum);
|
||||
child.enqueue_signal(signal);
|
||||
}
|
||||
|
||||
// Close all files then exit the process
|
||||
@ -61,7 +66,7 @@ pub fn do_exit_group(term_status: TermStatus) {
|
||||
|
||||
const INIT_PROCESS_PID: Pid = 1;
|
||||
|
||||
/// Get the init process
|
||||
/// Gets the init process
|
||||
fn get_init_process() -> Option<Arc<Process>> {
|
||||
process_table::get_process(INIT_PROCESS_PID)
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ pub struct PosixThreadBuilder {
|
||||
clear_child_tid: Vaddr,
|
||||
sig_mask: SigMask,
|
||||
sig_queues: SigQueues,
|
||||
is_main_thread: bool,
|
||||
}
|
||||
|
||||
impl PosixThreadBuilder {
|
||||
@ -45,7 +44,6 @@ impl PosixThreadBuilder {
|
||||
clear_child_tid: 0,
|
||||
sig_mask: SigMask::new_empty(),
|
||||
sig_queues: SigQueues::new(),
|
||||
is_main_thread: true,
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,12 +67,6 @@ impl PosixThreadBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub fn is_main_thread(mut self, is_main_thread: bool) -> Self {
|
||||
self.is_main_thread = is_main_thread;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn sig_mask(mut self, sig_mask: SigMask) -> Self {
|
||||
self.sig_mask = sig_mask;
|
||||
self
|
||||
@ -91,7 +83,6 @@ impl PosixThreadBuilder {
|
||||
clear_child_tid,
|
||||
sig_mask,
|
||||
sig_queues,
|
||||
is_main_thread,
|
||||
} = self;
|
||||
|
||||
let thread = Arc::new_cyclic(|thread_ref| {
|
||||
@ -104,7 +95,6 @@ impl PosixThreadBuilder {
|
||||
|
||||
let posix_thread = PosixThread {
|
||||
process,
|
||||
is_main_thread,
|
||||
name: Mutex::new(thread_name),
|
||||
set_child_tid: Mutex::new(set_child_tid),
|
||||
clear_child_tid: Mutex::new(clear_child_tid),
|
||||
|
65
kernel/aster-nix/src/process/posix_thread/exit.rs
Normal file
65
kernel/aster-nix/src/process/posix_thread/exit.rs
Normal file
@ -0,0 +1,65 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::{futex::futex_wake, robust_list::wake_robust_futex, PosixThread, PosixThreadExt};
|
||||
use crate::{
|
||||
prelude::*,
|
||||
process::{do_exit_group, TermStatus},
|
||||
thread::{thread_table, Thread, Tid},
|
||||
util::write_val_to_user,
|
||||
};
|
||||
|
||||
/// Exits the thread if the thread is a POSIX thread.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the thread is not a POSIX thread, this method will panic.
|
||||
pub fn do_exit(thread: Arc<Thread>, term_status: TermStatus) -> Result<()> {
|
||||
if thread.status().is_exited() {
|
||||
return Ok(());
|
||||
}
|
||||
thread.exit();
|
||||
|
||||
let tid = thread.tid();
|
||||
|
||||
let posix_thread = thread.as_posix_thread().unwrap();
|
||||
|
||||
let mut clear_ctid = posix_thread.clear_child_tid().lock();
|
||||
// If clear_ctid !=0 ,do a futex wake and write zero to the clear_ctid addr.
|
||||
if *clear_ctid != 0 {
|
||||
futex_wake(*clear_ctid, 1)?;
|
||||
// FIXME: the correct write length?
|
||||
write_val_to_user(*clear_ctid, &0u32).unwrap();
|
||||
*clear_ctid = 0;
|
||||
}
|
||||
// exit the robust list: walk the robust list; mark futex words as dead and do futex wake
|
||||
wake_robust_list(posix_thread, tid);
|
||||
|
||||
if tid != posix_thread.process().pid() {
|
||||
// We don't remove main thread.
|
||||
// The main thread is removed when the process is reaped.
|
||||
thread_table::remove_thread(tid);
|
||||
}
|
||||
|
||||
if posix_thread.is_main_thread(tid) || posix_thread.is_last_thread() {
|
||||
// exit current process.
|
||||
do_exit_group(term_status);
|
||||
}
|
||||
|
||||
futex_wake(Arc::as_ptr(&posix_thread.process()) as Vaddr, 1)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Walks the robust futex list, marking futex dead and wake waiters.
|
||||
/// It corresponds to Linux's exit_robust_list(), errors are silently ignored.
|
||||
fn wake_robust_list(thread: &PosixThread, tid: Tid) {
|
||||
let mut robust_list = thread.robust_list.lock();
|
||||
let list_head = match *robust_list {
|
||||
Some(robust_list_head) => robust_list_head,
|
||||
None => return,
|
||||
};
|
||||
trace!("wake the rubust_list: {:?}", list_head);
|
||||
for futex_addr in list_head.futexes() {
|
||||
wake_robust_futex(futex_addr, tid).unwrap();
|
||||
}
|
||||
*robust_list = None;
|
||||
}
|
@ -3,11 +3,8 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use aster_rights::{ReadOp, WriteOp};
|
||||
use futex::futex_wake;
|
||||
use robust_list::wake_robust_futex;
|
||||
|
||||
use super::{
|
||||
do_exit_group,
|
||||
kill::SignalSenderIds,
|
||||
signal::{
|
||||
sig_mask::{SigMask, SigSet},
|
||||
@ -16,24 +13,25 @@ use super::{
|
||||
signals::Signal,
|
||||
SigEvents, SigEventsFilter, SigStack,
|
||||
},
|
||||
Credentials, Process, TermStatus,
|
||||
Credentials, Process,
|
||||
};
|
||||
use crate::{
|
||||
events::Observer,
|
||||
prelude::*,
|
||||
process::signal::constants::SIGCONT,
|
||||
thread::{thread_table, Tid},
|
||||
thread::Tid,
|
||||
time::{clocks::ProfClock, Timer, TimerManager},
|
||||
util::write_val_to_user,
|
||||
};
|
||||
|
||||
mod builder;
|
||||
mod exit;
|
||||
pub mod futex;
|
||||
mod name;
|
||||
mod posix_thread_ext;
|
||||
mod robust_list;
|
||||
|
||||
pub use builder::PosixThreadBuilder;
|
||||
pub use exit::do_exit;
|
||||
pub use name::{ThreadName, MAX_THREAD_NAME_LEN};
|
||||
pub use posix_thread_ext::PosixThreadExt;
|
||||
pub use robust_list::RobustListHead;
|
||||
@ -41,7 +39,6 @@ pub use robust_list::RobustListHead;
|
||||
pub struct PosixThread {
|
||||
// Immutable part
|
||||
process: Weak<Process>,
|
||||
is_main_thread: bool,
|
||||
|
||||
// Mutable part
|
||||
name: Mutex<Option<ThreadName>>,
|
||||
@ -224,13 +221,13 @@ impl PosixThread {
|
||||
&self.robust_list
|
||||
}
|
||||
|
||||
/// Whether the thread is main thread. For Posix thread, If a thread's tid is equal to pid, it's main thread.
|
||||
pub fn is_main_thread(&self) -> bool {
|
||||
self.is_main_thread
|
||||
fn is_main_thread(&self, tid: Tid) -> bool {
|
||||
let process = self.process();
|
||||
let pid = process.pid();
|
||||
tid == pid
|
||||
}
|
||||
|
||||
/// whether the thread is the last running thread in process
|
||||
pub fn is_last_thread(&self) -> bool {
|
||||
fn is_last_thread(&self) -> bool {
|
||||
let process = self.process.upgrade().unwrap();
|
||||
let threads = process.threads().lock();
|
||||
threads
|
||||
@ -240,62 +237,6 @@ impl PosixThread {
|
||||
== 0
|
||||
}
|
||||
|
||||
/// Walks the robust futex list, marking futex dead and wake waiters.
|
||||
/// It corresponds to Linux's exit_robust_list(), errors are silently ignored.
|
||||
pub fn wake_robust_list(&self, tid: Tid) {
|
||||
let mut robust_list = self.robust_list.lock();
|
||||
let list_head = match *robust_list {
|
||||
None => {
|
||||
return;
|
||||
}
|
||||
Some(robust_list_head) => robust_list_head,
|
||||
};
|
||||
debug!("wake the rubust_list: {:?}", list_head);
|
||||
for futex_addr in list_head.futexes() {
|
||||
// debug!("futex addr = 0x{:x}", futex_addr);
|
||||
wake_robust_futex(futex_addr, tid).unwrap();
|
||||
}
|
||||
debug!("wake robust futex success");
|
||||
*robust_list = None;
|
||||
}
|
||||
|
||||
/// Posix thread does not contains tid info. So we require tid as a parameter.
|
||||
pub fn exit(&self, tid: Tid, term_status: TermStatus) -> Result<()> {
|
||||
let mut clear_ctid = self.clear_child_tid().lock();
|
||||
// If clear_ctid !=0 ,do a futex wake and write zero to the clear_ctid addr.
|
||||
debug!("wake up ctid");
|
||||
if *clear_ctid != 0 {
|
||||
debug!("futex wake");
|
||||
futex_wake(*clear_ctid, 1)?;
|
||||
debug!("write ctid");
|
||||
// FIXME: the correct write length?
|
||||
debug!("ctid = 0x{:x}", *clear_ctid);
|
||||
write_val_to_user(*clear_ctid, &0u32).unwrap();
|
||||
debug!("clear ctid");
|
||||
*clear_ctid = 0;
|
||||
}
|
||||
debug!("wake up ctid succeeds");
|
||||
// exit the robust list: walk the robust list; mark futex words as dead and do futex wake
|
||||
self.wake_robust_list(tid);
|
||||
|
||||
if tid != self.process().pid() {
|
||||
// If the thread is not main thread. We don't remove main thread.
|
||||
// Main thread are removed when the whole process is reaped.
|
||||
thread_table::remove_thread(tid);
|
||||
}
|
||||
|
||||
if self.is_main_thread() || self.is_last_thread() {
|
||||
// exit current process.
|
||||
debug!("self is main thread or last thread");
|
||||
debug!("main thread: {}", self.is_main_thread());
|
||||
debug!("last thread: {}", self.is_last_thread());
|
||||
do_exit_group(term_status);
|
||||
}
|
||||
debug!("perform futex wake");
|
||||
futex_wake(Arc::as_ptr(&self.process()) as Vaddr, 1)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets the read-only credentials of the thread.
|
||||
pub fn credentials(&self) -> Credentials<ReadOp> {
|
||||
self.credentials.dup().restrict()
|
||||
|
@ -181,12 +181,12 @@ impl<'a> ProcessBuilder<'a> {
|
||||
threads,
|
||||
executable_path.to_string(),
|
||||
process_vm,
|
||||
file_table,
|
||||
fs,
|
||||
file_table,
|
||||
umask,
|
||||
sig_dispositions,
|
||||
resource_limits,
|
||||
nice,
|
||||
sig_dispositions,
|
||||
)
|
||||
};
|
||||
|
||||
|
@ -7,7 +7,11 @@ use super::{
|
||||
process_vm::{Heap, InitStackReader, ProcessVm},
|
||||
rlimit::ResourceLimits,
|
||||
signal::{
|
||||
constants::SIGCHLD, sig_disposition::SigDispositions, sig_mask::SigMask, signals::Signal,
|
||||
constants::SIGCHLD,
|
||||
sig_disposition::SigDispositions,
|
||||
sig_mask::SigMask,
|
||||
sig_num::{AtomicSigNum, SigNum},
|
||||
signals::Signal,
|
||||
Pauser,
|
||||
},
|
||||
status::ProcessStatus,
|
||||
@ -89,6 +93,8 @@ pub struct Process {
|
||||
// Signal
|
||||
/// Sig dispositions
|
||||
sig_dispositions: Arc<Mutex<SigDispositions>>,
|
||||
/// The signal that the process should receive when parent process exits.
|
||||
parent_death_signal: AtomicSigNum,
|
||||
|
||||
/// A profiling clock measures the user CPU time and kernel CPU time of the current process.
|
||||
prof_clock: Arc<ProfClock>,
|
||||
@ -105,12 +111,14 @@ impl Process {
|
||||
threads: Vec<Arc<Thread>>,
|
||||
executable_path: String,
|
||||
process_vm: ProcessVm,
|
||||
file_table: Arc<Mutex<FileTable>>,
|
||||
|
||||
fs: Arc<RwMutex<FsResolver>>,
|
||||
file_table: Arc<Mutex<FileTable>>,
|
||||
|
||||
umask: Arc<RwLock<FileCreationMask>>,
|
||||
sig_dispositions: Arc<Mutex<SigDispositions>>,
|
||||
resource_limits: ResourceLimits,
|
||||
nice: Nice,
|
||||
sig_dispositions: Arc<Mutex<SigDispositions>>,
|
||||
) -> Arc<Self> {
|
||||
let children_pauser = {
|
||||
// SIGCHID does not interrupt pauser. Child process will
|
||||
@ -135,6 +143,7 @@ impl Process {
|
||||
fs,
|
||||
umask,
|
||||
sig_dispositions,
|
||||
parent_death_signal: AtomicSigNum::new_empty(),
|
||||
resource_limits: Mutex::new(resource_limits),
|
||||
nice: Atomic::new(nice),
|
||||
timer_manager: PosixTimerManager::new(&prof_clock, process_ref),
|
||||
@ -587,6 +596,24 @@ impl Process {
|
||||
posix_thread.enqueue_signal(Box::new(signal));
|
||||
}
|
||||
|
||||
/// Clears the parent death signal.
|
||||
pub fn clear_parent_death_signal(&self) {
|
||||
self.parent_death_signal.clear();
|
||||
}
|
||||
|
||||
/// Sets the parent death signal as `signum`.
|
||||
pub fn set_parent_death_signal(&self, sig_num: SigNum) {
|
||||
self.parent_death_signal.set(sig_num);
|
||||
}
|
||||
|
||||
/// Returns the parent death signal.
|
||||
///
|
||||
/// The parent death signal is the signal will be sent to child processes
|
||||
/// when the process exits.
|
||||
pub fn parent_death_signal(&self) -> Option<SigNum> {
|
||||
self.parent_death_signal.as_sig_num()
|
||||
}
|
||||
|
||||
// ******************* Status ********************
|
||||
|
||||
fn set_runnable(&self) {
|
||||
@ -642,12 +669,12 @@ mod test {
|
||||
vec![],
|
||||
String::new(),
|
||||
ProcessVm::alloc(),
|
||||
Arc::new(Mutex::new(FileTable::new())),
|
||||
Arc::new(RwMutex::new(FsResolver::new())),
|
||||
Arc::new(Mutex::new(FileTable::new())),
|
||||
Arc::new(RwLock::new(FileCreationMask::default())),
|
||||
Arc::new(Mutex::new(SigDispositions::default())),
|
||||
ResourceLimits::default(),
|
||||
Nice::default(),
|
||||
Arc::new(Mutex::new(SigDispositions::default())),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,11 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use core::sync::atomic::AtomicU8;
|
||||
|
||||
use atomic::Ordering;
|
||||
|
||||
use super::constants::*;
|
||||
use crate::prelude::*;
|
||||
|
||||
@ -77,3 +83,46 @@ impl SigNum {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Atomic signal number.
|
||||
///
|
||||
/// This struct represents a signal number and is different from [SigNum]
|
||||
/// in that it allows for an empty signal number.
|
||||
pub struct AtomicSigNum(AtomicU8);
|
||||
|
||||
impl AtomicSigNum {
|
||||
/// Creates a new empty atomic signal number
|
||||
pub const fn new_empty() -> Self {
|
||||
Self(AtomicU8::new(0))
|
||||
}
|
||||
|
||||
/// Creates a new signal number with the specified value
|
||||
pub const fn new(sig_num: SigNum) -> Self {
|
||||
Self(AtomicU8::new(sig_num.as_u8()))
|
||||
}
|
||||
|
||||
/// Determines whether the signal number is empty
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.load(Ordering::Relaxed) == 0
|
||||
}
|
||||
|
||||
/// Returns the corresponding [`SigNum`]
|
||||
pub fn as_sig_num(&self) -> Option<SigNum> {
|
||||
let sig_num = self.0.load(Ordering::Relaxed);
|
||||
if sig_num == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(SigNum::from_u8(sig_num))
|
||||
}
|
||||
|
||||
/// Sets the new `sig_num`
|
||||
pub fn set(&self, sig_num: SigNum) {
|
||||
self.0.store(sig_num.as_u8(), Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Clears the signal number
|
||||
pub fn clear(&self) {
|
||||
self.0.store(0, Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ use crate::{
|
||||
process::{
|
||||
check_executable_file, credentials_mut, load_program_to_vm,
|
||||
posix_thread::{PosixThreadExt, ThreadName},
|
||||
Credentials, MAX_ARGV_NUMBER, MAX_ARG_LEN, MAX_ENVP_NUMBER, MAX_ENV_LEN,
|
||||
Credentials, Process, MAX_ARGV_NUMBER, MAX_ARG_LEN, MAX_ENVP_NUMBER, MAX_ENV_LEN,
|
||||
},
|
||||
util::{read_cstring_from_user, read_val_from_user},
|
||||
};
|
||||
@ -118,8 +118,8 @@ fn do_execve(
|
||||
debug!("load elf in execve succeeds");
|
||||
|
||||
let credentials = credentials_mut();
|
||||
set_uid_from_elf(&credentials, &elf_file)?;
|
||||
set_gid_from_elf(&credentials, &elf_file)?;
|
||||
set_uid_from_elf(¤t, &credentials, &elf_file)?;
|
||||
set_gid_from_elf(¤t, &credentials, &elf_file)?;
|
||||
|
||||
// set executable path
|
||||
current.set_executable_path(new_executable_path);
|
||||
@ -177,10 +177,16 @@ fn read_cstring_vec(
|
||||
}
|
||||
|
||||
/// Sets uid for credentials as the same of uid of elf file if elf file has `set_uid` bit.
|
||||
fn set_uid_from_elf(credentials: &Credentials<WriteOp>, elf_file: &Arc<Dentry>) -> Result<()> {
|
||||
fn set_uid_from_elf(
|
||||
current: &Arc<Process>,
|
||||
credentials: &Credentials<WriteOp>,
|
||||
elf_file: &Arc<Dentry>,
|
||||
) -> Result<()> {
|
||||
if elf_file.mode()?.has_set_uid() {
|
||||
let uid = elf_file.owner()?;
|
||||
credentials.set_euid(uid);
|
||||
|
||||
current.clear_parent_death_signal();
|
||||
}
|
||||
|
||||
// No matter whether the elf_file has `set_uid` bit, suid should be reset.
|
||||
@ -189,10 +195,16 @@ fn set_uid_from_elf(credentials: &Credentials<WriteOp>, elf_file: &Arc<Dentry>)
|
||||
}
|
||||
|
||||
/// Sets gid for credentials as the same of gid of elf file if elf file has `set_gid` bit.
|
||||
fn set_gid_from_elf(credentials: &Credentials<WriteOp>, elf_file: &Arc<Dentry>) -> Result<()> {
|
||||
fn set_gid_from_elf(
|
||||
current: &Arc<Process>,
|
||||
credentials: &Credentials<WriteOp>,
|
||||
elf_file: &Arc<Dentry>,
|
||||
) -> Result<()> {
|
||||
if elf_file.mode()?.has_set_gid() {
|
||||
let gid = elf_file.group()?;
|
||||
credentials.set_egid(gid);
|
||||
|
||||
current.clear_parent_death_signal();
|
||||
}
|
||||
|
||||
// No matter whether the the elf file has `set_gid` bit, sgid should be reset.
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use crate::{
|
||||
prelude::*,
|
||||
process::{posix_thread::PosixThreadExt, TermStatus},
|
||||
process::{posix_thread::do_exit, TermStatus},
|
||||
syscall::SyscallReturn,
|
||||
};
|
||||
|
||||
@ -10,14 +10,8 @@ pub fn sys_exit(exit_code: i32) -> Result<SyscallReturn> {
|
||||
debug!("exid code = {}", exit_code);
|
||||
|
||||
let current_thread = current_thread!();
|
||||
current_thread.exit();
|
||||
|
||||
let tid = current_thread.tid();
|
||||
let pid = current!().pid();
|
||||
debug!("tid = {}, pid = {}", tid, pid);
|
||||
|
||||
let posix_thread = current_thread.as_posix_thread().unwrap();
|
||||
posix_thread.exit(tid, TermStatus::Exited(exit_code as _))?;
|
||||
let term_status = TermStatus::Exited(exit_code as _);
|
||||
do_exit(current_thread, term_status)?;
|
||||
|
||||
Ok(SyscallReturn::Return(0))
|
||||
}
|
||||
|
@ -6,15 +6,35 @@
|
||||
use super::SyscallReturn;
|
||||
use crate::{
|
||||
prelude::*,
|
||||
process::posix_thread::{PosixThreadExt, MAX_THREAD_NAME_LEN},
|
||||
util::{read_cstring_from_user, write_bytes_to_user},
|
||||
process::{
|
||||
posix_thread::{PosixThreadExt, MAX_THREAD_NAME_LEN},
|
||||
signal::sig_num::SigNum,
|
||||
},
|
||||
util::{read_cstring_from_user, write_bytes_to_user, write_val_to_user},
|
||||
};
|
||||
|
||||
pub fn sys_prctl(option: i32, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> Result<SyscallReturn> {
|
||||
let prctl_cmd = PrctlCmd::from_args(option, arg2, arg3, arg4, arg5)?;
|
||||
debug!("prctl cmd = {:x?}", prctl_cmd);
|
||||
let current_thread = current_thread!();
|
||||
let posix_thread = current_thread.as_posix_thread().unwrap();
|
||||
match prctl_cmd {
|
||||
PrctlCmd::PR_SET_PDEATHSIG(signum) => {
|
||||
let current = current!();
|
||||
current.set_parent_death_signal(signum);
|
||||
}
|
||||
PrctlCmd::PR_GET_PDEATHSIG(write_to_addr) => {
|
||||
let write_val = {
|
||||
let current = current!();
|
||||
|
||||
match current.parent_death_signal() {
|
||||
None => 0i32,
|
||||
Some(signum) => signum.as_u8() as i32,
|
||||
}
|
||||
};
|
||||
|
||||
write_val_to_user(write_to_addr, &write_val)?;
|
||||
}
|
||||
PrctlCmd::PR_GET_NAME(write_to_addr) => {
|
||||
let thread_name = posix_thread.thread_name().lock();
|
||||
if let Some(thread_name) = &*thread_name {
|
||||
@ -35,6 +55,8 @@ pub fn sys_prctl(option: i32, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> Res
|
||||
Ok(SyscallReturn::Return(0))
|
||||
}
|
||||
|
||||
const PR_SET_PDEATHSIG: i32 = 1;
|
||||
const PR_GET_PDEATHSIG: i32 = 2;
|
||||
const PR_SET_NAME: i32 = 15;
|
||||
const PR_GET_NAME: i32 = 16;
|
||||
const PR_SET_TIMERSLACK: i32 = 29;
|
||||
@ -43,6 +65,8 @@ const PR_GET_TIMERSLACK: i32 = 30;
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum PrctlCmd {
|
||||
PR_SET_PDEATHSIG(SigNum),
|
||||
PR_GET_PDEATHSIG(Vaddr),
|
||||
PR_SET_NAME(Vaddr),
|
||||
PR_GET_NAME(Vaddr),
|
||||
PR_SET_TIMERSLACK(u64),
|
||||
@ -52,6 +76,11 @@ pub enum PrctlCmd {
|
||||
impl PrctlCmd {
|
||||
fn from_args(option: i32, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> Result<PrctlCmd> {
|
||||
match option {
|
||||
PR_SET_PDEATHSIG => {
|
||||
let signum = SigNum::try_from(arg2 as u8)?;
|
||||
Ok(PrctlCmd::PR_SET_PDEATHSIG(signum))
|
||||
}
|
||||
PR_GET_PDEATHSIG => Ok(PrctlCmd::PR_GET_PDEATHSIG(arg2 as _)),
|
||||
PR_SET_NAME => Ok(PrctlCmd::PR_SET_NAME(arg2 as _)),
|
||||
PR_GET_NAME => Ok(PrctlCmd::PR_GET_NAME(arg2 as _)),
|
||||
PR_GET_TIMERSLACK => todo!(),
|
||||
|
@ -65,13 +65,13 @@ impl Thread {
|
||||
&self.task
|
||||
}
|
||||
|
||||
/// Run this thread at once.
|
||||
/// Runs this thread at once.
|
||||
pub fn run(&self) {
|
||||
self.set_status(ThreadStatus::Running);
|
||||
self.task.run();
|
||||
}
|
||||
|
||||
pub fn exit(&self) {
|
||||
pub(super) fn exit(&self) {
|
||||
self.set_status(ThreadStatus::Exited);
|
||||
}
|
||||
|
||||
@ -98,14 +98,16 @@ impl Thread {
|
||||
self.tid
|
||||
}
|
||||
|
||||
// The return type must be borrowed box, otherwise the downcast_ref will fail
|
||||
/// Returns the associated data.
|
||||
///
|
||||
/// The return type must be borrowed box, otherwise the `downcast_ref` will fail.
|
||||
#[allow(clippy::borrowed_box)]
|
||||
pub fn data(&self) -> &Box<dyn Send + Sync + Any> {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
/// allocate a new pid for new process
|
||||
/// Allocates a new tid for the new thread
|
||||
pub fn allocate_tid() -> Tid {
|
||||
TID_ALLOCATOR.fetch_add(1, Ordering::SeqCst)
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ itimer/timer_create
|
||||
mmap/map_shared_anon
|
||||
pthread/pthread_test
|
||||
pty/open_pty
|
||||
signal_c/parent_death_signal
|
||||
signal_c/signal_test
|
||||
"
|
||||
|
||||
|
49
regression/apps/signal_c/parent_death_signal.c
Normal file
49
regression/apps/signal_c/parent_death_signal.c
Normal file
@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <sys/prctl.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void signal_handler(int signum)
|
||||
{
|
||||
if (signum == SIGTERM) {
|
||||
printf("child process reveives SIGTERM\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == -1) {
|
||||
perror("fork");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (pid > 0) {
|
||||
printf("Parent PID: %d\n", getpid());
|
||||
// Ensure parent won't exit before child process runs
|
||||
sleep(1);
|
||||
} else {
|
||||
printf("CHild PID: %d\n", getpid());
|
||||
|
||||
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = signal_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGTERM, &sa, NULL);
|
||||
|
||||
// Child waits for signal from parent death
|
||||
while (1) {
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Reference in New Issue
Block a user