DragonOS/kernel/src/ipc/signal.rs
login 66f67c6a95
signal的发送(暂时父子进程之间共享信号及相应的结构体) (#89)
* 解决由于spinlock.h中包含preempt_enable()带来的循环include问题

* new: 初步实现signal的数据结构

* new:signal相关数据结构

* fix: 解决bindings.rs报一堆警告的问题

* new: rust下的kdebug kinfo kwarn kBUG kerror宏

* 移动asm.h和cmpxchg.h

* new: signal的发送(暂时只支持父子进程共享信号及处理函数)
2022-11-23 11:38:20 +08:00

403 lines
13 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use core::ptr::read_volatile;
use crate::{
include::{
bindings::bindings::{
pid_t, process_control_block, process_find_pcb_by_pid, pt_regs, spinlock_t, EINVAL,
ENOTSUP, ESRCH, PF_EXITING, PF_KTHREAD, PF_WAKEKILL, PROC_INTERRUPTIBLE,
},
DragonOS::signal::{
si_code_val, sighand_struct, siginfo, signal_struct, sigpending, sigset_t,
SignalNumber, MAX_SIG_NUM, sigaction, sigaction__union_u,
},
},
kBUG, kdebug, kwarn,
libs::{
ffi_convert::FFIBind2Rust,
spinlock::{spin_is_locked, spin_lock_irqsave, spin_unlock_irqrestore},
},
println,
process::{
pid::PidType,
process::{process_is_stopped, process_kick, process_wake_up_state},
},
};
use crate::include::DragonOS::signal::{__siginfo_union, __siginfo_union_data};
/// 默认信号处理程序占位符用于在sighand结构体中的action数组中占位
pub static DEFAULT_SIGACTION: sigaction = sigaction{
_u: sigaction__union_u{
_sa_handler: None,
},
sa_flags:0,
sa_mask:0,
sa_restorer:None
};
/// @brief kill系统调用向指定的进程发送信号
/// @param regs->r8 pid 要接收信号的进程id
/// @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: Option<SignalNumber> = SignalNumber::from_i32(regs.r9 as i32);
if sig.is_none() {
// 传入的signal数值不合法
kwarn!("Not a valid signal number");
return (-(EINVAL as i64)) as u64;
}
// 初始化signal info
let mut info = siginfo {
_sinfo: __siginfo_union {
data: __siginfo_union_data {
si_signo: sig.unwrap() as i32,
si_code: si_code_val::SI_USER as i32,
si_errno: 0,
reserved: 0,
_sifields: crate::include::DragonOS::signal::__sifields {
_kill: crate::include::DragonOS::signal::__sifields__kill { _pid: pid },
},
},
},
};
return signal_kill_something_info(sig.unwrap(), Some(&mut info), pid) as u64;
}
/// 通过kill的方式向目标进程发送信号
/// @param sig 要发送的信号
/// @param info 要发送的信息
/// @param pid 进程id目前只支持pid>0)
fn signal_kill_something_info(sig: SignalNumber, info: Option<&mut siginfo>, pid: pid_t) -> i32 {
// 暂时不支持特殊的kill操作
if pid <= 0 {
kwarn!("Kill operation not support: pid={}", pid);
return -(ENOTSUP as i32);
}
// kill单个进程
return signal_kill_proc_info(sig, info, pid);
}
fn signal_kill_proc_info(sig: SignalNumber, info: Option<&mut siginfo>, pid: pid_t) -> i32 {
let mut retval: i32 = -(ESRCH as i32);
// step1: 当进程管理模块拥有pcblist_lock之后对其加锁
// step2: 根据pid找到pcb
let pcb = unsafe { process_find_pcb_by_pid(pid).as_mut() };
if pcb.is_none() {
kwarn!("No such process.");
return retval;
}
println!("Target pcb = {:?}", pcb.as_ref().unwrap());
// step3: 调用signal_send_sig_info函数发送信息
retval = signal_send_sig_info(sig, info, pcb.unwrap());
// step4: 解锁
return retval;
}
/// @brief 验证信号的值是否在范围内
#[inline]
fn verify_signal(sig: SignalNumber) -> bool {
return if (sig as i32) <= MAX_SIG_NUM {
true
} else {
false
};
}
/// @brief 在发送信号给指定的进程前,做一些权限检查. 检查是否有权限发送
/// @param sig 要发送的信号
/// @param info 要发送的信息
/// @param target_pcb 信号的接收者
fn signal_send_sig_info(
sig: SignalNumber,
info: Option<&mut siginfo>,
target_pcb: &mut process_control_block,
) -> i32 {
kdebug!("signal_send_sig_info");
// 检查sig是否符合要求如果不符合要求则退出。
if !verify_signal(sig) {
return -(EINVAL as i32);
}
// 信号符合要求,可以发送
let mut retval = -(ESRCH as i32);
let mut flags: u64 = 0;
// 如果上锁成功,则发送信号
if !lock_process_sighand(target_pcb, &mut flags).is_none() {
// 发送信号
retval = send_signal_locked(sig, info, target_pcb, PidType::PID);
kdebug!("flags=0x{:016x}", flags);
// 对sighand放锁
unlock_process_sighand(target_pcb, &flags);
}
return retval;
}
/// @brief 对pcb的sighand结构体中的siglock进行加锁并关闭中断
/// @param pcb 目标pcb
/// @param flags 用来保存rflags的变量
/// @return 指向sighand_struct的可变引用
fn lock_process_sighand<'a>(
pcb: &'a mut process_control_block,
flags: &mut u64,
) -> Option<&'a mut sighand_struct> {
kdebug!("lock_process_sighand");
let x = unsafe { &mut *pcb.sighand };
let sighand_ptr = sighand_struct::convert_mut(unsafe { &mut *pcb.sighand });
// kdebug!("sighand_ptr={:?}", &sighand_ptr);
if !sighand_ptr.is_some() {
kBUG!("Sighand ptr of process {pid} is NULL!", pid = pcb.pid);
return None;
}else{
kdebug!("7777");
}
let lock = {&mut sighand_ptr.unwrap().siglock};
kdebug!("123");
kdebug!("lock={}", unsafe{*(lock as *mut spinlock_t as *mut i8)});
spin_lock_irqsave(lock, flags);
kdebug!("lock={}", unsafe{*(lock as *mut spinlock_t as *mut i8)});
kdebug!("locked");
let ret = unsafe { ((*pcb).sighand as *mut sighand_struct).as_mut() };
return ret;
}
/// @brief 对pcb的sighand结构体中的siglock进行放锁并恢复之前存储的rflags
/// @param pcb 目标pcb
/// @param flags 用来保存rflags的变量将这个值恢复到rflags寄存器中
fn unlock_process_sighand(pcb: &mut process_control_block, flags: &u64) {
kdebug!("unlock_process_sighand");
let lock = unsafe{&mut (*pcb.sighand).siglock};
kdebug!("lock={:?}", lock);
spin_unlock_irqrestore(lock, flags);
kdebug!("lock={}", unsafe{*(lock as *mut spinlock_t as *mut i8)});
kdebug!("123443");
}
/// @brief 判断是否需要强制发送信号,然后发送信号
/// 注意进入该函数前我们应当对pcb.sighand.siglock加锁。
///
/// @return i32 错误码
fn send_signal_locked(
sig: SignalNumber,
info: Option<&mut siginfo>,
pcb: &mut process_control_block,
pt: PidType,
) -> i32 {
kdebug!("send_signal_locked");
// 是否强制发送信号
let mut force_send = false;
// signal的信息为空
if info.is_none() {
// todo: 判断signal是否来自于一个祖先进程的namespace如果是则强制发送信号
} else {
force_send = unsafe { info.as_ref().unwrap()._sinfo.data }.si_code
== (si_code_val::SI_KERNEL as i32);
}
kdebug!("force send={}", force_send);
return __send_signal_locked(sig, info, pcb, pt, force_send);
}
/// @brief 发送信号
/// 注意进入该函数前我们应当对pcb.sighand.siglock加锁。
///
/// @param sig 信号
/// @param _info 信号携带的信息
/// @param pcb 目标进程的pcb
/// @param pt siginfo结构体中pid字段代表的含义
/// @return i32 错误码
fn __send_signal_locked(
sig: SignalNumber,
_info: Option<&mut siginfo>,
pcb: &mut process_control_block,
pt: PidType,
_force_send: bool,
) -> i32 {
kdebug!("__send_signal_locked");
let mut retval = 0;
// 判断该进入该函数时,是否已经持有了锁
println!("locked={}",spin_is_locked(unsafe { &(*pcb.sighand).siglock }));
kdebug!("1234");
let _pending: Option<&mut sigpending> = sigpending::convert_mut(&mut pcb.sig_pending);
kdebug!("567");
// 如果是kill或者目标pcb是内核线程则无需获取sigqueue直接发送信号即可
if sig == SignalNumber::SIGKILL || (pcb.flags & (PF_KTHREAD as u64)) != 0 {
complete_signal(sig, pcb, pt);
} else {
// todo: 如果是其他信号则加入到sigqueue内然后complete_signal
retval = -(ENOTSUP as i32);
}
kdebug!("12342");
return retval;
}
/// @brief 将信号添加到目标进程的sig_pending。在引入进程组后本函数还将负责把信号传递给整个进程组。
///
/// @param sig 信号
/// @param pcb 目标pcb
/// @param pt siginfo结构体中pid字段代表的含义
fn complete_signal(sig: SignalNumber, pcb: &mut process_control_block, pt: PidType) {
// todo: 将信号产生的消息通知到正在监听这个信号的进程引入signalfd之后在这里调用signalfd_notify)
kdebug!("complete_signal");
// 将这个信号加到目标进程的sig_pending中
sigset_add(
sigset_t::convert_mut(&mut pcb.sig_pending.signal).unwrap(),
sig,
);
// ===== 寻找需要wakeup的目标进程 =====
// 备注由于当前没有进程组的概念每个进程只有1个对应的线程因此不需要通知进程组内的每个进程。
// todo: 当引入进程组的概念后,需要完善这里,使得它能寻找一个目标进程来唤醒,接着执行信号处理的操作。
let _signal: Option<&mut signal_struct> = signal_struct::convert_mut(pcb.signal);
let mut _target: Option<&mut process_control_block> = None;
// 判断目标进程是否想接收这个信号
if wants_signal(sig, pcb) {
_target = Some(pcb);
} else if pt == PidType::PID {
/*
* There is just one thread and it does not need to be woken.
* It will dequeue unblocked signals before it runs again.
*/
return;
} else {
/*
* Otherwise try to find a suitable thread.
* 由于目前每个进程只有1个线程因此当前情况可以返回。信号队列的dequeue操作不需要考虑同步阻塞的问题。
*/
return;
}
// todo:引入进程组后,在这里挑选一个进程来唤醒,让它执行相应的操作。
// todo!();
// todo: 到这里信号已经被放置在共享的pending队列中我们在这里把目标进程唤醒。
if _target.is_some() {
signal_wake_up(pcb, sig == SignalNumber::SIGKILL);
}
}
/// @brief 本函数用于检测指定的进程是否想要接收SIG这个信号。
/// 当我们对于进程组中的所有进程都运行了这个检查之后,我们将可以找到组内愿意接收信号的进程。
/// 这么做是为了防止我们把信号发送给了一个正在或已经退出的进程,或者是不响应该信号的进程。
#[inline]
fn wants_signal(sig: SignalNumber, pcb: &process_control_block) -> bool {
// 如果改进程屏蔽了这个signal则不能接收
if sig_is_member(sigset_t::convert_ref(&pcb.sig_blocked).unwrap(), sig) {
return false;
}
// 如果进程正在退出,则不能接收信号
if (pcb.flags & (PF_EXITING as u64)) > 0 {
return false;
}
if sig == SignalNumber::SIGKILL {
return true;
}
if process_is_stopped(pcb) {
return false;
}
// todo: 检查目标进程是否正在一个cpu上执行如果是则返回true否则继续检查下一项
// 检查目标进程是否有信号正在等待处理如果是则返回false否则返回true
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 将指定的信号在sigset中的对应bit进行置位
#[inline]
fn sigset_add(set: &mut sigset_t, _sig: SignalNumber) {
*set |= 1 << ((_sig as u32) - 1);
}
/// @brief 判断signal的处理是否可能使得整个进程组退出
/// @return true 可能会导致退出(不一定)
#[allow(dead_code)]
#[inline]
fn sig_fatal(pcb: &process_control_block, sig: SignalNumber) -> bool {
let handler = unsafe {
sighand_struct::convert_ref(pcb.sighand).unwrap().action[(sig as usize) - 1]
._u
._sa_handler
};
// 如果handler是空采用默认函数signal处理可能会导致进程退出。
if handler.is_none() {
return true;
} else {
return false;
}
// todo: 参照linux的sig_fatal实现完整功能
}
/// @brief 判断某个进程是否有信号正在等待处理
#[inline]
fn has_sig_pending(pcb: &process_control_block) -> bool {
let ptr = &sigpending::convert_ref(&(*pcb).sig_pending).unwrap().signal;
if unsafe { read_volatile(ptr) } != 0 {
return true;
} else {
return false;
}
}
#[inline]
fn signal_wake_up(pcb: &mut process_control_block, fatal: bool) {
let mut state: u64 = 0;
if fatal {
state = PF_WAKEKILL as u64;
}
signal_wake_up_state(pcb, state);
}
fn signal_wake_up_state(pcb: &mut process_control_block, state: u64) {
assert!(spin_is_locked(&unsafe { *pcb.sighand }.siglock));
// todo: 设置线程结构体的标志位为TIF_SIGPENDING
// 如果目标进程已经在运行则发起一个ipi使得它陷入内核
if !process_wake_up_state(pcb, state | (PROC_INTERRUPTIBLE as u64)) {
process_kick(pcb);
}
}