You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
377 lines
12 KiB
377 lines
12 KiB
//! Syscalls for process
|
|
|
|
use super::*;
|
|
use crate::fs::INodeExt;
|
|
|
|
impl Syscall<'_> {
|
|
/// Fork the current process. Return the child's PID.
|
|
pub fn sys_fork(&mut self) -> SysResult {
|
|
let new_thread = self.thread.fork(self.tf);
|
|
let pid = new_thread.proc.lock().pid.get();
|
|
let tid = processor().manager().add(new_thread);
|
|
processor().manager().detach(tid);
|
|
info!("fork: {} -> {}", thread::current().id(), pid);
|
|
Ok(pid)
|
|
}
|
|
|
|
pub fn sys_vfork(&mut self) -> SysResult {
|
|
self.sys_fork()
|
|
}
|
|
|
|
/// Create a new thread in the current process.
|
|
/// The new thread's stack pointer will be set to `newsp`,
|
|
/// and thread pointer will be set to `newtls`.
|
|
/// The child tid will be stored at both `parent_tid` and `child_tid`.
|
|
/// This is partially implemented for musl only.
|
|
pub fn sys_clone(
|
|
&mut self,
|
|
flags: usize,
|
|
newsp: usize,
|
|
parent_tid: *mut u32,
|
|
child_tid: *mut u32,
|
|
newtls: usize,
|
|
) -> SysResult {
|
|
let clone_flags = CloneFlags::from_bits_truncate(flags);
|
|
info!(
|
|
"clone: flags: {:?} == {:#x}, newsp: {:#x}, parent_tid: {:?}, child_tid: {:?}, newtls: {:#x}",
|
|
clone_flags, flags, newsp, parent_tid, child_tid, newtls
|
|
);
|
|
if flags == 0x4111 || flags == 0x11 {
|
|
warn!("sys_clone is calling sys_fork instead, ignoring other args");
|
|
return self.sys_fork();
|
|
}
|
|
if (flags != 0x7d0f00) && (flags != 0x5d0f00) {
|
|
//0x5d0f00 is the args from gcc of alpine linux
|
|
//warn!("sys_clone only support musl pthread_create");
|
|
panic!(
|
|
"sys_clone only support sys_fork OR musl pthread_create without flags{:x}",
|
|
flags
|
|
);
|
|
//return Err(SysError::ENOSYS);
|
|
}
|
|
let parent_tid_ref = unsafe { self.process().vm.check_write_ptr(parent_tid)? };
|
|
let child_tid_ref = unsafe { self.process().vm.check_write_ptr(child_tid)? };
|
|
let new_thread = self
|
|
.thread
|
|
.clone(self.tf, newsp, newtls, child_tid as usize);
|
|
// FIXME: parent pid
|
|
let tid = processor().manager().add(new_thread);
|
|
processor().manager().detach(tid);
|
|
info!("clone: {} -> {}", thread::current().id(), tid);
|
|
*parent_tid_ref = tid as u32;
|
|
*child_tid_ref = tid as u32;
|
|
Ok(tid)
|
|
}
|
|
|
|
/// Wait for the process exit.
|
|
/// Return the PID. Store exit code to `wstatus` if it's not null.
|
|
pub fn sys_wait4(&mut self, pid: isize, wstatus: *mut i32) -> SysResult {
|
|
//info!("wait4: pid: {}, code: {:?}", pid, wstatus);
|
|
let wstatus = if !wstatus.is_null() {
|
|
Some(unsafe { self.process().vm.check_write_ptr(wstatus)? })
|
|
} else {
|
|
None
|
|
};
|
|
#[derive(Debug)]
|
|
enum WaitFor {
|
|
AnyChild,
|
|
Pid(usize),
|
|
}
|
|
let target = match pid {
|
|
-1 | 0 => WaitFor::AnyChild,
|
|
p if p > 0 => WaitFor::Pid(p as usize),
|
|
_ => unimplemented!(),
|
|
};
|
|
loop {
|
|
let mut proc = self.process();
|
|
// check child_exit_code
|
|
let find = match target {
|
|
WaitFor::AnyChild => proc
|
|
.child_exit_code
|
|
.iter()
|
|
.next()
|
|
.map(|(&pid, &code)| (pid, code)),
|
|
WaitFor::Pid(pid) => proc.child_exit_code.get(&pid).map(|&code| (pid, code)),
|
|
};
|
|
// if found, return
|
|
if let Some((pid, exit_code)) = find {
|
|
proc.child_exit_code.remove(&pid);
|
|
if let Some(wstatus) = wstatus {
|
|
*wstatus = exit_code as i32;
|
|
}
|
|
return Ok(pid);
|
|
}
|
|
// if not, check pid
|
|
let children: Vec<_> = proc
|
|
.children
|
|
.iter()
|
|
.filter_map(|weak| weak.upgrade())
|
|
.collect();
|
|
let invalid = match target {
|
|
WaitFor::AnyChild => children.len() == 0,
|
|
WaitFor::Pid(pid) => children
|
|
.iter()
|
|
.find(|p| p.lock().pid.get() == pid)
|
|
.is_none(),
|
|
};
|
|
if invalid {
|
|
return Err(SysError::ECHILD);
|
|
}
|
|
info!(
|
|
"wait: thread {} -> {:?}, sleep",
|
|
thread::current().id(),
|
|
target
|
|
);
|
|
let condvar = proc.child_exit.clone();
|
|
condvar.wait(proc);
|
|
}
|
|
}
|
|
|
|
/// Replaces the current ** process ** with a new process image
|
|
///
|
|
/// `argv` is an array of argument strings passed to the new program.
|
|
/// `envp` is an array of strings, conventionally of the form `key=value`,
|
|
/// which are passed as environment to the new program.
|
|
///
|
|
/// NOTICE: `argv` & `envp` can not be NULL (different from Linux)
|
|
///
|
|
/// NOTICE: for multi-thread programs
|
|
/// A call to any exec function from a process with more than one thread
|
|
/// shall result in all threads being terminated and the new executable image
|
|
/// being loaded and executed.
|
|
pub fn sys_exec(
|
|
&mut self,
|
|
path: *const u8,
|
|
argv: *const *const u8,
|
|
envp: *const *const u8,
|
|
) -> SysResult {
|
|
info!(
|
|
"exec:BEG: path: {:?}, argv: {:?}, envp: {:?}",
|
|
path, argv, envp
|
|
);
|
|
let mut proc = self.process();
|
|
let path = unsafe { proc.vm.check_and_clone_cstr(path)? };
|
|
let args = unsafe { proc.vm.check_and_clone_cstr_array(argv)? };
|
|
let envs = unsafe { proc.vm.check_and_clone_cstr_array(envp)? };
|
|
|
|
if args.is_empty() {
|
|
error!("exec: args is null");
|
|
return Err(SysError::EINVAL);
|
|
}
|
|
|
|
info!(
|
|
"exec:STEP2: path: {:?}, args: {:?}, envs: {:?}",
|
|
path, args, envs
|
|
);
|
|
|
|
// Kill other threads
|
|
proc.threads.retain(|&tid| {
|
|
if tid != processor().tid() {
|
|
processor().manager().exit(tid, 1);
|
|
}
|
|
tid == processor().tid()
|
|
});
|
|
|
|
// Read program file
|
|
let inode = proc.lookup_inode(&path)?;
|
|
|
|
// Make new Thread
|
|
let (mut vm, entry_addr, ustack_top) =
|
|
Thread::new_user_vm(&inode, &path, args, envs).map_err(|_| SysError::EINVAL)?;
|
|
|
|
// Activate new page table
|
|
core::mem::swap(&mut proc.vm, &mut vm);
|
|
unsafe {
|
|
proc.vm.activate();
|
|
}
|
|
|
|
// Modify exec path
|
|
proc.exec_path = path.clone();
|
|
drop(proc);
|
|
|
|
// Modify the TrapFrame
|
|
*self.tf = TrapFrame::new_user_thread(entry_addr, ustack_top);
|
|
|
|
info!("exec:END: path: {:?}", path);
|
|
Ok(0)
|
|
}
|
|
|
|
pub fn sys_yield(&mut self) -> SysResult {
|
|
thread::yield_now();
|
|
Ok(0)
|
|
}
|
|
|
|
/// Kill the process
|
|
pub fn sys_kill(&mut self, pid: usize, sig: usize) -> SysResult {
|
|
info!(
|
|
"kill: {} killed: {} with sig {}",
|
|
thread::current().id(),
|
|
pid,
|
|
sig
|
|
);
|
|
let current_pid = self.process().pid.get().clone();
|
|
if current_pid == pid {
|
|
// killing myself
|
|
self.sys_exit_group(sig);
|
|
} else {
|
|
if let Some(proc_arc) = PROCESSES.read().get(&pid).and_then(|weak| weak.upgrade()) {
|
|
let proc = proc_arc.lock();
|
|
// quit all threads
|
|
for tid in proc.threads.iter() {
|
|
processor().manager().exit(*tid, sig);
|
|
}
|
|
// notify parent and fill exit code
|
|
// avoid deadlock
|
|
let proc_parent = proc.parent.clone();
|
|
let pid = proc.pid.get();
|
|
drop(proc);
|
|
if let Some(parent) = proc_parent {
|
|
let mut parent = parent.lock();
|
|
parent.child_exit_code.insert(pid, sig);
|
|
parent.child_exit.notify_one();
|
|
}
|
|
Ok(0)
|
|
} else {
|
|
Err(SysError::EINVAL)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Get the current process id
|
|
pub fn sys_getpid(&mut self) -> SysResult {
|
|
info!("getpid");
|
|
Ok(self.process().pid.get())
|
|
}
|
|
|
|
/// Get the current thread id
|
|
pub fn sys_gettid(&mut self) -> SysResult {
|
|
info!("gettid");
|
|
// use pid as tid for now
|
|
Ok(thread::current().id())
|
|
}
|
|
|
|
/// Get the parent process id
|
|
pub fn sys_getppid(&mut self) -> SysResult {
|
|
if let Some(parent) = self.process().parent.as_ref() {
|
|
Ok(parent.lock().pid.get())
|
|
} else {
|
|
Ok(0)
|
|
}
|
|
}
|
|
|
|
/// Exit the current thread
|
|
pub fn sys_exit(&mut self, exit_code: usize) -> ! {
|
|
let tid = thread::current().id();
|
|
info!("exit: {}, code: {}", tid, exit_code);
|
|
let mut proc = self.process();
|
|
proc.threads.retain(|&id| id != tid);
|
|
|
|
// for last thread,
|
|
// notify parent and fill exit code
|
|
// avoid deadlock
|
|
let exit = proc.threads.len() == 0;
|
|
let proc_parent = proc.parent.clone();
|
|
let pid = proc.pid.get();
|
|
drop(proc);
|
|
if exit {
|
|
if let Some(parent) = proc_parent {
|
|
let mut parent = parent.lock();
|
|
parent.child_exit_code.insert(pid, exit_code);
|
|
parent.child_exit.notify_one();
|
|
}
|
|
}
|
|
|
|
// perform futex wake 1
|
|
// ref: http://man7.org/linux/man-pages/man2/set_tid_address.2.html
|
|
// FIXME: do it in all possible ways a thread can exit
|
|
// it has memory access so we can't move it to Thread::drop?
|
|
let mut proc = self.process();
|
|
let clear_child_tid = self.thread.clear_child_tid as *mut u32;
|
|
if !clear_child_tid.is_null() {
|
|
info!("exit: futex {:#?} wake 1", clear_child_tid);
|
|
if let Ok(clear_child_tid_ref) = unsafe { proc.vm.check_write_ptr(clear_child_tid) } {
|
|
*clear_child_tid_ref = 0;
|
|
let queue = proc.get_futex(clear_child_tid as usize);
|
|
queue.notify_one();
|
|
}
|
|
}
|
|
drop(proc);
|
|
|
|
processor().manager().exit(tid, exit_code as usize);
|
|
processor().yield_now();
|
|
unreachable!();
|
|
}
|
|
|
|
/// Exit the current thread group (i.e. process)
|
|
pub fn sys_exit_group(&mut self, exit_code: usize) -> ! {
|
|
let proc = self.process();
|
|
info!("exit_group: {}, code: {}", proc.pid, exit_code);
|
|
|
|
// quit all threads
|
|
for tid in proc.threads.iter() {
|
|
processor().manager().exit(*tid, exit_code);
|
|
}
|
|
|
|
// notify parent and fill exit code
|
|
// avoid deadlock
|
|
let proc_parent = proc.parent.clone();
|
|
let pid = proc.pid.get();
|
|
drop(proc);
|
|
if let Some(parent) = proc_parent {
|
|
let mut parent = parent.lock();
|
|
parent.child_exit_code.insert(pid, exit_code);
|
|
parent.child_exit.notify_one();
|
|
}
|
|
|
|
processor().yield_now();
|
|
unreachable!();
|
|
}
|
|
|
|
pub fn sys_nanosleep(&mut self, req: *const TimeSpec) -> SysResult {
|
|
let time = unsafe { *self.process().vm.check_read_ptr(req)? };
|
|
info!("nanosleep: time: {:#?}", time);
|
|
// TODO: handle spurious wakeup
|
|
thread::sleep(time.to_duration());
|
|
Ok(0)
|
|
}
|
|
|
|
pub fn sys_set_priority(&mut self, priority: usize) -> SysResult {
|
|
let pid = thread::current().id();
|
|
processor().manager().set_priority(pid, priority as u8);
|
|
Ok(0)
|
|
}
|
|
|
|
pub fn sys_set_tid_address(&mut self, tidptr: *mut u32) -> SysResult {
|
|
info!("set_tid_address: {:?}", tidptr);
|
|
self.thread.clear_child_tid = tidptr as usize;
|
|
Ok(thread::current().id())
|
|
}
|
|
}
|
|
|
|
bitflags! {
|
|
pub struct CloneFlags: usize {
|
|
const CSIGNAL = 0x000000ff;
|
|
const VM = 0x0000100;
|
|
const FS = 0x0000200;
|
|
const FILES = 0x0000400;
|
|
const SIGHAND = 0x0000800;
|
|
const PTRACE = 0x0002000;
|
|
const VFORK = 0x0004000;
|
|
const PARENT = 0x0008000;
|
|
const SYSVSEM = 0x0008000;
|
|
const SETTLS = 0x0008000;
|
|
const PARENT_SETTID = 0x0010000;
|
|
const CHILD_CLEARTID = 0x0020000;
|
|
const DETACHED = 0x0040000;
|
|
const UNTRACED = 0x0080000;
|
|
const CHILD_SETTID = 0x0100000;
|
|
const NEWCGROUP = 0x0200000;
|
|
const NEWUTS = 0x0400000;
|
|
const NEWIPC = 0x0800000;
|
|
const NEWUSER = 0x1000000;
|
|
const NEWPID = 0x2000000;
|
|
const NEWNET = 0x4000000;
|
|
const IO = 0x8000000;
|
|
}
|
|
}
|