mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-10 13:56:48 +00:00
Support interrupting foreground job with Ctrl+C
This commit is contained in:
parent
ad76b0329a
commit
32f3f5c300
@ -91,12 +91,10 @@ fn run_busybox() -> Result<Arc<Process>> {
|
||||
let argv = ["sh", "-l"];
|
||||
let envp = [
|
||||
"SHELL=/bin/sh",
|
||||
"PWD=/",
|
||||
"LOGNAME=root",
|
||||
"HOME=/",
|
||||
"USER=root",
|
||||
"PATH=/bin",
|
||||
"OLDPWD=/",
|
||||
];
|
||||
let argv = argv
|
||||
.into_iter()
|
||||
|
@ -124,12 +124,14 @@ pub fn clone_child(parent_context: CpuContext, clone_args: CloneArgs) -> Result<
|
||||
let child_thread = clone_child_thread(parent_context, clone_args)?;
|
||||
let child_tid = child_thread.tid();
|
||||
debug!(
|
||||
"*********schedule child thread, child pid = {}**********",
|
||||
"*********schedule child thread, current tid = {}, child pid = {}**********",
|
||||
current_thread!().tid(),
|
||||
child_tid
|
||||
);
|
||||
child_thread.run();
|
||||
debug!(
|
||||
"*********return to parent thread, child pid = {}*********",
|
||||
"*********return to parent thread, current tid = {}, child pid = {}*********",
|
||||
current_thread!().tid(),
|
||||
child_tid
|
||||
);
|
||||
Ok(child_tid)
|
||||
@ -137,12 +139,14 @@ pub fn clone_child(parent_context: CpuContext, clone_args: CloneArgs) -> Result<
|
||||
let child_process = clone_child_process(parent_context, clone_args)?;
|
||||
let child_pid = child_process.pid();
|
||||
debug!(
|
||||
"*********schedule child process, child pid = {}**********",
|
||||
"*********schedule child process, current pid = {}, child pid = {}**********",
|
||||
current!().pid(),
|
||||
child_pid
|
||||
);
|
||||
child_process.run();
|
||||
debug!(
|
||||
"*********return to parent process, child pid = {}*********",
|
||||
"*********return to parent process, current pid = {}, child pid = {}*********",
|
||||
current!().pid(),
|
||||
child_pid
|
||||
);
|
||||
Ok(child_pid)
|
||||
@ -197,7 +201,7 @@ fn clone_child_process(parent_context: CpuContext, clone_args: CloneArgs) -> Res
|
||||
// clone vm
|
||||
let parent_root_vmar = current.root_vmar();
|
||||
let child_root_vmar = clone_vm(parent_root_vmar, clone_flags)?;
|
||||
let child_user_vm = Some(current.user_vm().unwrap().clone());
|
||||
let child_user_vm = current.user_vm().clone();
|
||||
|
||||
// clone user space
|
||||
let child_cpu_context = clone_cpu_context(
|
||||
@ -221,7 +225,7 @@ fn clone_child_process(parent_context: CpuContext, clone_args: CloneArgs) -> Res
|
||||
// clone system V semaphore
|
||||
clone_sysvsem(clone_flags)?;
|
||||
|
||||
let child_elf_path = current.executable_path().unwrap().clone();
|
||||
let child_elf_path = current.executable_path().read().clone();
|
||||
let child_thread_name = ThreadName::new_from_executable_path(&child_elf_path)?;
|
||||
|
||||
// inherit parent's sig mask
|
||||
@ -243,7 +247,7 @@ fn clone_child_process(parent_context: CpuContext, clone_args: CloneArgs) -> Res
|
||||
child_pid,
|
||||
parent,
|
||||
vec![child_thread],
|
||||
Some(child_elf_path),
|
||||
child_elf_path,
|
||||
child_user_vm,
|
||||
child_root_vmar.clone(),
|
||||
Weak::new(),
|
||||
|
@ -40,12 +40,11 @@ pub type ExitCode = i32;
|
||||
const INIT_PROCESS_PID: Pid = 1;
|
||||
|
||||
/// Process stands for a set of threads that shares the same userspace.
|
||||
/// Currently, we only support one thread inside a process.
|
||||
pub struct Process {
|
||||
// Immutable Part
|
||||
pid: Pid,
|
||||
executable_path: Option<String>,
|
||||
user_vm: Option<UserVm>,
|
||||
|
||||
user_vm: UserVm,
|
||||
root_vmar: Arc<Vmar<Full>>,
|
||||
/// wait for child status changed
|
||||
waiting_children: WaitQueue,
|
||||
@ -53,6 +52,8 @@ pub struct Process {
|
||||
poll_queue: WaitQueue,
|
||||
|
||||
// Mutable Part
|
||||
/// The executable path.
|
||||
executable_path: RwLock<String>,
|
||||
/// The threads
|
||||
threads: Mutex<Vec<Arc<Thread>>>,
|
||||
/// The exit code
|
||||
@ -97,8 +98,8 @@ impl Process {
|
||||
pid: Pid,
|
||||
parent: Weak<Process>,
|
||||
threads: Vec<Arc<Thread>>,
|
||||
executable_path: Option<String>,
|
||||
user_vm: Option<UserVm>,
|
||||
executable_path: String,
|
||||
user_vm: UserVm,
|
||||
root_vmar: Arc<Vmar<Full>>,
|
||||
process_group: Weak<ProcessGroup>,
|
||||
file_table: Arc<Mutex<FileTable>>,
|
||||
@ -113,7 +114,7 @@ impl Process {
|
||||
Self {
|
||||
pid,
|
||||
threads: Mutex::new(threads),
|
||||
executable_path,
|
||||
executable_path: RwLock::new(executable_path),
|
||||
user_vm,
|
||||
root_vmar,
|
||||
waiting_children,
|
||||
@ -146,6 +147,8 @@ impl Process {
|
||||
argv: Vec<CString>,
|
||||
envp: Vec<CString>,
|
||||
) -> Result<Arc<Self>> {
|
||||
// spawn user process should give an absolute path
|
||||
debug_assert!(executable_path.starts_with('/'));
|
||||
let process = Process::create_user_process(executable_path, argv, envp)?;
|
||||
// FIXME: How to determine the fg process group?
|
||||
let pgid = process.pgid();
|
||||
@ -174,8 +177,8 @@ impl Process {
|
||||
pid,
|
||||
parent,
|
||||
vec![],
|
||||
Some(executable_path.to_string()),
|
||||
Some(user_vm),
|
||||
executable_path.to_string(),
|
||||
user_vm,
|
||||
Arc::new(root_vmar),
|
||||
process_group,
|
||||
Arc::new(Mutex::new(file_table)),
|
||||
@ -314,8 +317,8 @@ impl Process {
|
||||
}
|
||||
|
||||
/// returns the user_vm
|
||||
pub fn user_vm(&self) -> Option<&UserVm> {
|
||||
self.user_vm.as_ref()
|
||||
pub fn user_vm(&self) -> &UserVm {
|
||||
&self.user_vm
|
||||
}
|
||||
|
||||
/// returns the root vmar
|
||||
@ -324,11 +327,8 @@ impl Process {
|
||||
}
|
||||
|
||||
/// returns the user heap if the process does have, otherwise None
|
||||
pub fn user_heap(&self) -> Option<&UserHeap> {
|
||||
match self.user_vm {
|
||||
None => None,
|
||||
Some(ref user_vm) => Some(user_vm.user_heap()),
|
||||
}
|
||||
pub fn user_heap(&self) -> &UserHeap {
|
||||
self.user_vm.user_heap()
|
||||
}
|
||||
|
||||
/// free zombie child with pid, returns the exit code of child process.
|
||||
@ -336,6 +336,7 @@ impl Process {
|
||||
pub fn reap_zombie_child(&self, pid: Pid) -> i32 {
|
||||
let child_process = self.children.lock().remove(&pid).unwrap();
|
||||
assert!(child_process.status().lock().is_zombie());
|
||||
child_process.root_vmar().destroy_all().unwrap();
|
||||
for thread in &*child_process.threads.lock() {
|
||||
thread_table::remove_thread(thread.tid());
|
||||
}
|
||||
@ -359,8 +360,8 @@ impl Process {
|
||||
self.children.lock().len() != 0
|
||||
}
|
||||
|
||||
pub fn executable_path(&self) -> Option<&String> {
|
||||
self.executable_path.as_ref()
|
||||
pub fn executable_path(&self) -> &RwLock<String> {
|
||||
&self.executable_path
|
||||
}
|
||||
|
||||
pub fn status(&self) -> &Mutex<ProcessStatus> {
|
||||
|
@ -34,7 +34,7 @@ impl PosixThreadExt for Thread {
|
||||
argv: Vec<CString>,
|
||||
envp: Vec<CString>,
|
||||
) -> Result<Arc<Self>> {
|
||||
let elf_load_info = load_program_to_root_vmar(
|
||||
let (_, elf_load_info) = load_program_to_root_vmar(
|
||||
root_vmar,
|
||||
executable_path.to_string(),
|
||||
argv,
|
||||
|
@ -25,15 +25,16 @@ pub fn load_program_to_root_vmar(
|
||||
envp: Vec<CString>,
|
||||
fs_resolver: &FsResolver,
|
||||
recursion_limit: usize,
|
||||
) -> Result<ElfLoadInfo> {
|
||||
) -> Result<(String, ElfLoadInfo)> {
|
||||
// Temporary use because fs_resolver cannot deal with procfs now.
|
||||
// FIXME: removes this when procfs is ready.
|
||||
let executable_path = if &executable_path == "/proc/self/exe" {
|
||||
current!().executable_path().unwrap().clone()
|
||||
current!().executable_path().read().clone()
|
||||
} else {
|
||||
executable_path
|
||||
};
|
||||
let fs_path = FsPath::new(AT_FDCWD, &executable_path)?;
|
||||
let abs_path = fs_resolver.lookup(&fs_path)?.abs_path();
|
||||
let file = fs_resolver.open(&fs_path, AccessMode::O_RDONLY as u32, 0)?;
|
||||
let file_header = {
|
||||
// read the first page of file header
|
||||
@ -58,5 +59,6 @@ pub fn load_program_to_root_vmar(
|
||||
}
|
||||
|
||||
let elf_file = Arc::new(FileHandle::new_inode_handle(file));
|
||||
load_elf_to_root_vmar(root_vmar, &file_header, elf_file, argv, envp)
|
||||
let elf_load_info = load_elf_to_root_vmar(root_vmar, &file_header, elf_file, argv, envp)?;
|
||||
Ok((abs_path, elf_load_info))
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ pub fn handle_pending_signal(context: &mut CpuContext) -> Result<()> {
|
||||
let current_thread = current_thread!();
|
||||
let posix_thread = current_thread.as_posix_thread().unwrap();
|
||||
let pid = current.pid();
|
||||
let process_name = current.executable_path().unwrap();
|
||||
let sig_mask = posix_thread.sig_mask().lock().clone();
|
||||
let mut thread_sig_queues = posix_thread.sig_queues().lock();
|
||||
let mut proc_sig_queues = current.sig_queues().lock();
|
||||
@ -45,12 +44,12 @@ pub fn handle_pending_signal(context: &mut CpuContext) -> Result<()> {
|
||||
};
|
||||
if let Some(signal) = signal {
|
||||
let sig_num = signal.num();
|
||||
debug!("sig_num = {:?}, sig_name = {}", sig_num, sig_num.sig_name());
|
||||
trace!("sig_num = {:?}, sig_name = {}", sig_num, sig_num.sig_name());
|
||||
let sig_action = current.sig_dispositions().lock().get(sig_num);
|
||||
debug!("sig action: {:x?}", sig_action);
|
||||
trace!("sig action: {:x?}", sig_action);
|
||||
match sig_action {
|
||||
SigAction::Ign => {
|
||||
debug!("Ignore signal {:?}", sig_num);
|
||||
trace!("Ignore signal {:?}", sig_num);
|
||||
}
|
||||
SigAction::User {
|
||||
handler_addr,
|
||||
@ -68,12 +67,12 @@ pub fn handle_pending_signal(context: &mut CpuContext) -> Result<()> {
|
||||
)?,
|
||||
SigAction::Dfl => {
|
||||
let sig_default_action = SigDefaultAction::from_signum(sig_num);
|
||||
debug!("sig_default_action: {:?}", sig_default_action);
|
||||
trace!("sig_default_action: {:?}", sig_default_action);
|
||||
match sig_default_action {
|
||||
SigDefaultAction::Core | SigDefaultAction::Term => {
|
||||
error!(
|
||||
warn!(
|
||||
"{:?}: terminating on signal {}",
|
||||
process_name,
|
||||
current.executable_path().read(),
|
||||
sig_num.sig_name()
|
||||
);
|
||||
// FIXME: How to set correct status if process is terminated
|
||||
@ -116,7 +115,7 @@ pub fn handle_user_signal(
|
||||
context: &mut CpuContext,
|
||||
sig_info: siginfo_t,
|
||||
) -> Result<()> {
|
||||
debug!("sig_num = {:?}", sig_num);
|
||||
debug!("sig_num = {:?}, signame = {}", sig_num, sig_num.sig_name());
|
||||
debug!("handler_addr = 0x{:x}", handler_addr);
|
||||
debug!("flags = {:?}", flags);
|
||||
debug!("restorer_addr = 0x{:x}", restorer_addr);
|
||||
@ -134,7 +133,8 @@ pub fn handle_user_signal(
|
||||
// block signals in sigmask when running signal handler
|
||||
posix_thread.sig_mask().lock().block(mask.as_u64());
|
||||
|
||||
// set up signal stack in user stack. // avoid corrupt user stack, we minus 128 first.
|
||||
// Set up signal stack in user stack,
|
||||
// to avoid corrupting user stack, we minus 128 first.
|
||||
let mut user_rsp = context.gp_regs.rsp;
|
||||
user_rsp = user_rsp - 128;
|
||||
|
||||
@ -167,7 +167,7 @@ pub fn handle_user_signal(
|
||||
// If contains SA_RESTORER flag, trampoline code is provided by libc in restorer_addr.
|
||||
// We just store restorer_addr on user stack to allow user code just to trampoline code.
|
||||
user_rsp = write_u64_to_user_stack(user_rsp, restorer_addr as u64)?;
|
||||
debug!("After set restorer addr: user_rsp = 0x{:x}", user_rsp);
|
||||
trace!("After set restorer addr: user_rsp = 0x{:x}", user_rsp);
|
||||
} else {
|
||||
// Otherwise we create a trampoline.
|
||||
// FIXME: This may cause problems if we read old_context from rsp.
|
||||
|
@ -14,7 +14,7 @@ pub fn sys_brk(heap_end: u64) -> Result<SyscallReturn> {
|
||||
};
|
||||
debug!("new heap end = {:x?}", heap_end);
|
||||
let current = current!();
|
||||
let user_heap = current.user_heap().unwrap();
|
||||
let user_heap = current.user_heap();
|
||||
let new_heap_end = user_heap.brk(new_heap_end)?;
|
||||
|
||||
Ok(SyscallReturn::Return(new_heap_end as _))
|
||||
|
@ -17,6 +17,7 @@ pub fn sys_execve(
|
||||
log_syscall_entry!(SYS_EXECVE);
|
||||
let executable_path = read_cstring_from_user(filename_ptr, MAX_FILENAME_LEN)?;
|
||||
let executable_path = executable_path.into_string().unwrap();
|
||||
|
||||
let argv = read_cstring_vec(argv_ptr_ptr, MAX_ARGV_NUMBER, MAX_ARG_LEN)?;
|
||||
let envp = read_cstring_vec(envp_ptr_ptr, MAX_ENVP_NUMBER, MAX_ENV_LEN)?;
|
||||
debug!(
|
||||
@ -33,20 +34,19 @@ pub fn sys_execve(
|
||||
// FIXME: should we clear ctid when execve?
|
||||
*posix_thread.clear_child_tid().lock() = 0;
|
||||
|
||||
// let elf_file_content = crate::user_apps::read_execve_hello_content();
|
||||
let current = current!();
|
||||
// destroy root vmars
|
||||
let root_vmar = current.root_vmar();
|
||||
root_vmar.clear()?;
|
||||
let user_vm = current
|
||||
.user_vm()
|
||||
.expect("[Internal Error] User process should have user vm");
|
||||
user_vm.set_default();
|
||||
current.user_vm().set_default();
|
||||
// load elf content to new vm space
|
||||
let fs_resolver = &*current.fs().read();
|
||||
let elf_load_info =
|
||||
debug!("load program to root vmar");
|
||||
let (new_executable_path, elf_load_info) =
|
||||
load_program_to_root_vmar(root_vmar, executable_path, argv, envp, fs_resolver, 1)?;
|
||||
debug!("load elf in execve succeeds");
|
||||
// set executable path
|
||||
*current.executable_path().write() = new_executable_path;
|
||||
// set signal disposition to default
|
||||
current.sig_dispositions().lock().inherit();
|
||||
// set cpu context to default
|
||||
|
@ -26,7 +26,7 @@ pub fn sys_readlinkat(
|
||||
let current = current!();
|
||||
if pathname == CString::new("/proc/self/exe")? {
|
||||
// "proc/self/exe" is used to read the filename of current executable
|
||||
let process_file_name = current.executable_path().unwrap();
|
||||
let process_file_name = current.executable_path().read();
|
||||
debug!("process exec filename= {:?}", process_file_name);
|
||||
// readlink does not append a terminating null byte to buf
|
||||
let bytes = process_file_name.as_bytes();
|
||||
|
@ -33,7 +33,7 @@ pub fn sys_rt_sigaction(
|
||||
if sig_action_addr != 0 {
|
||||
let sig_action_c = read_val_from_user::<sigaction_t>(sig_action_addr)?;
|
||||
let sig_action = SigAction::try_from(sig_action_c).unwrap();
|
||||
debug!("sig action = {:?}", sig_action);
|
||||
trace!("sig action = {:?}", sig_action);
|
||||
sig_dispositions.set(sig_num, sig_action);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,11 @@
|
||||
use crate::{
|
||||
log_syscall_entry,
|
||||
prelude::*,
|
||||
process::{process_group::ProcessGroup, process_table, Pgid, Pid},
|
||||
process::{
|
||||
process_group::ProcessGroup,
|
||||
process_table::{self, pid_to_process},
|
||||
Pgid, Pid,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{SyscallReturn, SYS_SETPGID};
|
||||
@ -15,30 +19,35 @@ pub fn sys_setpgid(pid: Pid, pgid: Pgid) -> Result<SyscallReturn> {
|
||||
let pgid = if pgid == 0 { pid } else { pgid };
|
||||
debug!("pid = {}, pgid = {}", pid, pgid);
|
||||
|
||||
if current.pid() != pid {
|
||||
if pid != current.pid() && !current.children().lock().contains_key(&pid) {
|
||||
return_errno_with_message!(
|
||||
Errno::EACCES,
|
||||
"cannot set pgid for process other than current"
|
||||
Errno::ESRCH,
|
||||
"cannot set pgid for process other than current or children of current"
|
||||
);
|
||||
}
|
||||
// FIXME: If pid is child process of current and already calls execve, should return error.
|
||||
// How can we determine a child process has called execve?
|
||||
|
||||
// only can move process to an existing group or self
|
||||
if pgid != pid && process_table::pgid_to_process_group(pgid).is_none() {
|
||||
return_errno_with_message!(Errno::EPERM, "process group must exist");
|
||||
}
|
||||
|
||||
let process =
|
||||
pid_to_process(pid).ok_or(Error::with_message(Errno::ESRCH, "process does not exist"))?;
|
||||
|
||||
// if the process already belongs to the process group
|
||||
if current.pgid() == pgid {
|
||||
if process.pgid() == pgid {
|
||||
return Ok(SyscallReturn::Return(0));
|
||||
}
|
||||
|
||||
if let Some(new_process_group) = process_table::pgid_to_process_group(pgid) {
|
||||
new_process_group.add_process(current.clone());
|
||||
current.set_process_group(Arc::downgrade(&new_process_group));
|
||||
if let Some(process_group) = process_table::pgid_to_process_group(pgid) {
|
||||
process_group.add_process(process.clone());
|
||||
process.set_process_group(Arc::downgrade(&process_group));
|
||||
} else {
|
||||
let new_process_group = Arc::new(ProcessGroup::new(current.clone()));
|
||||
new_process_group.add_process(current.clone());
|
||||
current.set_process_group(Arc::downgrade(&new_process_group));
|
||||
let new_process_group = Arc::new(ProcessGroup::new(process.clone()));
|
||||
// new_process_group.add_process(process.clone());
|
||||
process.set_process_group(Arc::downgrade(&new_process_group));
|
||||
process_table::add_process_group(new_process_group);
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ pub fn sys_wait4(wait_pid: u64, exit_status_ptr: u64, wait_options: u32) -> Resu
|
||||
"pid = {}, exit_status_ptr = {}, wait_options: {:?}",
|
||||
wait_pid as i32, exit_status_ptr, wait_options
|
||||
);
|
||||
debug!("wait4 current pid = {}", current!().pid());
|
||||
let process_filter = ProcessFilter::from_id(wait_pid as _);
|
||||
let (return_pid, exit_code) = wait_child_exit(process_filter, wait_options)?;
|
||||
if return_pid != 0 && exit_status_ptr != 0 {
|
||||
|
@ -85,7 +85,7 @@ impl File for Tty {
|
||||
IoctlCmd::TCGETS => {
|
||||
// Get terminal attributes
|
||||
let termios = self.ldisc.get_termios();
|
||||
debug!("get termios = {:?}", termios);
|
||||
trace!("get termios = {:?}", termios);
|
||||
write_val_to_user(arg, &termios)?;
|
||||
Ok(0)
|
||||
}
|
||||
|
@ -242,7 +242,11 @@ impl KernelTermios {
|
||||
}
|
||||
|
||||
pub fn contains_isig(&self) -> bool {
|
||||
self.c_lflags.contains(C_LFLAGS::ISIG)
|
||||
// self.c_lflags.contains(C_LFLAGS::ISIG)
|
||||
|
||||
// FIXME: It seems that ash dose not respect ISIG (not very sure),
|
||||
// so for ash, if tty is set cannonical mode, then ISIG is also open.
|
||||
self.is_canonical_mode()
|
||||
}
|
||||
|
||||
pub fn contain_echo(&self) -> bool {
|
||||
@ -250,7 +254,11 @@ impl KernelTermios {
|
||||
}
|
||||
|
||||
pub fn contains_echo_ctl(&self) -> bool {
|
||||
self.c_lflags.contains(C_LFLAGS::ECHOCTL)
|
||||
// self.c_lflags.contains(C_LFLAGS::ECHOCTL)
|
||||
|
||||
// FIXME: It seems that ash dose not contains ECHOCTL (not very sure),
|
||||
// so for ash, it tty contains ECHO, then ECHOCTL is also open.
|
||||
self.contain_echo()
|
||||
}
|
||||
|
||||
pub fn contains_iexten(&self) -> bool {
|
||||
|
Loading…
x
Reference in New Issue
Block a user