mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-09 05:16:47 +00:00
Add real vfork logics
This commit is contained in:
parent
43e43ca133
commit
e4f07b76a0
@ -195,8 +195,18 @@ pub fn clone_child(
|
|||||||
Ok(child_tid)
|
Ok(child_tid)
|
||||||
} else {
|
} else {
|
||||||
let child_process = clone_child_process(ctx, parent_context, clone_args)?;
|
let child_process = clone_child_process(ctx, parent_context, clone_args)?;
|
||||||
|
if clone_args.flags.contains(CloneFlags::CLONE_VFORK) {
|
||||||
|
child_process.status().set_vfork_child(true);
|
||||||
|
}
|
||||||
|
|
||||||
child_process.run();
|
child_process.run();
|
||||||
|
|
||||||
|
if child_process.status().is_vfork_child() {
|
||||||
|
let cond = || (!child_process.status().is_vfork_child()).then_some(());
|
||||||
|
let current = ctx.process;
|
||||||
|
current.children_wait_queue().wait_until(cond);
|
||||||
|
}
|
||||||
|
|
||||||
let child_pid = child_process.pid();
|
let child_pid = child_process.pid();
|
||||||
Ok(child_pid)
|
Ok(child_pid)
|
||||||
}
|
}
|
||||||
@ -435,8 +445,10 @@ fn clone_user_ctx(
|
|||||||
// The return value of child thread is zero
|
// The return value of child thread is zero
|
||||||
child_context.set_syscall_ret(0);
|
child_context.set_syscall_ret(0);
|
||||||
|
|
||||||
if clone_flags.contains(CloneFlags::CLONE_VM) {
|
if clone_flags.contains(CloneFlags::CLONE_VM) && !clone_flags.contains(CloneFlags::CLONE_VFORK)
|
||||||
// if parent and child shares the same address space, a new stack must be specified.
|
{
|
||||||
|
// If parent and child shares the same address space and not in vfork situation,
|
||||||
|
// a new stack must be specified.
|
||||||
debug_assert!(new_sp != 0);
|
debug_assert!(new_sp != 0);
|
||||||
}
|
}
|
||||||
if new_sp != 0 {
|
if new_sp != 0 {
|
||||||
|
@ -14,6 +14,7 @@ use crate::{prelude::*, process::signal::signals::kernel::KernelSignal};
|
|||||||
/// [`do_exit_group`]: crate::process::posix_thread::do_exit_group
|
/// [`do_exit_group`]: crate::process::posix_thread::do_exit_group
|
||||||
pub(super) fn exit_process(thread_local: &ThreadLocal, current_process: &Process) {
|
pub(super) fn exit_process(thread_local: &ThreadLocal, current_process: &Process) {
|
||||||
current_process.status().set_zombie();
|
current_process.status().set_zombie();
|
||||||
|
current_process.status().set_vfork_child(false);
|
||||||
|
|
||||||
// FIXME: This is obviously wrong in a number of ways, since different threads can have
|
// FIXME: This is obviously wrong in a number of ways, since different threads can have
|
||||||
// different file tables, and different processes can share the same file table.
|
// different file tables, and different processes can share the same file table.
|
||||||
@ -25,7 +26,7 @@ pub(super) fn exit_process(thread_local: &ThreadLocal, current_process: &Process
|
|||||||
|
|
||||||
send_child_death_signal(current_process);
|
send_child_death_signal(current_process);
|
||||||
|
|
||||||
current_process.lock_root_vmar().clear();
|
current_process.lock_root_vmar().set_vmar(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends parent-death signals to the children.
|
/// Sends parent-death signals to the children.
|
||||||
|
@ -26,8 +26,10 @@ pub use process::{
|
|||||||
ExitCode, JobControl, Pgid, Pid, Process, ProcessBuilder, ProcessGroup, Session, Sid, Terminal,
|
ExitCode, JobControl, Pgid, Pid, Process, ProcessBuilder, ProcessGroup, Session, Sid, Terminal,
|
||||||
};
|
};
|
||||||
pub use process_filter::ProcessFilter;
|
pub use process_filter::ProcessFilter;
|
||||||
pub use process_vm::{MAX_ARGV_NUMBER, MAX_ARG_LEN, MAX_ENVP_NUMBER, MAX_ENV_LEN};
|
pub use process_vm::{
|
||||||
pub use program_loader::{check_executable_file, load_program_to_vm};
|
renew_vm_and_map, MAX_ARGV_NUMBER, MAX_ARG_LEN, MAX_ENVP_NUMBER, MAX_ENV_LEN,
|
||||||
|
};
|
||||||
|
pub use program_loader::{check_executable_file, ProgramToLoad};
|
||||||
pub use rlimit::ResourceType;
|
pub use rlimit::ResourceType;
|
||||||
pub use term_status::TermStatus;
|
pub use term_status::TermStatus;
|
||||||
pub use wait::{wait_child_exit, WaitOptions};
|
pub use wait::{wait_child_exit, WaitOptions};
|
||||||
|
@ -9,7 +9,7 @@ use crate::{
|
|||||||
thread_info::ThreadFsInfo,
|
thread_info::ThreadFsInfo,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
process::{process_vm::ProcessVm, program_loader::load_program_to_vm, Credentials, Process},
|
process::{process_vm::ProcessVm, program_loader::ProgramToLoad, Credentials, Process},
|
||||||
thread::{AsThread, Thread, Tid},
|
thread::{AsThread, Thread, Tid},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -48,7 +48,10 @@ pub fn create_posix_task_from_executable(
|
|||||||
let fs_resolver = fs.resolver().read();
|
let fs_resolver = fs.resolver().read();
|
||||||
let fs_path = FsPath::new(AT_FDCWD, executable_path)?;
|
let fs_path = FsPath::new(AT_FDCWD, executable_path)?;
|
||||||
let elf_file = fs.resolver().read().lookup(&fs_path)?;
|
let elf_file = fs.resolver().read().lookup(&fs_path)?;
|
||||||
load_program_to_vm(process_vm, elf_file, argv, envp, &fs_resolver, 1)?
|
let program_to_load =
|
||||||
|
ProgramToLoad::build_from_file(elf_file, &fs_resolver, argv, envp, 1)?;
|
||||||
|
process_vm.clear_and_map();
|
||||||
|
program_to_load.load_to_vm(process_vm, &fs_resolver)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut user_ctx = UserContext::default();
|
let mut user_ctx = UserContext::default();
|
||||||
|
@ -14,7 +14,7 @@ mod init_stack;
|
|||||||
|
|
||||||
use aster_rights::Full;
|
use aster_rights::Full;
|
||||||
pub use heap::Heap;
|
pub use heap::Heap;
|
||||||
use ostd::sync::MutexGuard;
|
use ostd::{sync::MutexGuard, task::disable_preempt};
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
heap::USER_HEAP_SIZE_LIMIT,
|
heap::USER_HEAP_SIZE_LIMIT,
|
||||||
@ -83,9 +83,12 @@ impl ProcessVmarGuard<'_> {
|
|||||||
self.inner.as_ref().unwrap()
|
self.inner.as_ref().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears the VMAR of the binding process.
|
/// Sets a new VMAR for the binding process.
|
||||||
pub(super) fn clear(&mut self) {
|
///
|
||||||
*self.inner = None;
|
/// If the `new_vmar` is `None`, this method will remove the
|
||||||
|
/// current VMAR.
|
||||||
|
pub(super) fn set_vmar(&mut self, new_vmar: Option<Vmar<Full>>) {
|
||||||
|
*self.inner = new_vmar;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,10 +163,25 @@ impl ProcessVm {
|
|||||||
&self.heap
|
&self.heap
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears existing mappings and then maps stack and heap vmo.
|
/// Clears existing mappings and then maps the heap VMO to the current VMAR.
|
||||||
pub(super) fn clear_and_map(&self) {
|
pub fn clear_and_map(&self) {
|
||||||
let root_vmar = self.lock_root_vmar();
|
let root_vmar = self.lock_root_vmar();
|
||||||
root_vmar.get().clear().unwrap();
|
root_vmar.get().clear().unwrap();
|
||||||
self.heap.alloc_and_map_vm(&root_vmar.get()).unwrap();
|
self.heap.alloc_and_map_vm(&root_vmar.get()).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Renews the [`ProcessVm`] of the current process and then maps the heap VMO to the new VMAR.
|
||||||
|
pub fn renew_vm_and_map(ctx: &Context) {
|
||||||
|
let process_vm = ctx.process.vm();
|
||||||
|
let mut root_vmar = process_vm.lock_root_vmar();
|
||||||
|
|
||||||
|
let new_vmar = Vmar::<Full>::new_root();
|
||||||
|
let guard = disable_preempt();
|
||||||
|
*ctx.thread_local.root_vmar().borrow_mut() = Some(new_vmar.dup().unwrap());
|
||||||
|
new_vmar.vm_space().activate();
|
||||||
|
root_vmar.set_vmar(Some(new_vmar));
|
||||||
|
drop(guard);
|
||||||
|
|
||||||
|
process_vm.heap.alloc_and_map_vm(root_vmar.get()).unwrap();
|
||||||
|
}
|
||||||
|
@ -17,56 +17,89 @@ use crate::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Load an executable to root vmar, including loading programme image, preparing heap and stack,
|
/// Represents an executable file that is ready to be loaded into memory and executed.
|
||||||
/// initializing argv, envp and aux tables.
|
///
|
||||||
/// About recursion_limit: recursion limit is used to limit th recursion depth of shebang executables.
|
/// This struct encapsulates the ELF file to be executed along with its header data,
|
||||||
/// If the interpreter(the program behind #!) of shebang executable is also a shebang,
|
/// the `argv` and the `envp` which is required for the program execution.
|
||||||
/// then it will trigger recursion. We will try to setup root vmar for the interpreter.
|
pub struct ProgramToLoad {
|
||||||
/// I guess for most cases, setting the recursion_limit as 1 should be enough.
|
|
||||||
/// because the interpreter is usually an elf binary(e.g., /bin/bash)
|
|
||||||
pub fn load_program_to_vm(
|
|
||||||
process_vm: &ProcessVm,
|
|
||||||
elf_file: Dentry,
|
elf_file: Dentry,
|
||||||
|
file_header: Box<[u8; PAGE_SIZE]>,
|
||||||
argv: Vec<CString>,
|
argv: Vec<CString>,
|
||||||
envp: Vec<CString>,
|
envp: Vec<CString>,
|
||||||
fs_resolver: &FsResolver,
|
}
|
||||||
recursion_limit: usize,
|
|
||||||
) -> Result<(String, ElfLoadInfo)> {
|
impl ProgramToLoad {
|
||||||
let abs_path = elf_file.abs_path();
|
/// Constructs a new `ProgramToLoad` from a file, handling shebang interpretation if needed.
|
||||||
let inode = elf_file.inode();
|
///
|
||||||
let file_header = {
|
/// About `recursion_limit`: recursion limit is used to limit th recursion depth of shebang executables.
|
||||||
// read the first page of file header
|
/// If the interpreter(the program behind #!) of shebang executable is also a shebang,
|
||||||
let mut file_header_buffer = Box::new([0u8; PAGE_SIZE]);
|
/// then it will trigger recursion. We will try to setup root vmar for the interpreter.
|
||||||
inode.read_bytes_at(0, &mut *file_header_buffer)?;
|
/// I guess for most cases, setting the `recursion_limit` as 1 should be enough.
|
||||||
file_header_buffer
|
/// because the interpreter is usually an elf binary(e.g., /bin/bash)
|
||||||
};
|
pub fn build_from_file(
|
||||||
if let Some(mut new_argv) = parse_shebang_line(&*file_header)? {
|
elf_file: Dentry,
|
||||||
if recursion_limit == 0 {
|
fs_resolver: &FsResolver,
|
||||||
return_errno_with_message!(Errno::ELOOP, "the recursieve limit is reached");
|
argv: Vec<CString>,
|
||||||
}
|
envp: Vec<CString>,
|
||||||
new_argv.extend_from_slice(&argv);
|
recursion_limit: usize,
|
||||||
let interpreter = {
|
) -> Result<Self> {
|
||||||
let filename = new_argv[0].to_str()?.to_string();
|
let inode = elf_file.inode();
|
||||||
let fs_path = FsPath::new(AT_FDCWD, &filename)?;
|
let file_header = {
|
||||||
fs_resolver.lookup(&fs_path)?
|
// read the first page of file header
|
||||||
|
let mut file_header_buffer = Box::new([0u8; PAGE_SIZE]);
|
||||||
|
inode.read_bytes_at(0, &mut *file_header_buffer)?;
|
||||||
|
file_header_buffer
|
||||||
};
|
};
|
||||||
check_executable_file(&interpreter)?;
|
if let Some(mut new_argv) = parse_shebang_line(&*file_header)? {
|
||||||
return load_program_to_vm(
|
if recursion_limit == 0 {
|
||||||
process_vm,
|
return_errno_with_message!(Errno::ELOOP, "the recursieve limit is reached");
|
||||||
interpreter,
|
}
|
||||||
new_argv,
|
new_argv.extend_from_slice(&argv);
|
||||||
|
let interpreter = {
|
||||||
|
let filename = new_argv[0].to_str()?.to_string();
|
||||||
|
let fs_path = FsPath::new(AT_FDCWD, &filename)?;
|
||||||
|
fs_resolver.lookup(&fs_path)?
|
||||||
|
};
|
||||||
|
check_executable_file(&interpreter)?;
|
||||||
|
return Self::build_from_file(
|
||||||
|
interpreter,
|
||||||
|
fs_resolver,
|
||||||
|
new_argv,
|
||||||
|
envp,
|
||||||
|
recursion_limit - 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
elf_file,
|
||||||
|
file_header,
|
||||||
|
argv,
|
||||||
envp,
|
envp,
|
||||||
fs_resolver,
|
})
|
||||||
recursion_limit - 1,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
process_vm.clear_and_map();
|
/// Loads the executable into the specified virtual memory space.
|
||||||
|
///
|
||||||
|
/// Returns a tuple containing:
|
||||||
|
/// 1. The absolute path of the loaded executable.
|
||||||
|
/// 2. Information about the ELF loading process.
|
||||||
|
pub fn load_to_vm(
|
||||||
|
self,
|
||||||
|
process_vm: &ProcessVm,
|
||||||
|
fs_resolver: &FsResolver,
|
||||||
|
) -> Result<(String, ElfLoadInfo)> {
|
||||||
|
let abs_path = self.elf_file.abs_path();
|
||||||
|
let elf_load_info = load_elf_to_vm(
|
||||||
|
process_vm,
|
||||||
|
&*self.file_header,
|
||||||
|
self.elf_file,
|
||||||
|
fs_resolver,
|
||||||
|
self.argv,
|
||||||
|
self.envp,
|
||||||
|
)?;
|
||||||
|
|
||||||
let elf_load_info =
|
Ok((abs_path, elf_load_info))
|
||||||
load_elf_to_vm(process_vm, &*file_header, elf_file, fs_resolver, argv, envp)?;
|
}
|
||||||
|
|
||||||
Ok((abs_path, elf_load_info))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_executable_file(dentry: &Dentry) -> Result<()> {
|
pub fn check_executable_file(dentry: &Dentry) -> Result<()> {
|
||||||
|
@ -10,10 +10,13 @@ use super::ExitCode;
|
|||||||
///
|
///
|
||||||
/// This maintains:
|
/// This maintains:
|
||||||
/// 1. Whether the process is a zombie (i.e., all its threads have exited);
|
/// 1. Whether the process is a zombie (i.e., all its threads have exited);
|
||||||
/// 2. The exit code of the process.
|
/// 2. Whether the process is the vfork child, which shares the user-space virtual memory
|
||||||
|
/// with its parent process;
|
||||||
|
/// 3. The exit code of the process.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ProcessStatus {
|
pub struct ProcessStatus {
|
||||||
is_zombie: AtomicBool,
|
is_zombie: AtomicBool,
|
||||||
|
is_vfork_child: AtomicBool,
|
||||||
exit_code: AtomicU32,
|
exit_code: AtomicU32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,6 +24,7 @@ impl Default for ProcessStatus {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
is_zombie: AtomicBool::new(false),
|
is_zombie: AtomicBool::new(false),
|
||||||
|
is_vfork_child: AtomicBool::new(false),
|
||||||
exit_code: AtomicU32::new(0),
|
exit_code: AtomicU32::new(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,6 +48,18 @@ impl ProcessStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ProcessStatus {
|
||||||
|
/// Returns whether the process is the vfork child.
|
||||||
|
pub fn is_vfork_child(&self) -> bool {
|
||||||
|
self.is_vfork_child.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets whether the process is the vfork child.
|
||||||
|
pub fn set_vfork_child(&self, is_vfork_child: bool) {
|
||||||
|
self.is_vfork_child.store(is_vfork_child, Ordering::Release);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ProcessStatus {
|
impl ProcessStatus {
|
||||||
/// Returns the exit code.
|
/// Returns the exit code.
|
||||||
pub fn exit_code(&self) -> ExitCode {
|
pub fn exit_code(&self) -> ExitCode {
|
||||||
|
@ -15,8 +15,8 @@ use crate::{
|
|||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
process::{
|
process::{
|
||||||
check_executable_file, load_program_to_vm, posix_thread::ThreadName, Credentials, Process,
|
check_executable_file, posix_thread::ThreadName, renew_vm_and_map, Credentials, Process,
|
||||||
MAX_ARGV_NUMBER, MAX_ARG_LEN, MAX_ENVP_NUMBER, MAX_ENV_LEN,
|
ProgramToLoad, MAX_ARGV_NUMBER, MAX_ARG_LEN, MAX_ENVP_NUMBER, MAX_ENV_LEN,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -118,11 +118,27 @@ fn do_execve(
|
|||||||
drop(closed_files);
|
drop(closed_files);
|
||||||
|
|
||||||
debug!("load program to root vmar");
|
debug!("load program to root vmar");
|
||||||
let (new_executable_path, elf_load_info) = {
|
let fs_resolver = &*posix_thread.fs().resolver().read();
|
||||||
let fs_resolver = &*posix_thread.fs().resolver().read();
|
let program_to_load =
|
||||||
let process_vm = process.vm();
|
ProgramToLoad::build_from_file(elf_file.clone(), fs_resolver, argv, envp, 1)?;
|
||||||
load_program_to_vm(process_vm, elf_file.clone(), argv, envp, fs_resolver, 1)?
|
|
||||||
};
|
let process_vm = process.vm();
|
||||||
|
if process.status().is_vfork_child() {
|
||||||
|
renew_vm_and_map(ctx);
|
||||||
|
|
||||||
|
// Resumes the parent process.
|
||||||
|
process.status().set_vfork_child(false);
|
||||||
|
let parent = process.parent().lock().process().upgrade().unwrap();
|
||||||
|
parent.children_wait_queue().wake_all();
|
||||||
|
} else {
|
||||||
|
// FIXME: Currently, the efficiency of replacing the VMAR is lower than that
|
||||||
|
// of directly clearing the VMAR. Therefore, if not in vfork case we will only
|
||||||
|
// clear the VMAR.
|
||||||
|
process_vm.clear_and_map();
|
||||||
|
}
|
||||||
|
|
||||||
|
let (new_executable_path, elf_load_info) =
|
||||||
|
program_to_load.load_to_vm(process_vm, fs_resolver)?;
|
||||||
|
|
||||||
// After the program has been successfully loaded, the virtual memory of the current process
|
// After the program has been successfully loaded, the virtual memory of the current process
|
||||||
// is initialized. Hence, it is necessary to clear the previously recorded robust list.
|
// is initialized. Hence, it is necessary to clear the previously recorded robust list.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user