Support interrupting foreground job with Ctrl+C

This commit is contained in:
Jianfeng Jiang 2023-03-30 02:53:39 -04:00 committed by Tate, Hongliang Tian
parent ad76b0329a
commit 32f3f5c300
14 changed files with 86 additions and 63 deletions

View File

@ -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()

View File

@ -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(),

View File

@ -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> {

View File

@ -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,

View File

@ -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))
}

View File

@ -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.

View File

@ -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 _))

View File

@ -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

View File

@ -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();

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 {

View File

@ -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)
}

View File

@ -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 {