mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-25 18:23:22 +00:00
新增系统调用,并对照linux-6.1.9改写sys_wait4 (#440)
* 1. 新增以下系统调用 - SYS_LSTAT - SYS_READV - SYS_ACCESS - SYS_UNLINK - SYS_CHMOD - SYS_FCHMOD - SYS_UMASK - SYS_SYSINFO - SYS_CLOCK_GETTIME - SYS_FCHMODAT - SYS_FACCESSAT 2. 修改sys_wait4,使得其部分符合Linux的行为(还是有些地方不符合的,详情请对比linux-6.1.9的sys_wait4接口)
This commit is contained in:
253
kernel/src/process/exit.rs
Normal file
253
kernel/src/process/exit.rs
Normal file
@ -0,0 +1,253 @@
|
||||
use core::intrinsics::likely;
|
||||
|
||||
use alloc::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
ipc::signal::{SigChildCode, Signal},
|
||||
sched::sched,
|
||||
CurrentIrqArch,
|
||||
},
|
||||
exception::InterruptArch,
|
||||
syscall::{user_access::UserBufferWriter, SystemError},
|
||||
};
|
||||
|
||||
use super::{
|
||||
abi::WaitOption, pid::PidType, resource::RUsage, Pid, ProcessControlBlock, ProcessManager,
|
||||
ProcessState,
|
||||
};
|
||||
|
||||
/// 内核wait4时的参数
|
||||
#[derive(Debug)]
|
||||
pub struct KernelWaitOption<'a> {
|
||||
pub pid_type: PidType,
|
||||
pub pid: Pid,
|
||||
pub options: WaitOption,
|
||||
pub ret_status: i32,
|
||||
pub ret_info: Option<WaitIdInfo>,
|
||||
pub ret_rusage: Option<&'a mut RUsage>,
|
||||
pub no_task_error: Option<SystemError>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WaitIdInfo {
|
||||
pub pid: Pid,
|
||||
pub status: i32,
|
||||
pub cause: i32,
|
||||
}
|
||||
|
||||
impl<'a> KernelWaitOption<'a> {
|
||||
pub fn new(pid_type: PidType, pid: Pid, options: WaitOption) -> Self {
|
||||
Self {
|
||||
pid_type,
|
||||
pid,
|
||||
options,
|
||||
ret_status: 0,
|
||||
ret_info: None,
|
||||
ret_rusage: None,
|
||||
no_task_error: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kernel_wait4(
|
||||
mut pid: i64,
|
||||
wstatus_buf: Option<UserBufferWriter<'_>>,
|
||||
options: WaitOption,
|
||||
rusage_buf: Option<&mut RUsage>,
|
||||
) -> Result<usize, SystemError> {
|
||||
// i64::MIN is not defined
|
||||
if pid == i64::MIN {
|
||||
return Err(SystemError::ESRCH);
|
||||
}
|
||||
|
||||
// 判断pid类型
|
||||
let pidtype: PidType;
|
||||
|
||||
if pid == -1 {
|
||||
pidtype = PidType::MAX;
|
||||
} else if pid < 0 {
|
||||
pidtype = PidType::PGID;
|
||||
kwarn!("kernel_wait4: currently not support pgid, default to wait for pid\n");
|
||||
pid = -pid;
|
||||
} else if pid == 0 {
|
||||
pidtype = PidType::PGID;
|
||||
kwarn!("kernel_wait4: currently not support pgid, default to wait for pid\n");
|
||||
pid = ProcessManager::current_pcb().pid().data() as i64;
|
||||
} else {
|
||||
pidtype = PidType::PID;
|
||||
}
|
||||
|
||||
let pid = Pid(pid as usize);
|
||||
|
||||
// 构造参数
|
||||
let mut kwo = KernelWaitOption::new(pidtype, pid, options);
|
||||
|
||||
kwo.options.insert(WaitOption::WEXITED);
|
||||
kwo.ret_rusage = rusage_buf;
|
||||
|
||||
// 调用do_wait,执行等待
|
||||
let r = do_wait(&mut kwo)?;
|
||||
|
||||
// 如果有wstatus_buf,则将wstatus写入用户空间
|
||||
if let Some(mut wstatus_buf) = wstatus_buf {
|
||||
let wstatus = if let Some(ret_info) = &kwo.ret_info {
|
||||
ret_info.status
|
||||
} else {
|
||||
kwo.ret_status
|
||||
};
|
||||
wstatus_buf.copy_one_to_user(&wstatus, 0)?;
|
||||
}
|
||||
|
||||
return Ok(r);
|
||||
}
|
||||
|
||||
/// 参考 https://opengrok.ringotek.cn/xref/linux-6.1.9/kernel/exit.c#1573
|
||||
fn do_wait(kwo: &mut KernelWaitOption) -> Result<usize, SystemError> {
|
||||
let mut retval: Result<usize, SystemError>;
|
||||
// todo: 在signal struct里面增加等待队列,并在这里初始化子进程退出的回调,使得子进程退出时,能唤醒当前进程。
|
||||
|
||||
loop {
|
||||
kwo.no_task_error = Some(SystemError::ECHILD);
|
||||
let child_pcb = ProcessManager::find(kwo.pid).ok_or(SystemError::ECHILD);
|
||||
if kwo.pid_type != PidType::MAX && child_pcb.is_err() {
|
||||
if let Some(err) = &kwo.no_task_error {
|
||||
retval = Err(err.clone());
|
||||
} else {
|
||||
retval = Ok(0);
|
||||
}
|
||||
|
||||
if !kwo.options.contains(WaitOption::WNOHANG) {
|
||||
retval = Err(SystemError::ERESTARTSYS);
|
||||
if ProcessManager::current_pcb()
|
||||
.sig_info_irqsave()
|
||||
.sig_pending()
|
||||
.has_pending()
|
||||
== false
|
||||
{
|
||||
// todo: 增加子进程退出的回调后,这里可以直接等待在自身的child_wait等待队列上。
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if kwo.pid_type == PidType::PID {
|
||||
let child_pcb = child_pcb.unwrap();
|
||||
// 获取weak引用,以便于在do_waitpid中能正常drop pcb
|
||||
let child_weak = Arc::downgrade(&child_pcb);
|
||||
let r = do_waitpid(child_pcb, kwo);
|
||||
if r.is_some() {
|
||||
return r.unwrap();
|
||||
} else {
|
||||
child_weak.upgrade().unwrap().wait_queue.sleep();
|
||||
}
|
||||
} else if kwo.pid_type == PidType::MAX {
|
||||
// 等待任意子进程
|
||||
// todo: 这里有问题!如果正在for循环的过程中,子进程退出了,可能会导致父进程永远等待。
|
||||
let current_pcb = ProcessManager::current_pcb();
|
||||
let rd_childen = current_pcb.children.read();
|
||||
let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() };
|
||||
for pid in rd_childen.iter() {
|
||||
let pcb = ProcessManager::find(*pid).ok_or(SystemError::ECHILD)?;
|
||||
if pcb.sched_info().state().is_exited() {
|
||||
kwo.ret_status = pcb.sched_info().state().exit_code().unwrap() as i32;
|
||||
drop(pcb);
|
||||
unsafe { ProcessManager::release(pid.clone()) };
|
||||
return Ok(pid.clone().into());
|
||||
} else {
|
||||
unsafe { pcb.wait_queue.sleep_without_schedule() };
|
||||
}
|
||||
}
|
||||
drop(irq_guard);
|
||||
sched();
|
||||
} else {
|
||||
// todo: 对于pgid的处理
|
||||
kwarn!("kernel_wait4: currently not support {:?}", kwo.pid_type);
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
fn do_waitpid(
|
||||
child_pcb: Arc<ProcessControlBlock>,
|
||||
kwo: &mut KernelWaitOption,
|
||||
) -> Option<Result<usize, SystemError>> {
|
||||
let state = child_pcb.sched_info().state();
|
||||
// 获取退出码
|
||||
match state {
|
||||
ProcessState::Runnable => {
|
||||
if kwo.options.contains(WaitOption::WNOHANG)
|
||||
|| kwo.options.contains(WaitOption::WNOWAIT)
|
||||
{
|
||||
if let Some(info) = &mut kwo.ret_info {
|
||||
*info = WaitIdInfo {
|
||||
pid: child_pcb.pid(),
|
||||
status: Signal::SIGCONT as i32,
|
||||
cause: SigChildCode::Continued.into(),
|
||||
};
|
||||
} else {
|
||||
kwo.ret_status = 0xffff;
|
||||
}
|
||||
|
||||
return Some(Ok(0));
|
||||
}
|
||||
}
|
||||
ProcessState::Blocked(_) | ProcessState::Stopped => {
|
||||
// todo: 在stopped里面,添加code字段,表示停止的原因
|
||||
let exitcode = 0;
|
||||
// 由于目前不支持ptrace,因此这个值为false
|
||||
let ptrace = false;
|
||||
|
||||
if (!ptrace) && (!kwo.options.contains(WaitOption::WUNTRACED)) {
|
||||
kwo.ret_status = 0;
|
||||
return Some(Ok(0));
|
||||
}
|
||||
|
||||
if likely(!(kwo.options.contains(WaitOption::WNOWAIT))) {
|
||||
kwo.ret_status = (exitcode << 8) | 0x7f;
|
||||
}
|
||||
if let Some(infop) = &mut kwo.ret_info {
|
||||
*infop = WaitIdInfo {
|
||||
pid: child_pcb.pid(),
|
||||
status: exitcode,
|
||||
cause: SigChildCode::Stopped.into(),
|
||||
};
|
||||
}
|
||||
|
||||
return Some(Ok(child_pcb.pid().data()));
|
||||
}
|
||||
ProcessState::Exited(status) => {
|
||||
let pid = child_pcb.pid();
|
||||
// kdebug!("wait4: child exited, pid: {:?}, status: {status}\n", pid);
|
||||
|
||||
if likely(!kwo.options.contains(WaitOption::WEXITED)) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// todo: 增加对线程组的group leader的处理
|
||||
|
||||
if let Some(infop) = &mut kwo.ret_info {
|
||||
*infop = WaitIdInfo {
|
||||
pid,
|
||||
status: status as i32,
|
||||
cause: SigChildCode::Exited.into(),
|
||||
};
|
||||
}
|
||||
|
||||
kwo.ret_status = status as i32;
|
||||
|
||||
drop(child_pcb);
|
||||
// kdebug!("wait4: to release {pid:?}");
|
||||
unsafe { ProcessManager::release(pid) };
|
||||
return Some(Ok(pid.into()));
|
||||
}
|
||||
};
|
||||
|
||||
return None;
|
||||
}
|
@ -55,6 +55,7 @@ use self::kthread::WorkerPrivate;
|
||||
pub mod abi;
|
||||
pub mod c_adapter;
|
||||
pub mod exec;
|
||||
pub mod exit;
|
||||
pub mod fork;
|
||||
pub mod idle;
|
||||
pub mod init;
|
||||
@ -473,6 +474,7 @@ impl ProcessState {
|
||||
return matches!(self, ProcessState::Blocked(true));
|
||||
}
|
||||
|
||||
/// Returns `true` if the process state is [`Exited`].
|
||||
#[inline(always)]
|
||||
pub fn is_exited(&self) -> bool {
|
||||
return matches!(self, ProcessState::Exited(_));
|
||||
@ -485,6 +487,15 @@ impl ProcessState {
|
||||
pub fn is_stopped(&self) -> bool {
|
||||
matches!(self, ProcessState::Stopped)
|
||||
}
|
||||
|
||||
/// Returns exit code if the process state is [`Exited`].
|
||||
#[inline(always)]
|
||||
pub fn exit_code(&self) -> Option<usize> {
|
||||
match self {
|
||||
ProcessState::Exited(code) => Some(*code),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
@ -848,6 +859,10 @@ impl ProcessControlBlock {
|
||||
self.sig_info.read()
|
||||
}
|
||||
|
||||
pub fn sig_info_irqsave(&self) -> RwLockReadGuard<ProcessSignalInfo> {
|
||||
self.sig_info.read_irqsave()
|
||||
}
|
||||
|
||||
pub fn try_siginfo(&self, times: u8) -> Option<RwLockReadGuard<ProcessSignalInfo>> {
|
||||
for _ in 0..times {
|
||||
if let Some(r) = self.sig_info.try_read() {
|
||||
|
@ -8,13 +8,13 @@ use alloc::{
|
||||
|
||||
use super::{
|
||||
abi::WaitOption,
|
||||
exit::kernel_wait4,
|
||||
fork::{CloneFlags, KernelCloneArgs},
|
||||
resource::{RLimit64, RLimitID, RUsage, RUsageWho},
|
||||
KernelStack, Pid, ProcessManager, ProcessState,
|
||||
KernelStack, Pid, ProcessManager,
|
||||
};
|
||||
use crate::{
|
||||
arch::{interrupt::TrapFrame, sched::sched, CurrentIrqArch, MMArch},
|
||||
exception::InterruptArch,
|
||||
arch::{interrupt::TrapFrame, MMArch},
|
||||
filesystem::{
|
||||
procfs::procfs_register_pid,
|
||||
vfs::{file::FileDescriptorVec, MAX_PATHLEN},
|
||||
@ -24,9 +24,7 @@ use crate::{
|
||||
process::ProcessControlBlock,
|
||||
sched::completion::Completion,
|
||||
syscall::{
|
||||
user_access::{
|
||||
check_and_clone_cstr, check_and_clone_cstr_array, UserBufferReader, UserBufferWriter,
|
||||
},
|
||||
user_access::{check_and_clone_cstr, check_and_clone_cstr_array, UserBufferWriter},
|
||||
Syscall, SystemError,
|
||||
},
|
||||
};
|
||||
@ -38,11 +36,16 @@ impl Syscall {
|
||||
}
|
||||
|
||||
pub fn vfork(frame: &mut TrapFrame) -> Result<usize, SystemError> {
|
||||
ProcessManager::fork(
|
||||
frame,
|
||||
CloneFlags::CLONE_VM | CloneFlags::CLONE_FS | CloneFlags::CLONE_SIGNAL,
|
||||
)
|
||||
.map(|pid| pid.into())
|
||||
// 由于Linux vfork需要保证子进程先运行(除非子进程调用execve或者exit),
|
||||
// 而我们目前没有实现这个特性,所以暂时使用fork代替vfork(linux文档表示这样也是也可以的)
|
||||
Self::fork(frame)
|
||||
|
||||
// 下面是以前的实现,除非我们实现了子进程先运行的特性,否则不要使用,不然会导致父进程数据损坏
|
||||
// ProcessManager::fork(
|
||||
// frame,
|
||||
// CloneFlags::CLONE_VM | CloneFlags::CLONE_FS | CloneFlags::CLONE_SIGNAL,
|
||||
// )
|
||||
// .map(|pid| pid.into())
|
||||
}
|
||||
|
||||
pub fn execve(
|
||||
@ -100,98 +103,35 @@ impl Syscall {
|
||||
options: i32,
|
||||
rusage: *mut c_void,
|
||||
) -> Result<usize, SystemError> {
|
||||
let ret = WaitOption::from_bits(options as u32);
|
||||
let options = match ret {
|
||||
Some(options) => options,
|
||||
None => {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
let options = WaitOption::from_bits(options as u32).ok_or(SystemError::EINVAL)?;
|
||||
|
||||
let wstatus_buf = if wstatus.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(UserBufferWriter::new(
|
||||
wstatus,
|
||||
core::mem::size_of::<i32>(),
|
||||
true,
|
||||
)?)
|
||||
};
|
||||
|
||||
let mut _rusage_buf =
|
||||
UserBufferReader::new::<c_void>(rusage, core::mem::size_of::<c_void>(), true)?;
|
||||
|
||||
let mut wstatus_buf =
|
||||
UserBufferWriter::new::<i32>(wstatus, core::mem::size_of::<i32>(), true)?;
|
||||
|
||||
let cur_pcb = ProcessManager::current_pcb();
|
||||
let rd_childen = cur_pcb.children.read();
|
||||
|
||||
if pid > 0 {
|
||||
let pid = Pid(pid as usize);
|
||||
let child_pcb = ProcessManager::find(pid).ok_or(SystemError::ECHILD)?;
|
||||
drop(rd_childen);
|
||||
|
||||
loop {
|
||||
let state = child_pcb.sched_info().state();
|
||||
// 获取退出码
|
||||
match state {
|
||||
ProcessState::Runnable => {
|
||||
if options.contains(WaitOption::WNOHANG)
|
||||
|| options.contains(WaitOption::WNOWAIT)
|
||||
{
|
||||
if !wstatus.is_null() {
|
||||
wstatus_buf.copy_one_to_user(&WaitOption::WCONTINUED.bits(), 0)?;
|
||||
}
|
||||
return Ok(0);
|
||||
}
|
||||
}
|
||||
ProcessState::Blocked(_) | ProcessState::Stopped => {
|
||||
// 指定WUNTRACED则等待暂停的进程,不指定则返回0
|
||||
if !options.contains(WaitOption::WUNTRACED)
|
||||
|| options.contains(WaitOption::WNOWAIT)
|
||||
{
|
||||
if !wstatus.is_null() {
|
||||
wstatus_buf.copy_one_to_user(&WaitOption::WSTOPPED.bits(), 0)?;
|
||||
}
|
||||
return Ok(0);
|
||||
}
|
||||
}
|
||||
ProcessState::Exited(status) => {
|
||||
// kdebug!("wait4: child exited, pid: {:?}, status: {status}\n", pid);
|
||||
if !wstatus.is_null() {
|
||||
wstatus_buf.copy_one_to_user(
|
||||
&(status as u32 | WaitOption::WEXITED.bits()),
|
||||
0,
|
||||
)?;
|
||||
}
|
||||
drop(child_pcb);
|
||||
// kdebug!("wait4: to release {pid:?}");
|
||||
unsafe { ProcessManager::release(pid) };
|
||||
return Ok(pid.into());
|
||||
}
|
||||
};
|
||||
|
||||
// 等待指定进程
|
||||
child_pcb.wait_queue.sleep();
|
||||
}
|
||||
} else if pid < -1 {
|
||||
// TODO 判断是否pgid == -pid(等待指定组任意进程)
|
||||
// 暂时不支持
|
||||
return Err(SystemError::EINVAL);
|
||||
} else if pid == 0 {
|
||||
// TODO 判断是否pgid == current_pgid(等待当前组任意进程)
|
||||
// 暂时不支持
|
||||
return Err(SystemError::EINVAL);
|
||||
let mut tmp_rusage = if rusage.is_null() {
|
||||
None
|
||||
} else {
|
||||
// 等待任意子进程(这两)
|
||||
let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() };
|
||||
for pid in rd_childen.iter() {
|
||||
let pcb = ProcessManager::find(*pid).ok_or(SystemError::ECHILD)?;
|
||||
if pcb.sched_info().state().is_exited() {
|
||||
if !wstatus.is_null() {
|
||||
wstatus_buf.copy_one_to_user(&0, 0)?;
|
||||
}
|
||||
return Ok(pid.clone().into());
|
||||
} else {
|
||||
unsafe { pcb.wait_queue.sleep_without_schedule() };
|
||||
}
|
||||
}
|
||||
drop(irq_guard);
|
||||
sched();
|
||||
}
|
||||
Some(RUsage::default())
|
||||
};
|
||||
|
||||
return Ok(0);
|
||||
let r = kernel_wait4(pid, wstatus_buf, options, tmp_rusage.as_mut())?;
|
||||
|
||||
if !rusage.is_null() {
|
||||
let mut rusage_buf = UserBufferWriter::new::<RUsage>(
|
||||
rusage as *mut RUsage,
|
||||
core::mem::size_of::<RUsage>(),
|
||||
true,
|
||||
)?;
|
||||
rusage_buf.copy_one_to_user(&tmp_rusage.unwrap(), 0)?;
|
||||
}
|
||||
return Ok(r);
|
||||
}
|
||||
|
||||
/// # 退出进程
|
||||
@ -356,16 +296,12 @@ impl Syscall {
|
||||
pub fn prlimit64(
|
||||
_pid: Pid,
|
||||
resource: usize,
|
||||
new_limit: *const RLimit64,
|
||||
_new_limit: *const RLimit64,
|
||||
old_limit: *mut RLimit64,
|
||||
) -> Result<usize, SystemError> {
|
||||
let resource = RLimitID::try_from(resource)?;
|
||||
let mut writer = None;
|
||||
|
||||
if new_limit.is_null() {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
|
||||
if !old_limit.is_null() {
|
||||
writer = Some(UserBufferWriter::new(
|
||||
old_limit,
|
||||
@ -374,8 +310,6 @@ impl Syscall {
|
||||
)?);
|
||||
}
|
||||
|
||||
let _reader = UserBufferReader::new(new_limit, core::mem::size_of::<RLimit64>(), true)?;
|
||||
|
||||
match resource {
|
||||
RLimitID::Stack => {
|
||||
if let Some(mut writer) = writer {
|
||||
|
Reference in New Issue
Block a user