From 6efd4740336205c9bfdd8b164e667cee2f38781e Mon Sep 17 00:00:00 2001 From: login Date: Sat, 17 Dec 2022 16:27:50 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=81=E8=AE=B8=E7=94=A8=E6=88=B7=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E4=BF=A1=E5=8F=B7=E5=A4=84=E7=90=86=E5=87=BD?= =?UTF-8?q?=E6=95=B0=20(#112)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * new: 用户注册信号处理函数,能够进入自定义的handler * 修复忘了传信号的数字给用户的处理函数的bug * new:sigreturn * 删除注释 --- .vscode/settings.json | 1 + kernel/src/include/DragonOS/signal.h | 1 + kernel/src/include/bindings/wrapper.h | 2 +- kernel/src/ipc/signal.rs | 338 +++++++++++++++++++++++--- kernel/src/ipc/signal_types.rs | 119 ++++++++- kernel/src/syscall/syscall.c | 6 +- kernel/src/syscall/syscall_num.h | 8 +- user/apps/test_signal/main.c | 32 ++- user/libs/libc/src/Makefile | 41 +--- user/libs/libc/src/include/signal.h | 51 +++- user/libs/libc/src/signal.c | 44 ++++ user/libs/libsystem/syscall.h | 23 +- 12 files changed, 566 insertions(+), 100 deletions(-) create mode 100644 user/libs/libc/src/signal.c diff --git a/.vscode/settings.json b/.vscode/settings.json index e8c15828..afdfee8b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -164,6 +164,7 @@ "wait.h": "c", "__libc__.h": "c", "ctype.h": "c", + "signal.h": "c", "mmio.h": "c", "stdint-gcc.h": "c", "acpi.h": "c", diff --git a/kernel/src/include/DragonOS/signal.h b/kernel/src/include/DragonOS/signal.h index d3bc77b2..e64c3411 100644 --- a/kernel/src/include/DragonOS/signal.h +++ b/kernel/src/include/DragonOS/signal.h @@ -112,6 +112,7 @@ struct sigaction #define SA_FLAG_IGN (1UL << 0) // 当前sigaction表示忽略信号的动作 #define SA_FLAG_DFL (1UL << 1) // 当前sigaction表示系统默认的动作 #define SA_FLAG_RESTORER (1UL << 2) // 当前sigaction具有用户指定的restorer +#define SA_FLAG_IMMUTABLE (1UL << 3) // 当前sigaction不可被更改 /** * 由于signal_struct总是和sighand_struct一起使用,并且信号处理的过程中必定会对sighand加锁, diff --git a/kernel/src/include/bindings/wrapper.h b/kernel/src/include/bindings/wrapper.h index 7a276872..29c2afc6 100644 --- a/kernel/src/include/bindings/wrapper.h +++ b/kernel/src/include/bindings/wrapper.h @@ -16,13 +16,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include diff --git a/kernel/src/ipc/signal.rs b/kernel/src/ipc/signal.rs index e1acea99..f61f454c 100644 --- a/kernel/src/ipc/signal.rs +++ b/kernel/src/ipc/signal.rs @@ -7,10 +7,11 @@ use crate::{ }, include::bindings::bindings::{ pid_t, process_control_block, process_do_exit, process_find_pcb_by_pid, pt_regs, - spinlock_t, verify_area, EINVAL, ENOTSUP, EPERM, ESRCH, PF_EXITING, PF_KTHREAD, - PF_SIGNALED, PF_WAKEKILL, PROC_INTERRUPTIBLE, USER_CS, USER_DS, USER_MAX_LINEAR_ADDR, + spinlock_t, verify_area, EFAULT, EINVAL, ENOTSUP, EPERM, ESRCH, NULL, PF_EXITING, + PF_KTHREAD, PF_SIGNALED, PF_WAKEKILL, PROC_INTERRUPTIBLE, USER_CS, USER_DS, + USER_MAX_LINEAR_ADDR, }, - ipc::signal_types::sigset_add, + ipc::signal_types::{sigset_add, user_sigaction}, kBUG, kdebug, kerror, kwarn, libs::{ ffi_convert::FFIBind2Rust, @@ -19,7 +20,6 @@ use crate::{ spin_unlock_irqrestore, }, }, - println, process::{ pid::PidType, process::{process_is_stopped, process_kick, process_wake_up_state}, @@ -27,28 +27,34 @@ use crate::{ }; use super::signal_types::{ - si_code_val, sigaction, sigaction__union_u, sigcontext, sigframe, sighand_struct, siginfo, - signal_struct, sigpending, sigset_clear, sigset_del, sigset_t, SignalNumber, MAX_SIG_NUM, - SA_FLAG_DFL, SA_FLAG_IGN, SA_FLAG_RESTORER, STACK_ALIGN, _NSIG_U64_CNT, + si_code_val, sig_is_member, sigaction, sigaction__union_u, sigcontext, sigframe, + sighand_struct, siginfo, signal_struct, sigpending, sigset_clear, sigset_del, sigset_delmask, + sigset_equal, sigset_init, sigset_t, SigQueue, SignalNumber, MAX_SIG_NUM, SA_ALL_FLAGS, + SA_FLAG_DFL, SA_FLAG_IGN, SA_FLAG_IMMUTABLE, SA_FLAG_RESTORER, STACK_ALIGN, USER_SIG_DFL, + USER_SIG_IGN, _NSIG_U64_CNT, }; use super::signal_types::{__siginfo_union, __siginfo_union_data}; /// 默认信号处理程序占位符(用于在sighand结构体中的action数组中占位) pub static DEFAULT_SIGACTION: sigaction = sigaction { - _u: sigaction__union_u { _sa_handler: None }, + _u: sigaction__union_u { + _sa_handler: NULL as u64, + }, sa_flags: SA_FLAG_DFL, sa_mask: 0, - sa_restorer: None, + sa_restorer: NULL as u64, }; /// 默认的“忽略信号”的sigaction #[allow(dead_code)] pub static DEFAULT_SIGACTION_IGNORE: sigaction = sigaction { - _u: sigaction__union_u { _sa_handler: None }, + _u: sigaction__union_u { + _sa_handler: NULL as u64, + }, sa_flags: SA_FLAG_IGN, sa_mask: 0, - sa_restorer: None, + sa_restorer: NULL as u64, }; /// @brief kill系统调用,向指定的进程发送信号 @@ -56,13 +62,6 @@ pub static DEFAULT_SIGACTION_IGNORE: sigaction = sigaction { /// @param regs->r9 sig 信号 #[no_mangle] pub extern "C" fn sys_kill(regs: &pt_regs) -> u64 { - println!( - "sys kill, target pid={}, file={}, line={}", - regs.r8, - file!(), - line!() - ); - let pid: pid_t = regs.r8 as pid_t; let sig: SignalNumber = SignalNumber::from(regs.r9 as i32); if sig == SignalNumber::INVALID { @@ -343,18 +342,6 @@ fn wants_signal(sig: SignalNumber, pcb: &process_control_block) -> bool { return !has_sig_pending(pcb); } -/// @brief 判断指定的信号在sigset中的对应位是否被置位 -/// @return true: 给定的信号在sigset中被置位 -/// @return false: 给定的信号在sigset中没有被置位 -#[inline] -fn sig_is_member(set: &sigset_t, _sig: SignalNumber) -> bool { - return if 1 & (set >> ((_sig as u32) - 1)) != 0 { - true - } else { - false - }; -} - /// @brief 判断signal的处理是否可能使得整个进程组退出 /// @return true 可能会导致退出(不一定) #[allow(dead_code)] @@ -367,7 +354,7 @@ fn sig_fatal(pcb: &process_control_block, sig: SignalNumber) -> bool { }; // 如果handler是空,采用默认函数,signal处理可能会导致进程退出。 - if handler.is_none() { + if handler == NULL.into() { return true; } else { return false; @@ -626,7 +613,7 @@ fn setup_frame( ) -> Result { let mut err = 0; let frame: *mut sigframe = get_stack(ka, ®s, size_of::()); - + // kdebug!("frame=0x{:016x}", frame as usize); // 要求这个frame的地址位于用户空间,因此进行校验 let access_check_ok = unsafe { verify_area(frame as u64, size_of::() as u64) }; if !access_check_ok { @@ -640,7 +627,7 @@ fn setup_frame( (*frame).arg0 = sig as u64; (*frame).arg1 = &((*frame).info) as *const siginfo as usize; (*frame).arg2 = &((*frame).context) as *const sigcontext as usize; - (*frame).handler = ka._u._sa_handler.unwrap() as *mut core::ffi::c_void; + (*frame).handler = ka._u._sa_handler as usize as *mut c_void; } // 将siginfo拷贝到用户栈 @@ -653,8 +640,7 @@ fn setup_frame( // 为了与Linux的兼容性,64位程序必须由用户自行指定restorer if ka.sa_flags & SA_FLAG_RESTORER != 0 { unsafe { - (*frame).ret_code_ptr = - (&mut ka.sa_restorer.unwrap()) as *mut unsafe extern "C" fn() as *mut c_void; + (*frame).ret_code_ptr = ka.sa_restorer as usize as *mut c_void; } } else { kerror!( @@ -668,11 +654,13 @@ fn setup_frame( // todo: 在这里生成一个sigsegv,然后core dump return Err(1); } - + // 传入信号处理函数的第一个参数 regs.rdi = sig as u64; regs.rsi = unsafe { &(*frame).info as *const siginfo as u64 }; regs.rsp = frame as u64; - regs.rip = unsafe { ka._u._sa_handler.unwrap() as *const unsafe extern "C" fn() as u64 }; + regs.rip = unsafe { ka._u._sa_handler }; + + // todo: 传入新版的sa_sigaction的处理函数的第三个参数 // 如果handler位于内核空间 if regs.rip >= USER_MAX_LINEAR_ADDR { @@ -737,6 +725,25 @@ fn setup_sigcontext(context: &mut sigcontext, mask: &sigset_t, regs: &pt_regs) - return Ok(0); } +/// @brief 将指定的sigcontext恢复到当前进程的内核栈帧中,并将当前线程结构体的几个参数进行恢复 +/// +/// @param context 要被恢复的context +/// @param regs 目标栈帧(也就是把context恢复到这个栈帧中) +/// +/// @return bool true -> 成功恢复 +/// false -> 执行失败 +fn restore_sigcontext(context: *const sigcontext, regs: &mut pt_regs) -> bool { + let mut current_thread = current_pcb().thread; + unsafe { + *regs = (*context).regs; + + (*current_thread).trap_num = (*context).trap_num; + (*current_thread).cr2 = (*context).cr2; + (*current_thread).err_code = (*context).err_code; + } + return true; +} + /// @brief 刷新指定进程的sighand的sigaction,将满足条件的sigaction恢复为Default /// 除非某个信号被设置为ignore且force_default为false,否则都不会将其恢复 /// @@ -759,3 +766,260 @@ pub fn flush_signal_handlers(pcb: *mut process_control_block, force_default: boo } compiler_fence(core::sync::atomic::Ordering::SeqCst); } + +/// @brief 用户程序用于设置信号处理动作的函数(遵循posix2008) +/// +/// @param regs->r8 signumber 信号的编号 +/// @param regs->r9 act 新的,将要被设置的sigaction +/// @param regs->r10 oact 返回给用户的原本的sigaction(内核将原本的sigaction的值拷贝给这个地址) +/// +/// @return int 错误码 +#[no_mangle] +pub extern "C" fn sys_sigaction(regs: &mut pt_regs) -> u64 { + // 请注意:用户态传进来的user_sigaction结构体类型,请注意,这个结构体与内核实际的不一样 + let act = regs.r9 as usize as *mut user_sigaction; + let mut old_act = regs.r10 as usize as *mut user_sigaction; + let mut new_ka: sigaction = Default::default(); + let mut old_ka: sigaction = Default::default(); + + // 如果传入的,新的sigaction不为空 + if !act.is_null() { + // 如果参数的范围不在用户空间,则返回错误 + if unsafe { !verify_area(act as usize as u64, size_of::() as u64) } { + return (-(EFAULT as i64)) as u64; + } + let mask: sigset_t = unsafe { (*act).sa_mask }; + let _input_sah = unsafe { (*act).sa_handler as u64 }; + + match _input_sah { + USER_SIG_DFL | USER_SIG_IGN => { + if _input_sah == USER_SIG_DFL { + new_ka = DEFAULT_SIGACTION; + new_ka.sa_flags = + (unsafe { (*act).sa_flags } & (!(SA_FLAG_DFL | SA_FLAG_IGN))) | SA_FLAG_DFL; + } else { + new_ka = DEFAULT_SIGACTION_IGNORE; + new_ka.sa_flags = + (unsafe { (*act).sa_flags } & (!(SA_FLAG_DFL | SA_FLAG_IGN))) | SA_FLAG_IGN; + } + + let sar = unsafe { (*act).sa_restorer }; + new_ka.sa_restorer = sar as u64; + } + _ => { + // 从用户空间获得sigaction结构体 + new_ka = sigaction { + _u: sigaction__union_u { + _sa_handler: unsafe { (*act).sa_handler as u64 }, + }, + sa_flags: unsafe { (*act).sa_flags }, + sa_mask: sigset_t::default(), + sa_restorer: unsafe { (*act).sa_restorer as u64 }, + }; + } + } + // 如果用户手动给了sa_restorer,那么就置位SA_FLAG_RESTORER,否则报错。(用户必须手动指定restorer) + if new_ka.sa_restorer != NULL as u64 { + new_ka.sa_flags |= SA_FLAG_RESTORER; + } else { + kwarn!( + "pid:{}: in sys_sigaction: User must manually sprcify a sa_restorer for signal {}.", + current_pcb().pid, + regs.r8.clone() + ); + } + sigset_init(&mut new_ka.sa_mask, mask); + } + + let sig = SignalNumber::from(regs.r8 as i32); + // 如果给出的信号值不合法 + if sig == SignalNumber::INVALID { + return (-(EINVAL as i64)) as u64; + } + + let retval = do_sigaction( + sig, + if act.is_null() { + None + } else { + Some(&mut new_ka) + }, + if old_act.is_null() { + None + } else { + Some(&mut old_ka) + }, + ); + + // 将原本的sigaction拷贝到用户程序指定的地址 + if (retval == 0) && (!old_act.is_null()) { + if unsafe { !verify_area(old_act as usize as u64, size_of::() as u64) } { + return (-(EFAULT as i64)) as u64; + } + // !!!!!!!!!!todo: 检查这里old_ka的mask,是否位SIG_IGN SIG_DFL,如果是,则将_sa_handler字段替换为对应的值 + let sah: u64; + let flag = old_ka.sa_flags & (SA_FLAG_DFL | SA_FLAG_IGN); + match flag { + SA_FLAG_DFL => { + sah = USER_SIG_DFL; + } + SA_FLAG_IGN => { + sah = USER_SIG_IGN; + } + _ => sah = unsafe { old_ka._u._sa_handler }, + } + unsafe { + (*old_act).sa_handler = sah as *mut c_void; + (*old_act).sa_flags = old_ka.sa_flags; + (*old_act).sa_mask = old_ka.sa_mask; + (*old_act).sa_restorer = old_ka.sa_restorer as *mut c_void; + } + } + return retval as u64; +} + +fn do_sigaction( + sig: SignalNumber, + act: Option<&mut sigaction>, + old_act: Option<&mut sigaction>, +) -> i32 { + let pcb = current_pcb(); + + // 指向当前信号的action的引用 + let action = + sigaction::convert_mut(unsafe { &mut (*(pcb.sighand)).action[(sig as usize) - 1] }) + .unwrap(); + + spin_lock_irq(unsafe { &mut (*(pcb.sighand)).siglock }); + + if (action.sa_flags & SA_FLAG_IMMUTABLE) != 0 { + spin_unlock_irq(unsafe { &mut (*(pcb.sighand)).siglock }); + return -(EINVAL as i32); + } + + // 如果需要保存原有的sigaction + // 写的这么恶心,还得感谢rust的所有权系统...old_act的所有权被传入了这个闭包之后,必须要把所有权返回给外面。(也许是我不会用才导致写的这么丑,但是它确实能跑) + let old_act: Option<&mut sigaction> = { + if old_act.is_some() { + let oa = old_act.unwrap(); + *(oa) = *action; + Some(oa) + } else { + None + } + }; + + // 清除所有的脏的sa_flags位(也就是清除那些未使用的) + let act = { + if act.is_some() { + let ac = act.unwrap(); + ac.sa_flags &= SA_ALL_FLAGS; + Some(ac) + } else { + None + } + }; + + if old_act.is_some() { + old_act.unwrap().sa_flags &= SA_ALL_FLAGS; + } + + if act.is_some() { + let ac = act.unwrap(); + // 将act.sa_mask的SIGKILL SIGSTOP的屏蔽清除 + sigset_delmask( + &mut ac.sa_mask, + sigmask(SignalNumber::SIGKILL) | sigmask(SignalNumber::SIGSTOP), + ); + + // 将新的sigaction拷贝到进程的action中 + *action = *ac; + + /* + * 根据POSIX 3.3.1.3规定: + * 1.不管一个信号是否被阻塞,只要将其设置SIG_IGN,如果当前已经存在了正在pending的信号,那么就把这个信号忽略。 + * + * 2.不管一个信号是否被阻塞,只要将其设置SIG_DFL,如果当前已经存在了正在pending的信号, + 并且对这个信号的默认处理方式是忽略它,那么就会把pending的信号忽略。 + */ + if action.ignored(sig) { + let mut mask: sigset_t = 0; + sigset_clear(&mut mask); + sigset_add(&mut mask, sig); + let sq = pcb.sig_pending.sigqueue as *mut SigQueue; + let sq = unsafe { sq.as_mut::<'static>() }.unwrap(); + sq.flush_by_mask(&mask); + + // todo: 当有了多个线程后,在这里进行操作,把每个线程的sigqueue都进行刷新 + } + } + + spin_unlock_irq(unsafe { &mut (*(pcb.sighand)).siglock }); + return 0; +} + +/// @brief 对于给定的signal number,将u64中对应的位进行置位 +pub fn sigmask(sig: SignalNumber) -> u64 { + // 减1的原因是,sigset的第0位表示信号1 + return 1u64 << ((sig as i32) - 1); +} + +#[no_mangle] +pub extern "C" fn sys_rt_sigreturn(regs: &mut pt_regs) -> u64 { + // kdebug!( + // "sigreturn, pid={}, regs.rsp=0x{:018x}", + // current_pcb().pid, + // regs.rsp + // ); + let frame = regs.rsp as usize as *mut sigframe; + + // 如果当前的rsp不来自用户态,则认为产生了错误(或被SROP攻击) + if unsafe { !verify_area(frame as u64, size_of::() as u64) } { + // todo:这里改为生成一个sigsegv + // 退出进程 + unsafe { + process_do_exit(SignalNumber::SIGSEGV as u64); + } + } + + let mut sigmask: sigset_t = unsafe { (*frame).context.oldmask }; + set_current_sig_blocked(&mut sigmask); + + // 从用户栈恢复sigcontext + if restore_sigcontext(unsafe { &mut (*frame).context }, regs) == false { + // todo:这里改为生成一个sigsegv + // 退出进程 + unsafe { + process_do_exit(SignalNumber::SIGSEGV as u64); + } + } + + // 由于系统调用的返回值会被系统调用模块被存放在rax寄存器,因此,为了还原原来的那个系统调用的返回值,我们需要在这里返回恢复后的rax的值 + return regs.rax; +} + +fn set_current_sig_blocked(new_set: &mut sigset_t) { + sigset_delmask( + new_set, + sigmask(SignalNumber::SIGKILL) | sigmask(SignalNumber::SIGSTOP), + ); + + let mut pcb = current_pcb(); + + /* + 如果当前pcb的sig_blocked和新的相等,那么就不用改变它。 + 请注意,一个进程的sig_blocked字段不能被其他进程修改! + */ + if sigset_equal(&pcb.sig_blocked, new_set) { + return; + } + + let lock: &mut spinlock_t = &mut sighand_struct::convert_mut(pcb.sighand).unwrap().siglock; + spin_lock_irq(lock); + // todo: 当一个进程有多个线程后,在这里需要设置每个线程的block字段,并且 retarget_shared_pending(虽然我还没搞明白linux这部分是干啥的) + + // 设置当前进程的sig blocked + pcb.sig_blocked = *new_set; + recalc_sigpending(); + spin_unlock_irq(lock); +} diff --git a/kernel/src/ipc/signal_types.rs b/kernel/src/ipc/signal_types.rs index aed0cde3..fc24b338 100644 --- a/kernel/src/ipc/signal_types.rs +++ b/kernel/src/ipc/signal_types.rs @@ -6,6 +6,7 @@ use core::fmt::Debug; use alloc::vec::Vec; +use crate::include::bindings::bindings::NULL; // todo: 将这里更换为手动编写的ffi绑定 use crate::include::bindings::bindings::atomic_t; use crate::include::bindings::bindings::pt_regs; @@ -18,8 +19,12 @@ use crate::libs::refcount::RefCount; /// 请注意,sigset_t这个bitmap, 第0位表示sig=1的信号。也就是说,SignalNumber-1才是sigset_t中对应的位 pub type sigset_t = u64; -pub type __signalfn_t = ::core::option::Option; +/// 存储信号处理函数的地址(来自用户态) +pub type __signalfn_t = u64; pub type __sighandler_t = __signalfn_t; +/// 存储信号处理恢复函数的地址(来自用户态) +pub type __sigrestorer_fn_t = u64; +pub type __sigrestorer_t = __sigrestorer_fn_t; /// 最大的信号数量(改动这个值的时候请同步到signal.h) pub const MAX_SIG_NUM: i32 = 64; @@ -70,14 +75,27 @@ impl core::fmt::Debug for sigaction__union_u { impl Default for sigaction__union_u { fn default() -> Self { - Self { _sa_handler: None } + Self { + _sa_handler: NULL as u64, + } } } -// ============ sigaction结构体中的的sa_flags的可选值 =========== -pub const SA_FLAG_IGN: u64 = 1u64 << 0; // 当前sigaction表示忽略信号的动作 -pub const SA_FLAG_DFL: u64 = 1u64 << 1; // 当前sigaction表示系统默认的动作 +// ============ sigaction结构体中的的sa_flags的可选值 begin =========== +pub const SA_FLAG_DFL: u64 = 1u64 << 0; // 当前sigaction表示系统默认的动作 +pub const SA_FLAG_IGN: u64 = 1u64 << 1; // 当前sigaction表示忽略信号的动作 pub const SA_FLAG_RESTORER: u64 = 1u64 << 2; // 当前sigaction具有用户指定的restorer +pub const SA_FLAG_IMMUTABLE: u64 = 1u64 << 3; // 当前sigaction不可被更改 + +/// 所有的sa_flags的mask。(用于去除那些不存在的sa_flags位) +pub const SA_ALL_FLAGS: u64 = SA_FLAG_IGN | SA_FLAG_DFL | SA_FLAG_RESTORER | SA_FLAG_IMMUTABLE; + +// ============ sigaction结构体中的的sa_flags的可选值 end =========== + +/// 用户态程序传入的SIG_DFL的值 +pub const USER_SIG_DFL: u64 = 1u64 << 0; +/// 用户态程序传入的SIG_IGN的值 +pub const USER_SIG_IGN: u64 = 1u64 << 1; /** * @brief 信号处理结构体 @@ -87,9 +105,9 @@ pub const SA_FLAG_RESTORER: u64 = 1u64 << 2; // 当前sigaction具有用户指 pub struct sigaction { pub _u: sigaction__union_u, pub sa_flags: u64, - pub sa_mask: sigset_t, + pub sa_mask: sigset_t, // 为了可扩展性而设置的sa_mask /// 信号处理函数执行结束后,将会跳转到这个函数内进行执行,然后执行sigreturn系统调用 - pub sa_restorer: ::core::option::Option, + pub sa_restorer: __sigrestorer_t, } impl Default for sigaction { @@ -103,6 +121,30 @@ impl Default for sigaction { } } +impl sigaction { + /// @brief 判断这个sigaction是否被忽略 + pub fn ignored(&self, _sig: SignalNumber) -> bool { + if (self.sa_flags & SA_FLAG_IGN) != 0 { + return true; + } + // todo: 增加对sa_flags为SA_FLAG_DFL,但是默认处理函数为忽略的情况的判断 + + return false; + } +} + +/// @brief 用户态传入的sigaction结构体(符合posix规范) +/// 请注意,我们会在sys_sigaction函数里面将其转换成内核使用的sigaction结构体 +#[repr(C)] +#[derive(Debug)] +pub struct user_sigaction { + pub sa_handler: *mut core::ffi::c_void, + pub sa_sigaction: *mut core::ffi::c_void, + pub sa_mask: sigset_t, + pub sa_flags: u64, + pub sa_restorer: *mut core::ffi::c_void, +} + /** * 信号消息的结构体,作为参数传入sigaction结构体中指向的处理函数 */ @@ -485,6 +527,23 @@ impl SigQueue { return (filter_result.pop(), still_pending); } + + /// @brief 从sigqueue中删除mask中被置位的信号。也就是说,比如mask的第1位被置为1,那么就从sigqueue中删除所有signum为2的信号的信息。 + pub fn flush_by_mask(&mut self, mask: &sigset_t) { + // 定义过滤器,从sigqueue中删除mask中被置位的信号 + let filter = |x: &mut siginfo| { + if sig_is_member(mask, SignalNumber::from(unsafe { x._sinfo.data.si_signo })) { + true + } else { + false + } + }; + let filter_result: Vec = self.q.drain_filter(filter).collect(); + // 回收这些siginfo + for x in filter_result { + drop(x) + } + } } impl Default for SigQueue { @@ -509,8 +568,8 @@ pub fn sigset_del(set: &mut sigset_t, sig: SignalNumber) { /// @brief 将指定的信号在sigset中的对应bit进行置位 #[inline] -pub fn sigset_add(set: &mut sigset_t, _sig: SignalNumber) { - *set |= 1 << ((_sig as u32) - 1); +pub fn sigset_add(set: &mut sigset_t, sig: SignalNumber) { + *set |= 1 << ((sig as u32) - 1); } /// @brief 将sigset清零 @@ -519,6 +578,46 @@ pub fn sigset_clear(set: &mut sigset_t) { *set = 0; } +/// @brief 将mask中置为1的位,在sigset中清零 +#[inline] +pub fn sigset_delmask(set: &mut sigset_t, mask: u64) { + *set &= !mask; +} + +/// @brief 判断两个sigset是否相等 +#[inline] +pub fn sigset_equal(a: &sigset_t, b: &sigset_t) -> bool { + if _NSIG_U64_CNT == 1{ + return *a == *b; + } + return false; +} + +/// @brief 使用指定的值,初始化sigset(为支持将来超过64个signal留下接口) +#[inline] +pub fn sigset_init(new_set: &mut sigset_t, mask: u64) { + *new_set = mask; + match _NSIG_U64_CNT { + 1 => {} + _ => { + // 暂时不支持大于64个信号 + todo!(); + } + }; +} + +/// @brief 判断指定的信号在sigset中的对应位是否被置位 +/// @return true: 给定的信号在sigset中被置位 +/// @return false: 给定的信号在sigset中没有被置位 +#[inline] +pub fn sig_is_member(set: &sigset_t, _sig: SignalNumber) -> bool { + return if 1 & (set >> ((_sig as u32) - 1)) != 0 { + true + } else { + false + }; +} + #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct sigframe { @@ -544,7 +643,7 @@ pub struct sigcontext { pub regs: pt_regs, // 暂存的系统调用/中断返回时,原本要弹出的内核栈帧 pub trap_num: u64, // 用来保存线程结构体中的trap_num字段 - pub oldmask: u64, // 暂存的执行信号处理函数之前的sigmask + pub oldmask: u64, // 暂存的执行信号处理函数之前的,被设置block的信号 pub cr2: u64, // 用来保存线程结构体中的cr2字段 pub err_code: u64, // 用来保存线程结构体中的err_code字段 // todo: 支持x87浮点处理器后,在这里增加浮点处理器的状态结构体指针 diff --git a/kernel/src/syscall/syscall.c b/kernel/src/syscall/syscall.c index 9bd2e29f..f2fe0700 100644 --- a/kernel/src/syscall/syscall.c +++ b/kernel/src/syscall/syscall.c @@ -20,6 +20,8 @@ extern uint64_t sys_mstat(struct pt_regs *regs); extern uint64_t sys_open(struct pt_regs *regs); extern uint64_t sys_unlink_at(struct pt_regs *regs); extern uint64_t sys_kill(struct pt_regs *regs); +extern uint64_t sys_sigaction(struct pt_regs * regs); +extern uint64_t sys_rt_sigreturn(struct pt_regs * regs); /** * @brief 导出系统调用处理函数的符号 @@ -586,6 +588,8 @@ system_call_t system_call_table[MAX_SYSTEM_CALL_NUM] = { [21] = sys_mstat, [22] = sys_unlink_at, [23] = sys_kill, - [24 ... 254] = system_call_not_exists, + [24] = sys_sigaction, + [25] = sys_rt_sigreturn, + [26 ... 254] = system_call_not_exists, [255] = sys_ahci_end_req, }; diff --git a/kernel/src/syscall/syscall_num.h b/kernel/src/syscall/syscall_num.h index 8776c46c..853d95e4 100644 --- a/kernel/src/syscall/syscall_num.h +++ b/kernel/src/syscall/syscall_num.h @@ -32,8 +32,10 @@ #define SYS_CLOCK 19 // 获取当前cpu时间 #define SYS_PIPE 20 // 创建管道 -#define SYS_MSTAT 21 // 获取系统的内存状态信息 -#define SYS_UNLINK_AT 22 // 删除文件夹/删除文件链接 -#define SYS_KILL 23 // kill一个进程(向这个进程发出信号) +#define SYS_MSTAT 21 // 获取系统的内存状态信息 +#define SYS_UNLINK_AT 22 // 删除文件夹/删除文件链接 +#define SYS_KILL 23 // kill一个进程(向这个进程发出信号) +#define SYS_SIGACTION 24 // 设置进程的信号处理动作 +#define SYS_RT_SIGRETURN 25 // 从信号处理函数返回 #define SYS_AHCI_END_REQ 255 // AHCI DMA请求结束end_request的系统调用 \ No newline at end of file diff --git a/user/apps/test_signal/main.c b/user/apps/test_signal/main.c index db73f135..7ed6a3af 100644 --- a/user/apps/test_signal/main.c +++ b/user/apps/test_signal/main.c @@ -1,12 +1,12 @@ /** - * @file main.c + * @file main.c * @author longjin (longjin@RinGoTek.cn) * @brief 测试signal用的程序 * @version 0.1 * @date 2022-12-06 - * + * * @copyright Copyright (c) 2022 - * + * */ /** @@ -14,8 +14,8 @@ * 1.在DragonOS的控制台输入 exec bin/test_signal.elf & * 请注意,一定要输入末尾的 '&',否则进程不会后台运行 * 2.然后kill对应的进程的pid (上一条命令执行后,将会输出这样一行:"[1] 生成的pid") - * -*/ + * + */ #include #include @@ -23,16 +23,36 @@ #include #include +#include + +bool handle_ok = false; + +void handler(int sig) +{ + printf("handle %d\n", sig); + handle_ok = true; +} + int main() { printf("Test signal running...\n"); + signal(SIGKILL, &handler); + printf("registered.\n"); + clock_t last = clock(); while (1) { - if ((clock()-last)/CLOCKS_PER_SEC >= 1){ + if ((clock() - last) / CLOCKS_PER_SEC >= 1) + { // printf("Test signal running\n"); last = clock(); } + if (handle_ok) + { + printf("Handle OK!\n"); + handle_ok = false; + } } + return 0; } \ No newline at end of file diff --git a/user/libs/libc/src/Makefile b/user/libs/libc/src/Makefile index 075606e4..043c99bb 100644 --- a/user/libs/libc/src/Makefile +++ b/user/libs/libc/src/Makefile @@ -11,6 +11,15 @@ ifeq ($(ARCH), __x86_64__) libc_sub_dirs += sysdeps/x86_64 endif +libc_objs:= $(shell find ./*.c) + +ECHO: + @echo "$@" + + +$(libc_objs): ECHO + $(CC) $(CFLAGS) -c $@ -o $@.o + clean: cargo clean rm -rf $(GARBAGE) @@ -20,7 +29,7 @@ clean: cd .. ;\ done -libc: unistd.o fcntl.o malloc.o errno.o printf.o stdlib.o ctype.o string.o dirent.o time.o libc_rust +libc: $(libc_objs) libc_rust @list='$(libc_sub_dirs)'; for subdir in $$list; do \ echo "make all in $$subdir";\ cd $$subdir;\ @@ -28,36 +37,6 @@ libc: unistd.o fcntl.o malloc.o errno.o printf.o stdlib.o ctype.o string.o diren cd ..;\ done -unistd.o: unistd.c - $(CC) $(CFLAGS) -c unistd.c -o unistd.o - -fcntl.o: fcntl.c - $(CC) $(CFLAGS) -c fcntl.c -o fcntl.o - -malloc.o: malloc.c - $(CC) $(CFLAGS) -c malloc.c -o malloc.o - -errno.o: errno.c - $(CC) $(CFLAGS) -c errno.c -o errno.o - -printf.o: printf.c - $(CC) $(CFLAGS) -c printf.c -o printf.o - -stdlib.o: stdlib.c - $(CC) $(CFLAGS) -c stdlib.c -o stdlib.o - -ctype.o: ctype.c - $(CC) $(CFLAGS) -c ctype.c -o ctype.o - -string.o: string.c - $(CC) $(CFLAGS) -c string.c -o string.o - -dirent.o: dirent.c - $(CC) $(CFLAGS) -c dirent.c -o dirent.o - -time.o: time.c - $(CC) $(CFLAGS) -c time.c -o time.o - libc_rust: rustup default nightly cargo +nightly build --release --target ./x86_64-unknown-none.json \ No newline at end of file diff --git a/user/libs/libc/src/include/signal.h b/user/libs/libc/src/include/signal.h index dd917c75..004d98a6 100644 --- a/user/libs/libc/src/include/signal.h +++ b/user/libs/libc/src/include/signal.h @@ -1,4 +1,5 @@ #pragma once +#include #define SIGHUP 1 #define SIGINT 2 @@ -37,4 +38,52 @@ /* These should not be considered constants from userland. */ #define SIGRTMIN 32 -#define SIGRTMAX MAX_SIG_NUM \ No newline at end of file +#define SIGRTMAX MAX_SIG_NUM + +typedef void (*__sighandler_t) (int); + +// 注意,该结构体最大16字节 +union __sifields { + /* kill() */ + struct + { + pid_t _pid; /* 信号发送者的pid */ + } _kill; +}; + +// 注意,该结构体最大大小为32字节 +#define __SIGINFO \ + struct \ + { \ + int32_t si_signo; /* signal number */ \ + int32_t si_code; \ + int32_t si_errno; \ + uint32_t reserved; /* 保留备用 */ \ + union __sifields _sifields; \ + } + +typedef struct +{ + union { + __SIGINFO; + uint64_t padding[4]; // 让siginfo占用32字节大小 + }; +} siginfo_t; + +typedef struct +{ + uint64_t set; +} sigset_t; + +struct sigaction +{ + // sa_handler和sa_sigaction二选1 + __sighandler_t sa_handler; + void (*sa_sigaction)(int, siginfo_t *, void *); + sigset_t sa_mask; + uint64_t sa_flags; + void (*sa_restorer)(void); +}; + +int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); +int signal(int signum, __sighandler_t handler); \ No newline at end of file diff --git a/user/libs/libc/src/signal.c b/user/libs/libc/src/signal.c new file mode 100644 index 00000000..a000475d --- /dev/null +++ b/user/libs/libc/src/signal.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +#pragma GCC push_options +#pragma GCC optimize("O0") +void __libc_sa_restorer() +{ + // 在这里发起sigreturn,请注意,由于内核需要读取到原来的do_signal时保存的栈帧,因此这里不能发生函数调用(会导致函数压栈),只能够这样来完成sigreturn + __asm__ __volatile__("int $0x80 \n\t" ::"a"(SYS_RT_SIGRETURN) : "memory"); +} +#pragma GCC pop_options + +/** + * @brief 设置信号处理动作(简单版本) + * + * @param signum + * @param handler + * @return int + */ +int signal(int signum, __sighandler_t handler) +{ + struct sigaction sa = {0}; + sa.sa_handler = handler; + // 由于DragonOS必须由用户程序指定一个sa_restorer,因此这里设置为libc的sa_restorer + sa.sa_restorer = &__libc_sa_restorer; + // printf("handler address: %#018lx\n", handler); + // printf("restorer address: %#018lx\n", &__libc_sa_restorer); + sigaction(SIGKILL, &sa, NULL); +} + +/** + * @brief 设置信号处理动作 + * + * @param signum 信号 + * @param act 处理动作(不可为NULL) + * @param oldact 返回的旧的处理动作(若为NULL,则不返回) + * @return int 错误码(遵循posix) + */ +int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) +{ + return syscall_invoke(SYS_SIGACTION, (uint64_t)signum, (uint64_t)act, (uint64_t)oldact, 0, 0, 0, 0, 0); +} \ No newline at end of file diff --git a/user/libs/libsystem/syscall.h b/user/libs/libsystem/syscall.h index 9b11e001..f2289813 100644 --- a/user/libs/libsystem/syscall.h +++ b/user/libs/libsystem/syscall.h @@ -15,20 +15,22 @@ #define SYS_BRK 9 #define SYS_SBRK 10 -#define SYS_REBOOT 11 // 重启 -#define SYS_CHDIR 12 // 切换工作目录 +#define SYS_REBOOT 11 // 重启 +#define SYS_CHDIR 12 // 切换工作目录 #define SYS_GET_DENTS 13 // 获取目录中的数据 -#define SYS_EXECVE 14 // 执行新的应用程序 -#define SYS_WAIT4 15 // 等待进程退出 -#define SYS_EXIT 16 // 进程退出 -#define SYS_MKDIR 17 // 创建文件夹 +#define SYS_EXECVE 14 // 执行新的应用程序 +#define SYS_WAIT4 15 // 等待进程退出 +#define SYS_EXIT 16 // 进程退出 +#define SYS_MKDIR 17 // 创建文件夹 #define SYS_NANOSLEEP 18 // 纳秒级休眠 -#define SYS_CLOCK 19 // 获取当前cpu时间 +#define SYS_CLOCK 19 // 获取当前cpu时间 #define SYS_PIPE 20 -#define SYS_MSTAT 21 // 获取系统的内存状态信息 +#define SYS_MSTAT 21 // 获取系统的内存状态信息 #define SYS_UNLINK_AT 22 // 删除文件夹/删除文件链接 -#define SYS_KILL 23 // kill一个进程(向这个进程发出信号) +#define SYS_KILL 23 // kill一个进程(向这个进程发出信号) +#define SYS_SIGACTION 24 // 设置进程的信号处理动作 +#define SYS_RT_SIGRETURN 25 // 从信号处理函数返回 /** * @brief 用户态系统调用函数 @@ -44,4 +46,5 @@ * @param arg7 * @return long */ -long syscall_invoke(uint64_t syscall_id, uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6, uint64_t arg7); \ No newline at end of file +long syscall_invoke(uint64_t syscall_id, uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, + uint64_t arg5, uint64_t arg6, uint64_t arg7); \ No newline at end of file