Use builder pattern to refactor the process module

This commit is contained in:
Jianfeng Jiang
2023-09-06 17:32:25 +08:00
committed by Tate, Hongliang Tian
parent 9ca64c281e
commit f540345bfd
42 changed files with 878 additions and 643 deletions

View File

@ -194,8 +194,8 @@ impl FileLike for PtyMaster {
// TODO: reimplement when adding session.
let foreground = {
let current = current!();
let process_group = current.process_group().lock();
process_group.clone()
let process_group = current.process_group().unwrap();
Arc::downgrade(&process_group)
};
self.output.set_fg(foreground);
Ok(0)

View File

@ -1,6 +1,6 @@
use crate::fs::utils::{IoEvents, Pollee, Poller};
use crate::process::process_group::ProcessGroup;
use crate::process::signal::constants::{SIGINT, SIGQUIT};
use crate::process::ProcessGroup;
use crate::{
prelude::*,
process::{signal::signals::kernel::KernelSignal, Pgid},

View File

@ -5,8 +5,7 @@ use self::line_discipline::LineDiscipline;
use super::*;
use crate::fs::utils::{IoEvents, IoctlCmd, Poller};
use crate::prelude::*;
use crate::process::process_group::ProcessGroup;
use crate::process::process_table;
use crate::process::{process_table, ProcessGroup};
use crate::util::{read_val_from_user, write_val_to_user};
pub mod driver;
@ -101,7 +100,10 @@ impl Device for Tty {
IoctlCmd::TIOCSPGRP => {
// Set the process group id of fg progress group
let pgid = read_val_from_user::<i32>(arg)?;
match process_table::pgid_to_process_group(pgid) {
if pgid < 0 {
return_errno_with_message!(Errno::EINVAL, "invalid pgid");
}
match process_table::pgid_to_process_group(pgid as u32) {
None => self.ldisc.set_fg(Weak::new()),
Some(process_group) => self.ldisc.set_fg(Arc::downgrade(&process_group)),
}

View File

@ -15,7 +15,7 @@ impl CommFileOps {
impl FileOps for CommFileOps {
fn data(&self) -> Result<Vec<u8>> {
let mut comm_output = {
let exe_path = self.0.executable_path().read();
let exe_path = self.0.executable_path();
let last_component = exe_path.rsplit('/').next().unwrap_or(&exe_path);
let mut comm = last_component.as_bytes().to_vec();
comm.push(b'\0');

View File

@ -14,6 +14,6 @@ impl ExeSymOps {
impl SymOps for ExeSymOps {
fn read_link(&self) -> Result<String> {
Ok(self.0.executable_path().read().clone())
Ok(self.0.executable_path())
}
}

View File

@ -85,7 +85,7 @@ fn init_thread() {
.expect("Run init process failed.");
// Wait till initproc become zombie.
while !initproc.status().lock().is_zombie() {
while !initproc.is_zombie() {
// We don't have preemptive scheduler now.
// The long running init thread should yield its own execution to allow other tasks to go on.
Thread::yield_now();

View File

@ -28,7 +28,7 @@ pub(crate) use pod::Pod;
#[macro_export]
macro_rules! current {
() => {
$crate::process::Process::current()
$crate::process::current()
};
}

View File

@ -1,8 +1,4 @@
use jinux_frame::{
cpu::UserContext,
user::UserSpace,
vm::{VmIo, VmSpace},
};
use jinux_frame::{cpu::UserContext, user::UserSpace, vm::VmIo};
use jinux_rights::Full;
@ -12,9 +8,7 @@ use crate::{
fs::{fs_resolver::FsResolver, utils::FileCreationMask},
prelude::*,
process::{
posix_thread::{
builder::PosixThreadBuilder, name::ThreadName, posix_thread_ext::PosixThreadExt,
},
posix_thread::{PosixThreadBuilder, PosixThreadExt, ThreadName},
process_table,
},
thread::{allocate_tid, thread_table, Thread, Tid},
@ -24,7 +18,7 @@ use crate::{
use super::{
posix_thread::PosixThread, process_vm::ProcessVm, signal::sig_disposition::SigDispositions,
Process,
Process, ProcessBuilder,
};
bitflags! {
@ -164,6 +158,8 @@ fn clone_child_thread(parent_context: UserContext, clone_args: CloneArgs) -> Res
debug_assert!(clone_flags.contains(CloneFlags::CLONE_FILES));
debug_assert!(clone_flags.contains(CloneFlags::CLONE_SIGHAND));
let child_root_vmar = current.root_vmar();
let child_user_space = {
let child_vm_space = child_root_vmar.vm_space().clone();
let child_cpu_context = clone_cpu_context(
parent_context,
@ -171,20 +167,30 @@ fn clone_child_thread(parent_context: UserContext, clone_args: CloneArgs) -> Res
clone_args.tls,
clone_flags,
);
let child_user_space = Arc::new(UserSpace::new(child_vm_space, child_cpu_context));
Arc::new(UserSpace::new(child_vm_space, child_cpu_context))
};
clone_sysvsem(clone_flags)?;
let child_tid = allocate_tid();
// inherit sigmask from current thread
// Inherit sigmask from current thread
let sig_mask = {
let current_thread = current_thread!();
let current_posix_thread = current_thread.as_posix_thread().unwrap();
let sig_mask = *current_posix_thread.sig_mask().lock();
let sigmask = current_posix_thread.sig_mask().lock();
*sigmask
};
let child_tid = allocate_tid();
let child_thread = {
let is_main_thread = child_tid == current.pid();
let thread_builder = PosixThreadBuilder::new(child_tid, child_user_space)
.process(Arc::downgrade(&current))
.sig_mask(sig_mask)
.is_main_thread(is_main_thread);
let child_thread = thread_builder.build();
current.threads.lock().push(child_thread.clone());
thread_builder.build()
};
current.threads().lock().push(child_thread.clone());
let child_posix_thread = child_thread.as_posix_thread().unwrap();
clone_parent_settid(child_tid, clone_args.parent_tidptr, clone_flags)?;
clone_child_cleartid(child_posix_thread, clone_args.child_tidptr, clone_flags)?;
@ -203,73 +209,77 @@ fn clone_child_process(parent_context: UserContext, clone_args: CloneArgs) -> Re
let clone_flags = clone_args.clone_flags;
// clone vm
let child_root_vmar = {
let parent_root_vmar = current.root_vmar();
clone_vm(parent_root_vmar, clone_flags)?
};
let child_process_vm = {
let child_user_heap = current.user_heap().clone();
ProcessVm::new(child_user_heap, child_root_vmar.dup()?)
let parent_process_vm = current.vm();
clone_vm(parent_process_vm, clone_flags)?
};
// clone user space
let child_user_space = {
let child_cpu_context = clone_cpu_context(
parent_context,
clone_args.new_sp,
clone_args.tls,
clone_flags,
);
let child_vm_space = child_root_vmar.vm_space().clone();
let child_user_space = Arc::new(UserSpace::new(child_vm_space, child_cpu_context));
let child_vm_space = {
let child_root_vmar = child_process_vm.root_vmar();
child_root_vmar.vm_space().clone()
};
Arc::new(UserSpace::new(child_vm_space, child_cpu_context))
};
// clone file table
let child_file_table = clone_files(current.file_table(), clone_flags);
// clone fs
let child_fs = clone_fs(current.fs(), clone_flags);
// clone umask
let parent_umask = current.umask.read().get();
let child_umask = Arc::new(RwLock::new(FileCreationMask::new(parent_umask)));
let child_umask = {
let parent_umask = current.umask().read().get();
Arc::new(RwLock::new(FileCreationMask::new(parent_umask)))
};
// clone sig dispositions
let child_sig_dispositions = clone_sighand(current.sig_dispositions(), clone_flags);
// clone system V semaphore
clone_sysvsem(clone_flags)?;
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
let child_sig_mask = {
let current_thread = current_thread!();
let posix_thread = current_thread.as_posix_thread().unwrap();
let child_sig_mask = *posix_thread.sig_mask().lock();
let sigmask = posix_thread.sig_mask().lock();
*sigmask
};
let child_tid = allocate_tid();
let mut child_thread_builder = PosixThreadBuilder::new(child_tid, child_user_space)
.thread_name(Some(child_thread_name))
.sig_mask(child_sig_mask);
let child = Arc::new_cyclic(|child_process_ref| {
let weak_child_process = child_process_ref.clone();
let child_pid = child_tid;
child_thread_builder = child_thread_builder.process(weak_child_process);
let child_thread = child_thread_builder.build();
Process::new(
child_pid,
parent,
vec![child_thread],
child_elf_path,
child_process_vm,
Weak::new(),
child_file_table,
child_fs,
child_umask,
child_sig_dispositions,
)
});
// Inherit parent's process group
let parent_process_group = current.process_group().lock().upgrade().unwrap();
parent_process_group.add_process(child.clone());
child.set_process_group(Arc::downgrade(&parent_process_group));
let child = {
let child_elf_path = current.executable_path();
let child_thread_builder = {
let child_thread_name = ThreadName::new_from_executable_path(&child_elf_path)?;
PosixThreadBuilder::new(child_tid, child_user_space)
.thread_name(Some(child_thread_name))
.sig_mask(child_sig_mask)
};
let mut process_builder =
ProcessBuilder::new(child_tid, &child_elf_path, Arc::downgrade(&current));
process_builder
.main_thread_builder(child_thread_builder)
.process_vm(child_process_vm)
.file_table(child_file_table)
.fs(child_fs)
.umask(child_umask)
.sig_dispositions(child_sig_dispositions)
.process_group(current.process_group().unwrap());
process_builder.build()?
};
current!().add_child(child.clone());
process_table::add_process(child.clone());
@ -278,8 +288,10 @@ fn clone_child_process(parent_context: UserContext, clone_args: CloneArgs) -> Re
let child_posix_thread = child_thread.as_posix_thread().unwrap();
clone_parent_settid(child_tid, clone_args.parent_tidptr, clone_flags)?;
clone_child_cleartid(child_posix_thread, clone_args.child_tidptr, clone_flags)?;
let child_root_vmar = child.root_vmar();
clone_child_settid(
&child_root_vmar,
child_root_vmar,
child_tid,
clone_args.child_tidptr,
clone_flags,
@ -322,13 +334,15 @@ fn clone_parent_settid(
Ok(())
}
/// clone child vmar. If CLONE_VM is set, both threads share the same root vmar.
/// Clone child process vm. If CLONE_VM is set, both threads share the same root vmar.
/// Otherwise, fork a new copy-on-write vmar.
fn clone_vm(parent_root_vmar: &Vmar<Full>, clone_flags: CloneFlags) -> Result<Vmar<Full>> {
fn clone_vm(parent_process_vm: &ProcessVm, clone_flags: CloneFlags) -> Result<ProcessVm> {
if clone_flags.contains(CloneFlags::CLONE_VM) {
Ok(parent_root_vmar.dup()?)
Ok(parent_process_vm.clone())
} else {
Ok(parent_root_vmar.fork_vmar()?)
let root_vmar = parent_process_vm.root_vmar().fork_vmar()?;
let user_heap = parent_process_vm.user_heap().clone();
Ok(ProcessVm::new(user_heap, root_vmar))
}
}
@ -400,19 +414,3 @@ fn clone_sysvsem(clone_flags: CloneFlags) -> Result<()> {
}
Ok(())
}
/// debug use. check clone vm space corrent.
fn debug_check_clone_vm_space(parent_vm_space: &VmSpace, child_vm_space: &VmSpace) {
let mut buffer1 = vec![0u8; 0x78];
let mut buffer2 = vec![0u8; 0x78];
parent_vm_space
.read_bytes(0x401000, &mut buffer1)
.expect("read buffer1 failed");
child_vm_space
.read_bytes(0x401000, &mut buffer2)
.expect("read buffer1 failed");
for len in 0..buffer1.len() {
assert_eq!(buffer1[len], buffer2[len]);
}
debug!("check clone vm space succeed.");
}

View File

@ -0,0 +1,64 @@
use crate::process::posix_thread::PosixThreadExt;
use crate::process::signal::signals::kernel::KernelSignal;
use crate::{prelude::*, process::signal::constants::SIGCHLD};
use super::{process_table, Pid, Process, TermStatus};
pub fn do_exit_group(term_status: TermStatus) {
let current = current!();
debug!("exit group was called");
if current.is_zombie() {
return;
}
current.set_zombie(term_status);
// Exit all threads
let threads = current.threads().lock().clone();
for thread in threads {
if thread.is_exited() {
continue;
}
thread.exit();
if let Some(posix_thread) = thread.as_posix_thread() {
let tid = thread.tid();
if let Err(e) = posix_thread.exit(tid, term_status) {
debug!("Ignore error when call exit: {:?}", e);
}
}
}
// Close all files then exit the process
let files = current.file_table().lock().close_all();
for file in files {
let _ = file.clean_for_close();
}
// Move children to the init process
if !is_init_process(&current) {
if let Some(init_process) = get_init_process() {
for (_, child_process) in current.children().lock().extract_if(|_, _| true) {
child_process.set_parent(Arc::downgrade(&init_process));
init_process.add_child(child_process);
}
}
}
if let Some(parent) = current.parent() {
// Notify parent
let signal = Box::new(KernelSignal::new(SIGCHLD));
parent.enqueue_signal(signal);
parent.waiting_children().wake_all();
}
}
const INIT_PROCESS_PID: Pid = 1;
/// Get the init process
fn get_init_process() -> Option<Arc<Process>> {
process_table::pid_to_process(INIT_PROCESS_PID)
}
fn is_init_process(process: &Process) -> bool {
process.pid() == INIT_PROCESS_PID
}

View File

@ -1,402 +1,27 @@
use self::posix_thread::posix_thread_ext::PosixThreadExt;
use self::process_group::ProcessGroup;
use self::process_vm::user_heap::UserHeap;
use self::process_vm::ProcessVm;
use self::rlimit::ResourceLimits;
use self::signal::constants::SIGCHLD;
use self::signal::sig_disposition::SigDispositions;
use self::signal::sig_queues::SigQueues;
use self::signal::signals::kernel::KernelSignal;
use self::signal::signals::Signal;
use self::status::ProcessStatus;
use crate::device::tty::get_n_tty;
use crate::fs::file_table::FileTable;
use crate::fs::fs_resolver::FsResolver;
use crate::fs::utils::FileCreationMask;
use crate::prelude::*;
use crate::thread::{allocate_tid, thread_table, Thread};
use crate::vm::vmar::Vmar;
use jinux_frame::sync::WaitQueue;
use jinux_rights::Full;
pub mod clone;
mod clone;
mod exit;
pub mod fifo_scheduler;
pub mod posix_thread;
pub mod process_filter;
pub mod process_group;
#[allow(clippy::module_inception)]
mod process;
mod process_filter;
mod process_group;
pub mod process_table;
pub mod process_vm;
pub mod program_loader;
pub mod rlimit;
mod process_vm;
mod program_loader;
mod rlimit;
pub mod signal;
pub mod status;
mod status;
mod term_status;
pub mod wait;
mod wait;
pub use clone::{clone_child, CloneArgs, CloneFlags};
pub use exit::do_exit_group;
pub use process::ProcessBuilder;
pub use process::{current, ExitCode, Pgid, Pid, Process};
pub use process_filter::ProcessFilter;
pub use process_group::ProcessGroup;
pub use program_loader::{check_executable_file, load_program_to_vm};
pub use rlimit::ResourceType;
pub use term_status::TermStatus;
pub type Pid = i32;
pub type Pgid = i32;
pub type ExitCode = i32;
const INIT_PROCESS_PID: Pid = 1;
/// Process stands for a set of threads that shares the same userspace.
pub struct Process {
// Immutable Part
pid: Pid,
process_vm: ProcessVm,
/// wait for child status changed
waiting_children: WaitQueue,
// Mutable Part
/// The executable path.
executable_path: RwLock<String>,
/// The threads
threads: Mutex<Vec<Arc<Thread>>>,
/// Process status
status: Mutex<ProcessStatus>,
/// Parent process
parent: Mutex<Weak<Process>>,
/// Children processes
children: Mutex<BTreeMap<Pid, Arc<Process>>>,
/// Process group
process_group: Mutex<Weak<ProcessGroup>>,
/// File table
file_table: Arc<Mutex<FileTable>>,
/// FsResolver
fs: Arc<RwLock<FsResolver>>,
/// umask
umask: Arc<RwLock<FileCreationMask>>,
/// resource limits
resource_limits: Mutex<ResourceLimits>,
// Signal
/// sig dispositions
sig_dispositions: Arc<Mutex<SigDispositions>>,
/// Process-level signal queues
sig_queues: Mutex<SigQueues>,
}
impl Process {
/// returns the current process
pub fn current() -> Arc<Process> {
let current_thread = Thread::current();
if let Some(posix_thread) = current_thread.as_posix_thread() {
posix_thread.process()
} else {
panic!("[Internal error]The current thread does not belong to a process");
}
}
/// create a new process(not schedule it)
#[allow(clippy::too_many_arguments)]
pub fn new(
pid: Pid,
parent: Weak<Process>,
threads: Vec<Arc<Thread>>,
executable_path: String,
process_vm: ProcessVm,
process_group: Weak<ProcessGroup>,
file_table: Arc<Mutex<FileTable>>,
fs: Arc<RwLock<FsResolver>>,
umask: Arc<RwLock<FileCreationMask>>,
sig_dispositions: Arc<Mutex<SigDispositions>>,
) -> Self {
let children = BTreeMap::new();
let waiting_children = WaitQueue::new();
let resource_limits = ResourceLimits::default();
Self {
pid,
threads: Mutex::new(threads),
executable_path: RwLock::new(executable_path),
process_vm,
waiting_children,
status: Mutex::new(ProcessStatus::Runnable),
parent: Mutex::new(parent),
children: Mutex::new(children),
process_group: Mutex::new(process_group),
file_table,
fs,
umask,
sig_dispositions,
sig_queues: Mutex::new(SigQueues::new()),
resource_limits: Mutex::new(resource_limits),
}
}
pub fn waiting_children(&self) -> &WaitQueue {
&self.waiting_children
}
/// init a user process and run the process
pub fn spawn_user_process(
executable_path: &str,
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 process_group = Weak::clone(&process.process_group.lock());
// FIXME: tty should be a parameter?
let tty = get_n_tty();
tty.set_fg(process_group);
process.run();
Ok(process)
}
fn create_user_process(
executable_path: &str,
argv: Vec<CString>,
envp: Vec<CString>,
) -> Result<Arc<Self>> {
let fs = FsResolver::new();
let umask = FileCreationMask::default();
let pid = allocate_tid();
let parent = Weak::new();
let process_group = Weak::new();
let process_vm = ProcessVm::alloc()?;
let file_table = FileTable::new_with_stdio();
let sig_dispositions = SigDispositions::new();
let user_process = Arc::new(Process::new(
pid,
parent,
vec![],
executable_path.to_string(),
process_vm,
process_group,
Arc::new(Mutex::new(file_table)),
Arc::new(RwLock::new(fs)),
Arc::new(RwLock::new(umask)),
Arc::new(Mutex::new(sig_dispositions)),
));
let thread = Thread::new_posix_thread_from_executable(
pid,
&user_process.process_vm,
&user_process.fs().read(),
executable_path,
Arc::downgrade(&user_process),
argv,
envp,
)?;
user_process.threads().lock().push(thread);
// Set process group
user_process.create_and_set_process_group();
process_table::add_process(user_process.clone());
Ok(user_process)
}
/// returns the pid of the process
pub fn pid(&self) -> Pid {
self.pid
}
/// returns the process group id of the process
pub fn pgid(&self) -> Pgid {
if let Some(process_group) = self.process_group.lock().upgrade() {
process_group.pgid()
} else {
0
}
}
pub fn process_group(&self) -> &Mutex<Weak<ProcessGroup>> {
&self.process_group
}
/// add a child process
pub fn add_child(&self, child: Arc<Process>) {
let child_pid = child.pid();
self.children.lock().insert(child_pid, child);
}
pub fn set_parent(&self, parent: Weak<Process>) {
*self.parent.lock() = parent;
}
/// Set process group for current process. If old process group exists,
/// remove current process from old process group.
pub fn set_process_group(&self, process_group: Weak<ProcessGroup>) {
if let Some(old_process_group) = self.process_group.lock().upgrade() {
old_process_group.remove_process(self.pid());
}
*self.process_group.lock() = process_group;
}
pub fn file_table(&self) -> &Arc<Mutex<FileTable>> {
&self.file_table
}
pub fn fs(&self) -> &Arc<RwLock<FsResolver>> {
&self.fs
}
pub fn umask(&self) -> &Arc<RwLock<FileCreationMask>> {
&self.umask
}
/// create a new process group for the process and add it to globle table.
/// Then set the process group for current process.
fn create_and_set_process_group(self: &Arc<Self>) {
let process_group = Arc::new(ProcessGroup::new(self.clone()));
let pgid = process_group.pgid();
self.set_process_group(Arc::downgrade(&process_group));
process_table::add_process_group(process_group);
}
pub fn parent(&self) -> Option<Arc<Process>> {
self.parent.lock().upgrade()
}
/// Exit thread group(the process).
/// Set the status of the process as Zombie and set exit code.
/// Move all children to init process.
/// Wake up the parent wait queue if parent is waiting for self.
pub fn exit_group(&self, term_status: TermStatus) {
debug!("exit group was called");
if self.status.lock().is_zombie() {
return;
}
self.status.lock().set_zombie(term_status);
let threads = self.threads.lock().clone();
for thread in threads {
if thread.is_exited() {
continue;
}
thread.exit();
if let Some(posix_thread) = thread.as_posix_thread() {
let tid = thread.tid();
if let Err(e) = posix_thread.exit(tid, term_status) {
debug!("Ignore error when call exit: {:?}", e);
}
}
}
// close all files then exit the process
let files = self.file_table().lock().close_all();
for file in files {
let _ = file.clean_for_close();
}
// move children to the init process
if !self.is_init_process() {
if let Some(init_process) = get_init_process() {
for (_, child_process) in self.children.lock().extract_if(|_, _| true) {
child_process.set_parent(Arc::downgrade(&init_process));
init_process.add_child(child_process);
}
}
}
if let Some(parent) = self.parent() {
// set parent sig child
let signal = Box::new(KernelSignal::new(SIGCHLD));
parent.sig_queues().lock().enqueue(signal);
// wake up parent waiting children, if any
parent.waiting_children().wake_all();
}
}
/// if the current process is init process
pub fn is_init_process(&self) -> bool {
self.pid == 0
}
/// start to run current process
pub fn run(&self) {
let threads = self.threads.lock();
// when run the process, the process should has only one thread
debug_assert!(threads.len() == 1);
let thread = threads[0].clone();
// should not hold the lock when run thread
drop(threads);
thread.run();
}
pub fn threads(&self) -> &Mutex<Vec<Arc<Thread>>> {
&self.threads
}
/// returns the user_vm
pub fn process_vm(&self) -> &ProcessVm {
&self.process_vm
}
/// returns the root vmar
pub fn root_vmar(&self) -> &Vmar<Full> {
self.process_vm.root_vmar()
}
/// returns the user heap if the process does have, otherwise None
pub fn user_heap(&self) -> &UserHeap {
self.process_vm.user_heap()
}
/// free zombie child with pid, returns the exit code of child process.
/// remove process from process group.
pub fn reap_zombie_child(&self, pid: Pid) -> u32 {
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());
}
process_table::remove_process(child_process.pid());
if let Some(process_group) = child_process.process_group().lock().upgrade() {
process_group.remove_process(child_process.pid);
}
child_process.exit_code().unwrap()
}
pub fn children(&self) -> &Mutex<BTreeMap<Pid, Arc<Process>>> {
&self.children
}
pub fn exit_code(&self) -> Option<u32> {
match &*self.status.lock() {
ProcessStatus::Runnable => None,
ProcessStatus::Zombie(term_status) => Some(term_status.as_u32()),
}
}
/// whether the process has child process
pub fn has_child(&self) -> bool {
self.children.lock().len() != 0
}
pub fn executable_path(&self) -> &RwLock<String> {
&self.executable_path
}
pub fn status(&self) -> &Mutex<ProcessStatus> {
&self.status
}
pub fn resource_limits(&self) -> &Mutex<ResourceLimits> {
&self.resource_limits
}
pub fn sig_dispositions(&self) -> &Arc<Mutex<SigDispositions>> {
&self.sig_dispositions
}
pub fn sig_queues(&self) -> &Mutex<SigQueues> {
&self.sig_queues
}
pub fn enqueue_signal(&self, signal: Box<dyn Signal>) {
if !self.status().lock().is_zombie() {
self.sig_queues.lock().enqueue(signal);
}
}
}
/// Get the init process
pub fn get_init_process() -> Option<Arc<Process>> {
process_table::pid_to_process(INIT_PROCESS_PID)
}
pub use wait::{wait_child_exit, WaitOptions};

View File

@ -1,6 +1,7 @@
use crate::{
prelude::*,
process::{
do_exit_group,
posix_thread::{futex::futex_wake, robust_list::wake_robust_futex},
TermStatus,
},
@ -8,18 +9,21 @@ use crate::{
util::write_val_to_user,
};
use self::{name::ThreadName, robust_list::RobustListHead};
use super::{
signal::{sig_mask::SigMask, sig_queues::SigQueues},
signal::{sig_mask::SigMask, sig_queues::SigQueues, signals::Signal},
Process,
};
pub mod builder;
mod builder;
pub mod futex;
pub mod name;
pub mod posix_thread_ext;
pub mod robust_list;
mod name;
mod posix_thread_ext;
mod robust_list;
pub use builder::PosixThreadBuilder;
pub use name::{ThreadName, MAX_THREAD_NAME_LEN};
pub use posix_thread_ext::PosixThreadExt;
pub use robust_list::RobustListHead;
pub struct PosixThread {
// Immutable part
@ -71,6 +75,18 @@ impl PosixThread {
&self.sig_queues
}
pub fn has_pending_signal(&self) -> bool {
self.sig_queues.lock().is_empty()
}
pub fn enqueue_signal(&self, signal: Box<dyn Signal>) {
self.sig_queues.lock().enqueue(signal);
}
pub fn dequeue_signal(&self, mask: &SigMask) -> Option<Box<dyn Signal>> {
self.sig_queues.lock().dequeue(mask)
}
pub fn sig_context(&self) -> &Mutex<Option<Vaddr>> {
&self.sig_context
}
@ -133,7 +149,7 @@ impl PosixThread {
// exit the robust list: walk the robust list; mark futex words as dead and do futex wake
self.wake_robust_list(tid);
if tid != self.process().pid {
if tid != self.process().pid() {
// If the thread is not main thread. We don't remove main thread.
// Main thread are removed when the whole process is reaped.
thread_table::remove_thread(tid);
@ -144,7 +160,7 @@ impl PosixThread {
debug!("self is main thread or last thread");
debug!("main thread: {}", self.is_main_thread());
debug!("last thread: {}", self.is_last_thread());
current!().exit_group(term_status);
do_exit_group(term_status);
}
debug!("perform futex wake");
futex_wake(Arc::as_ptr(&self.process()) as Vaddr, 1)?;

View File

@ -132,7 +132,7 @@ pub fn wake_robust_futex(futex_addr: Vaddr, tid: Pid) -> Result<()> {
let mut old_val = futex_val;
loop {
// This futex may held by another thread, do nothing
if old_val & FUTEX_TID_MASK != tid as u32 {
if old_val & FUTEX_TID_MASK != tid {
break;
}
let new_val = (old_val & FUTEX_WAITERS) | FUTEX_OWNER_DIED;

View File

@ -0,0 +1,212 @@
use crate::fs::file_table::FileTable;
use crate::fs::fs_resolver::FsResolver;
use crate::fs::utils::FileCreationMask;
use crate::process::posix_thread::PosixThreadBuilder;
use crate::process::process_group::ProcessGroup;
use crate::process::process_table;
use crate::process::process_vm::ProcessVm;
use crate::process::rlimit::ResourceLimits;
use crate::process::{posix_thread::PosixThreadExt, signal::sig_disposition::SigDispositions};
use crate::thread::Thread;
use super::{Pid, Process};
use crate::prelude::*;
pub struct ProcessBuilder<'a> {
// Essential parts
pid: Pid,
executable_path: &'a str,
parent: Weak<Process>,
// Optional parts
main_thread_builder: Option<PosixThreadBuilder>,
argv: Option<Vec<CString>>,
envp: Option<Vec<CString>>,
process_vm: Option<ProcessVm>,
process_group: Option<Arc<ProcessGroup>>,
file_table: Option<Arc<Mutex<FileTable>>>,
fs: Option<Arc<RwLock<FsResolver>>>,
umask: Option<Arc<RwLock<FileCreationMask>>>,
resource_limits: Option<ResourceLimits>,
sig_dispositions: Option<Arc<Mutex<SigDispositions>>>,
}
impl<'a> ProcessBuilder<'a> {
pub fn new(pid: Pid, executable_path: &'a str, parent: Weak<Process>) -> Self {
ProcessBuilder {
pid,
executable_path,
parent,
main_thread_builder: None,
argv: None,
envp: None,
process_vm: None,
process_group: None,
file_table: None,
fs: None,
umask: None,
resource_limits: None,
sig_dispositions: None,
}
}
pub fn main_thread_builder(&mut self, builder: PosixThreadBuilder) -> &mut Self {
self.main_thread_builder = Some(builder);
self
}
pub fn process_vm(&mut self, process_vm: ProcessVm) -> &mut Self {
self.process_vm = Some(process_vm);
self
}
pub fn process_group(&mut self, process_group: Arc<ProcessGroup>) -> &mut Self {
self.process_group = Some(process_group);
self
}
pub fn file_table(&mut self, file_table: Arc<Mutex<FileTable>>) -> &mut Self {
self.file_table = Some(file_table);
self
}
pub fn fs(&mut self, fs: Arc<RwLock<FsResolver>>) -> &mut Self {
self.fs = Some(fs);
self
}
pub fn umask(&mut self, umask: Arc<RwLock<FileCreationMask>>) -> &mut Self {
self.umask = Some(umask);
self
}
pub fn resource_limits(&mut self, resource_limits: ResourceLimits) -> &mut Self {
self.resource_limits = Some(resource_limits);
self
}
pub fn sig_dispositions(&mut self, sig_dispositions: Arc<Mutex<SigDispositions>>) -> &mut Self {
self.sig_dispositions = Some(sig_dispositions);
self
}
pub fn argv(&mut self, argv: Vec<CString>) -> &mut Self {
self.argv = Some(argv);
self
}
pub fn envp(&mut self, envp: Vec<CString>) -> &mut Self {
self.envp = Some(envp);
self
}
fn check_build(&self) -> Result<()> {
if self.main_thread_builder.is_some() {
debug_assert!(self.parent.upgrade().is_some());
debug_assert!(self.argv.is_none());
debug_assert!(self.envp.is_none());
}
if self.main_thread_builder.is_none() {
debug_assert!(self.parent.upgrade().is_none());
debug_assert!(self.argv.is_some());
debug_assert!(self.envp.is_some());
}
Ok(())
}
pub fn build(self) -> Result<Arc<Process>> {
self.check_build()?;
let Self {
pid,
executable_path,
parent,
main_thread_builder,
argv,
envp,
process_vm,
process_group,
file_table,
fs,
umask,
resource_limits,
sig_dispositions,
} = self;
let process_vm = process_vm
.or_else(|| Some(ProcessVm::alloc().unwrap()))
.unwrap();
let process_group_ref = process_group
.as_ref()
.map_or_else(Weak::new, Arc::downgrade);
let file_table = file_table
.or_else(|| Some(Arc::new(Mutex::new(FileTable::new_with_stdio()))))
.unwrap();
let fs = fs
.or_else(|| Some(Arc::new(RwLock::new(FsResolver::new()))))
.unwrap();
let umask = umask
.or_else(|| Some(Arc::new(RwLock::new(FileCreationMask::default()))))
.unwrap();
let resource_limits = resource_limits
.or_else(|| Some(ResourceLimits::default()))
.unwrap();
let sig_dispositions = sig_dispositions
.or_else(|| Some(Arc::new(Mutex::new(SigDispositions::new()))))
.unwrap();
let process = {
let threads = Vec::new();
Arc::new(Process::new(
pid,
parent,
threads,
executable_path.to_string(),
process_vm,
process_group_ref,
file_table,
fs,
umask,
sig_dispositions,
resource_limits,
))
};
let thread = if let Some(thread_builder) = main_thread_builder {
let builder = thread_builder.process(Arc::downgrade(&process));
builder.build()
} else {
Thread::new_posix_thread_from_executable(
pid,
process.vm(),
&process.fs().read(),
executable_path,
Arc::downgrade(&process),
argv.unwrap(),
envp.unwrap(),
)?
};
process.threads().lock().push(thread);
if let Some(process_group) = process_group {
process_group.add_process(process.clone());
} else {
let new_process_group = Arc::new(ProcessGroup::new(process.clone()));
let pgid = new_process_group.pgid();
process.set_process_group(Arc::downgrade(&new_process_group));
process_table::add_process_group(new_process_group);
}
process.set_runnable();
Ok(process)
}
}

View File

@ -0,0 +1,299 @@
mod builder;
use super::posix_thread::PosixThreadExt;
use super::process_group::ProcessGroup;
use super::process_vm::user_heap::UserHeap;
use super::process_vm::ProcessVm;
use super::rlimit::ResourceLimits;
use super::signal::sig_disposition::SigDispositions;
use super::signal::sig_mask::SigMask;
use super::signal::sig_queues::SigQueues;
use super::signal::signals::Signal;
use super::status::ProcessStatus;
use super::{process_table, TermStatus};
use crate::device::tty::get_n_tty;
use crate::fs::file_table::FileTable;
use crate::fs::fs_resolver::FsResolver;
use crate::fs::utils::FileCreationMask;
use crate::prelude::*;
use crate::thread::{allocate_tid, Thread};
use crate::vm::vmar::Vmar;
use jinux_frame::sync::WaitQueue;
use jinux_rights::Full;
pub use builder::ProcessBuilder;
pub type Pid = u32;
pub type Pgid = u32;
pub type ExitCode = i32;
/// Process stands for a set of threads that shares the same userspace.
pub struct Process {
// Immutable Part
pid: Pid,
process_vm: ProcessVm,
/// wait for child status changed
waiting_children: WaitQueue,
// Mutable Part
/// The executable path.
executable_path: RwLock<String>,
/// The threads
threads: Mutex<Vec<Arc<Thread>>>,
/// Process status
status: Mutex<ProcessStatus>,
/// Parent process
parent: Mutex<Weak<Process>>,
/// Children processes
children: Mutex<BTreeMap<Pid, Arc<Process>>>,
/// Process group
process_group: Mutex<Weak<ProcessGroup>>,
/// File table
file_table: Arc<Mutex<FileTable>>,
/// FsResolver
fs: Arc<RwLock<FsResolver>>,
/// umask
umask: Arc<RwLock<FileCreationMask>>,
/// resource limits
resource_limits: Mutex<ResourceLimits>,
// Signal
/// sig dispositions
sig_dispositions: Arc<Mutex<SigDispositions>>,
/// Process-level signal queues
sig_queues: Mutex<SigQueues>,
}
impl Process {
#[allow(clippy::too_many_arguments)]
fn new(
pid: Pid,
parent: Weak<Process>,
threads: Vec<Arc<Thread>>,
executable_path: String,
process_vm: ProcessVm,
process_group: Weak<ProcessGroup>,
file_table: Arc<Mutex<FileTable>>,
fs: Arc<RwLock<FsResolver>>,
umask: Arc<RwLock<FileCreationMask>>,
sig_dispositions: Arc<Mutex<SigDispositions>>,
resource_limits: ResourceLimits,
) -> Self {
Self {
pid,
threads: Mutex::new(threads),
executable_path: RwLock::new(executable_path),
process_vm,
waiting_children: WaitQueue::new(),
status: Mutex::new(ProcessStatus::Uninit),
parent: Mutex::new(parent),
children: Mutex::new(BTreeMap::new()),
process_group: Mutex::new(process_group),
file_table,
fs,
umask,
sig_dispositions,
sig_queues: Mutex::new(SigQueues::new()),
resource_limits: Mutex::new(resource_limits),
}
}
/// init a user process and run the process
pub fn spawn_user_process(
executable_path: &str,
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 process_group = Weak::clone(&process.process_group.lock());
// FIXME: tty should be a parameter?
let tty = get_n_tty();
tty.set_fg(process_group);
process.run();
Ok(process)
}
fn create_user_process(
executable_path: &str,
argv: Vec<CString>,
envp: Vec<CString>,
) -> Result<Arc<Self>> {
let process_builder = {
let pid = allocate_tid();
let parent = Weak::new();
let mut builder = ProcessBuilder::new(pid, executable_path, parent);
builder.argv(argv).envp(envp);
builder
};
let process = process_builder.build()?;
process_table::add_process(process.clone());
Ok(process)
}
/// start to run current process
pub fn run(&self) {
let threads = self.threads.lock();
// when run the process, the process should has only one thread
debug_assert!(threads.len() == 1);
debug_assert!(self.is_runnable());
let thread = threads[0].clone();
// should not hold the lock when run thread
drop(threads);
thread.run();
}
// *********** Basic structures ***********
pub fn pid(&self) -> Pid {
self.pid
}
pub fn threads(&self) -> &Mutex<Vec<Arc<Thread>>> {
&self.threads
}
pub fn executable_path(&self) -> String {
self.executable_path.read().clone()
}
pub fn set_executable_path(&self, executable_path: String) {
*self.executable_path.write() = executable_path;
}
pub fn resource_limits(&self) -> &Mutex<ResourceLimits> {
&self.resource_limits
}
// *********** Parent and child ***********
pub fn add_child(&self, child: Arc<Process>) {
let child_pid = child.pid();
self.children.lock().insert(child_pid, child);
}
pub fn set_parent(&self, parent: Weak<Process>) {
*self.parent.lock() = parent;
}
pub fn parent(&self) -> Option<Arc<Process>> {
self.parent.lock().upgrade()
}
pub fn children(&self) -> &Mutex<BTreeMap<Pid, Arc<Process>>> {
&self.children
}
pub fn waiting_children(&self) -> &WaitQueue {
&self.waiting_children
}
// *********** Process group ***********
pub fn pgid(&self) -> Pgid {
if let Some(process_group) = self.process_group.lock().upgrade() {
process_group.pgid()
} else {
0
}
}
/// Set process group for current process. If old process group exists,
/// remove current process from old process group.
pub fn set_process_group(&self, process_group: Weak<ProcessGroup>) {
if let Some(old_process_group) = self.process_group() {
old_process_group.remove_process(self.pid());
}
*self.process_group.lock() = process_group;
}
pub fn process_group(&self) -> Option<Arc<ProcessGroup>> {
self.process_group.lock().upgrade()
}
// ************** Virtual Memory *************
pub fn vm(&self) -> &ProcessVm {
&self.process_vm
}
pub fn root_vmar(&self) -> &Vmar<Full> {
self.process_vm.root_vmar()
}
pub fn user_heap(&self) -> &UserHeap {
self.process_vm.user_heap()
}
// ************** File system ****************
pub fn file_table(&self) -> &Arc<Mutex<FileTable>> {
&self.file_table
}
pub fn fs(&self) -> &Arc<RwLock<FsResolver>> {
&self.fs
}
pub fn umask(&self) -> &Arc<RwLock<FileCreationMask>> {
&self.umask
}
// ****************** Signal ******************
pub fn sig_dispositions(&self) -> &Arc<Mutex<SigDispositions>> {
&self.sig_dispositions
}
pub fn has_pending_signal(&self) -> bool {
self.sig_queues.lock().is_empty()
}
pub fn enqueue_signal(&self, signal: Box<dyn Signal>) {
if !self.is_zombie() {
self.sig_queues.lock().enqueue(signal);
}
}
pub fn dequeue_signal(&self, mask: &SigMask) -> Option<Box<dyn Signal>> {
self.sig_queues.lock().dequeue(mask)
}
// ******************* Status ********************
fn set_runnable(&self) {
self.status.lock().set_runnable();
}
fn is_runnable(&self) -> bool {
self.status.lock().is_runnable()
}
pub fn is_zombie(&self) -> bool {
self.status.lock().is_zombie()
}
pub fn set_zombie(&self, term_status: TermStatus) {
*self.status.lock() = ProcessStatus::Zombie(term_status);
}
pub fn exit_code(&self) -> Option<u32> {
match &*self.status.lock() {
ProcessStatus::Runnable | ProcessStatus::Uninit => None,
ProcessStatus::Zombie(term_status) => Some(term_status.as_u32()),
}
}
}
pub fn current() -> Arc<Process> {
let current_thread = Thread::current();
if let Some(posix_thread) = current_thread.as_posix_thread() {
posix_thread.process()
} else {
panic!("[Internal error]The current thread does not belong to a process");
}
}

View File

@ -1,4 +1,5 @@
use super::{Pgid, Pid, Process};
use super::{Pgid, Pid};
use crate::prelude::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProcessFilter {
@ -32,7 +33,7 @@ impl ProcessFilter {
ProcessFilter::Any
} else if wait_pid == 0 {
// wait for any child process with same process group ID
let pgid = Process::current().pgid();
let pgid = current!().pgid();
ProcessFilter::WithPgid(pgid)
} else {
// pid > 0. wait for the child whose process ID is equal to the value of pid.

View File

@ -1,69 +0,0 @@
//! Definition of MMap flags, conforming to the linux mmap interface:
//! https://man7.org/linux/man-pages/man2/mmap.2.html
//!
//! The first 4 bits of the flag value represents the type of memory map,
//! while other bits are used as memory map flags.
//!
use crate::prelude::*;
// The map type mask
const MAP_TYPE: u32 = 0xf;
#[derive(Copy, Clone, PartialEq, Debug, TryFromInt)]
#[repr(u8)]
pub enum MMapType {
File = 0x0, // Invalid
Shared = 0x1,
Private = 0x2,
SharedValidate = 0x3,
}
bitflags! {
pub struct MMapFlags : u32 {
const MAP_FIXED = 0x10;
const MAP_ANONYMOUS = 0x20;
const MAP_GROWSDOWN = 0x100;
const MAP_DENYWRITE = 0x800;
const MAP_EXECUTABLE = 0x1000;
const MAP_LOCKED = 0x2000;
const MAP_NORESERVE = 0x4000;
const MAP_POPULATE = 0x8000;
const MAP_NONBLOCK = 0x10000;
const MAP_STACK = 0x20000;
const MAP_HUGETLB = 0x40000;
const MAP_SYNC = 0x80000;
const MAP_FIXED_NOREPLACE = 0x100000;
}
}
#[derive(Debug)]
pub struct MMapOptions {
typ: MMapType,
flags: MMapFlags,
}
impl TryFrom<u32> for MMapOptions {
type Error = Error;
fn try_from(value: u32) -> Result<Self> {
let typ_raw = (value & MAP_TYPE) as u8;
let typ = MMapType::try_from(typ_raw)?;
let flags_raw = value & !MAP_TYPE;
let Some(flags) = MMapFlags::from_bits(flags_raw) else {
return Err(Error::with_message(Errno::EINVAL, "unknown mmap flags"));
};
Ok(MMapOptions { typ, flags })
}
}
impl MMapOptions {
pub fn typ(&self) -> MMapType {
self.typ
}
pub fn flags(&self) -> MMapFlags {
self.flags
}
}

View File

@ -4,7 +4,6 @@
//! So we define a UserVm struct to store such infomation.
//! Briefly, it contains the exact usage of each segment of virtual spaces.
pub mod mmap_options;
pub mod user_heap;
use crate::prelude::*;
@ -46,6 +45,15 @@ pub struct ProcessVm {
root_vmar: Vmar<Full>,
}
impl Clone for ProcessVm {
fn clone(&self) -> Self {
Self {
root_vmar: self.root_vmar.dup().unwrap(),
user_heap: self.user_heap.clone(),
}
}
}
impl ProcessVm {
pub fn alloc() -> Result<Self> {
let root_vmar = Vmar::<Full>::new_root()?;

View File

@ -5,7 +5,7 @@ use crate::fs::fs_resolver::{FsPath, FsResolver, AT_FDCWD};
use crate::fs::utils::Dentry;
use crate::process::process_vm::ProcessVm;
use crate::process::program_loader::elf::init_stack::{init_aux_vec, InitStack};
use crate::process::TermStatus;
use crate::process::{do_exit_group, TermStatus};
use crate::vm::perms::VmPerms;
use crate::vm::vmo::{VmoOptions, VmoRightsOp};
use crate::{
@ -50,9 +50,8 @@ pub fn load_elf_to_vm(
// FIXME: if `current` macro is used when creating the init process,
// the macro will panic. This corner case should be handled later.
let current = current!();
// FIXME: how to set the correct exit status?
current.exit_group(TermStatus::Exited(1));
do_exit_group(TermStatus::Exited(1));
Task::current().exit();
}
}

View File

@ -16,10 +16,10 @@ use self::c_types::siginfo_t;
use self::sig_mask::SigMask;
use self::sig_num::SigNum;
use crate::current_thread;
use crate::process::posix_thread::posix_thread_ext::PosixThreadExt;
use crate::process::posix_thread::PosixThreadExt;
use crate::process::signal::c_types::ucontext_t;
use crate::process::signal::sig_action::SigActionFlags;
use crate::process::TermStatus;
use crate::process::{do_exit_group, TermStatus};
use crate::util::{write_bytes_to_user, write_val_to_user};
use crate::{
prelude::*,
@ -33,13 +33,11 @@ pub fn handle_pending_signal(context: &mut UserContext) -> Result<()> {
let posix_thread = current_thread.as_posix_thread().unwrap();
let pid = current.pid();
let sig_mask = *posix_thread.sig_mask().lock();
let mut thread_sig_queues = posix_thread.sig_queues().lock();
let mut proc_sig_queues = current.sig_queues().lock();
// We first deal with signal in current thread, then signal in current process.
let signal = if let Some(signal) = thread_sig_queues.dequeue(&sig_mask) {
let signal = if let Some(signal) = posix_thread.dequeue_signal(&sig_mask) {
Some(signal)
} else {
proc_sig_queues.dequeue(&sig_mask)
current.dequeue_signal(&sig_mask)
};
if let Some(signal) = signal {
let sig_num = signal.num();
@ -71,10 +69,10 @@ pub fn handle_pending_signal(context: &mut UserContext) -> Result<()> {
SigDefaultAction::Core | SigDefaultAction::Term => {
warn!(
"{:?}: terminating on signal {}",
&*current.executable_path().read(),
current.executable_path(),
sig_num.sig_name()
);
current.exit_group(TermStatus::Killed(sig_num));
do_exit_group(TermStatus::Killed(sig_num));
// We should exit current here, since we cannot restore a valid status from trap now.
Task::current().exit();
}

View File

@ -24,7 +24,7 @@ impl SigQueues {
}
}
pub fn empty(&self) -> bool {
pub fn is_empty(&self) -> bool {
self.count == 0
}
@ -63,7 +63,7 @@ impl SigQueues {
pub fn dequeue(&mut self, blocked: &SigMask) -> Option<Box<dyn Signal>> {
// Fast path for the common case of no pending signals
if self.empty() {
if self.is_empty() {
return None;
}

View File

@ -4,6 +4,8 @@ use super::TermStatus;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProcessStatus {
// Not ready to run
Uninit,
/// Can be scheduled to run
Runnable,
/// Exit while not reaped by parent
@ -18,4 +20,12 @@ impl ProcessStatus {
pub fn is_zombie(&self) -> bool {
matches!(self, ProcessStatus::Zombie(_))
}
pub fn set_runnable(&mut self) {
*self = ProcessStatus::Runnable;
}
pub fn is_runnable(&self) -> bool {
*self == ProcessStatus::Runnable
}
}

View File

@ -1,6 +1,6 @@
use crate::prelude::*;
use crate::{prelude::*, process::process_table, thread::thread_table};
use super::{process_filter::ProcessFilter, ExitCode, Pid};
use super::{process_filter::ProcessFilter, ExitCode, Pid, Process};
// The definition of WaitOptions is from Occlum
bitflags! {
@ -45,9 +45,7 @@ pub fn wait_child_exit(
}
// return immediately if we find a zombie child
let zombie_child = unwaited_children
.iter()
.find(|child| child.status().lock().is_zombie());
let zombie_child = unwaited_children.iter().find(|child| child.is_zombie());
if let Some(zombie_child) = zombie_child {
let zombie_pid = zombie_child.pid();
@ -56,7 +54,7 @@ pub fn wait_child_exit(
// does not reap child, directly return
return Some(Ok((zombie_pid, exit_code)));
} else {
let exit_code = current.reap_zombie_child(zombie_pid);
let exit_code = reap_zombie_child(&current, zombie_pid);
return Some(Ok((zombie_pid, exit_code)));
}
}
@ -71,3 +69,18 @@ pub fn wait_child_exit(
Ok((pid, exit_code as _))
}
/// Free zombie child with pid, returns the exit code of child process.
fn reap_zombie_child(process: &Process, pid: Pid) -> u32 {
let child_process = process.children().lock().remove(&pid).unwrap();
assert!(child_process.is_zombie());
child_process.root_vmar().destroy_all().unwrap();
for thread in &*child_process.threads().lock() {
thread_table::remove_thread(thread.tid());
}
process_table::remove_process(child_process.pid());
if let Some(process_group) = child_process.process_group() {
process_group.remove_process(child_process.pid());
}
child_process.exit_code().unwrap()
}

View File

@ -1,7 +1,7 @@
use jinux_frame::cpu::UserContext;
use crate::log_syscall_entry;
use crate::process::clone::{clone_child, CloneArgs, CloneFlags};
use crate::process::{clone_child, CloneArgs, CloneFlags};
use crate::{prelude::*, syscall::SYS_CLONE};
use super::SyscallReturn;

View File

@ -6,9 +6,8 @@ use crate::fs::fs_resolver::{FsPath, AT_FDCWD};
use crate::fs::utils::{Dentry, InodeType};
use crate::log_syscall_entry;
use crate::prelude::*;
use crate::process::posix_thread::name::ThreadName;
use crate::process::posix_thread::posix_thread_ext::PosixThreadExt;
use crate::process::program_loader::{check_executable_file, load_program_to_vm};
use crate::process::posix_thread::{PosixThreadExt, ThreadName};
use crate::process::{check_executable_file, load_program_to_vm};
use crate::syscall::{SYS_EXECVE, SYS_EXECVEAT};
use crate::util::{read_cstring_from_user, read_val_from_user};
@ -100,12 +99,12 @@ fn do_execve(
debug!("load program to root vmar");
let (new_executable_path, elf_load_info) = {
let fs_resolver = &*current.fs().read();
let process_vm = current.process_vm();
let process_vm = current.vm();
load_program_to_vm(process_vm, elf_file, argv, envp, fs_resolver, 1)?
};
debug!("load elf in execve succeeds");
// set executable path
*current.executable_path().write() = new_executable_path;
current.set_executable_path(new_executable_path);
// set signal disposition to default
current.sig_dispositions().lock().inherit();
// set cpu context to default

View File

@ -1,4 +1,4 @@
use crate::process::posix_thread::posix_thread_ext::PosixThreadExt;
use crate::process::posix_thread::PosixThreadExt;
use crate::process::TermStatus;
use crate::{log_syscall_entry, prelude::*};

View File

@ -1,4 +1,4 @@
use crate::process::TermStatus;
use crate::process::{do_exit_group, TermStatus};
use crate::{log_syscall_entry, prelude::*};
use crate::syscall::{SyscallReturn, SYS_EXIT_GROUP};
@ -8,6 +8,6 @@ pub fn sys_exit_group(exit_code: u64) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_EXIT_GROUP);
// Exit all thread in current process
let term_status = TermStatus::Exited(exit_code as _);
current!().exit_group(term_status);
do_exit_group(term_status);
Ok(SyscallReturn::Return(0))
}

View File

@ -1,7 +1,7 @@
use crate::{
log_syscall_entry,
prelude::*,
process::clone::{clone_child, CloneArgs},
process::{clone_child, CloneArgs},
};
use jinux_frame::cpu::UserContext;

View File

@ -3,7 +3,7 @@ use crate::{log_syscall_entry, prelude::*};
use crate::process::process_table;
use crate::process::signal::signals::user::{UserSignal, UserSignalKind};
use crate::{
process::{process_filter::ProcessFilter, signal::sig_num::SigNum},
process::{signal::sig_num::SigNum, ProcessFilter},
syscall::SYS_KILL,
};

View File

@ -1,7 +1,6 @@
//! This mod defines mmap flags and the handler to syscall mmap
use crate::fs::file_table::FileDescripter;
use crate::process::process_vm::mmap_options::{MMapFlags, MMapOptions, MMapType};
use crate::vm::perms::VmPerms;
use crate::vm::vmo::{VmoChildOptions, VmoOptions, VmoRightsOp};
use crate::{log_syscall_entry, prelude::*};
@ -131,3 +130,70 @@ fn mmap_filebacked_vmo(
trace!("map range = 0x{:x} - 0x{:x}", map_addr, map_addr + len);
Ok(map_addr)
}
// Definition of MMap flags, conforming to the linux mmap interface:
// https://man7.org/linux/man-pages/man2/mmap.2.html
//
// The first 4 bits of the flag value represents the type of memory map,
// while other bits are used as memory map flags.
// The map type mask
const MAP_TYPE: u32 = 0xf;
#[derive(Copy, Clone, PartialEq, Debug, TryFromInt)]
#[repr(u8)]
pub enum MMapType {
File = 0x0, // Invalid
Shared = 0x1,
Private = 0x2,
SharedValidate = 0x3,
}
bitflags! {
pub struct MMapFlags : u32 {
const MAP_FIXED = 0x10;
const MAP_ANONYMOUS = 0x20;
const MAP_GROWSDOWN = 0x100;
const MAP_DENYWRITE = 0x800;
const MAP_EXECUTABLE = 0x1000;
const MAP_LOCKED = 0x2000;
const MAP_NORESERVE = 0x4000;
const MAP_POPULATE = 0x8000;
const MAP_NONBLOCK = 0x10000;
const MAP_STACK = 0x20000;
const MAP_HUGETLB = 0x40000;
const MAP_SYNC = 0x80000;
const MAP_FIXED_NOREPLACE = 0x100000;
}
}
#[derive(Debug)]
pub struct MMapOptions {
typ: MMapType,
flags: MMapFlags,
}
impl TryFrom<u32> for MMapOptions {
type Error = Error;
fn try_from(value: u32) -> Result<Self> {
let typ_raw = (value & MAP_TYPE) as u8;
let typ = MMapType::try_from(typ_raw)?;
let flags_raw = value & !MAP_TYPE;
let Some(flags) = MMapFlags::from_bits(flags_raw) else {
return Err(Error::with_message(Errno::EINVAL, "unknown mmap flags"));
};
Ok(MMapOptions { typ, flags })
}
}
impl MMapOptions {
pub fn typ(&self) -> MMapType {
self.typ
}
pub fn flags(&self) -> MMapFlags {
self.flags
}
}

View File

@ -1,4 +1,4 @@
use crate::{prelude::*, process::posix_thread::posix_thread_ext::PosixThreadExt, thread::Thread};
use crate::{prelude::*, process::posix_thread::PosixThreadExt, thread::Thread};
use super::SyscallReturn;
@ -8,7 +8,7 @@ pub fn sys_pause() -> Result<SyscallReturn> {
// check sig_queue of current thread and process,
// if there's any pending signal, break loop
let posix_thread = current_thread.as_posix_thread().unwrap();
if !posix_thread.sig_queues().lock().empty() || !current!().sig_queues().lock().empty() {
if posix_thread.has_pending_signal() || current!().has_pending_signal() {
break;
}
// there's no pending signal, yield execution

View File

@ -1,7 +1,7 @@
use crate::log_syscall_entry;
use crate::prelude::*;
use crate::process::posix_thread::name::MAX_THREAD_NAME_LEN;
use crate::process::posix_thread::posix_thread_ext::PosixThreadExt;
use crate::process::posix_thread::PosixThreadExt;
use crate::process::posix_thread::MAX_THREAD_NAME_LEN;
use crate::util::read_cstring_from_user;
use crate::util::write_bytes_to_user;

View File

@ -1,4 +1,4 @@
use crate::process::rlimit::ResourceType;
use crate::process::ResourceType;
use crate::util::{read_val_from_user, write_val_to_user};
use crate::{log_syscall_entry, prelude::*, process::Pid};

View File

@ -1,6 +1,6 @@
use jinux_frame::vm::VmIo;
use crate::process::posix_thread::posix_thread_ext::PosixThreadExt;
use crate::process::posix_thread::PosixThreadExt;
use crate::process::signal::constants::{SIGKILL, SIGSTOP};
use crate::{
log_syscall_entry,

View File

@ -1,7 +1,7 @@
use crate::{
log_syscall_entry,
prelude::*,
process::{posix_thread::posix_thread_ext::PosixThreadExt, signal::c_types::ucontext_t},
process::{posix_thread::PosixThreadExt, signal::c_types::ucontext_t},
util::read_val_from_user,
};
use jinux_frame::cpu::UserContext;

View File

@ -2,7 +2,7 @@ use super::{SyscallReturn, SYS_SET_ROBUST_LIST};
use crate::{
log_syscall_entry,
prelude::*,
process::posix_thread::{posix_thread_ext::PosixThreadExt, robust_list::RobustListHead},
process::posix_thread::{PosixThreadExt, RobustListHead},
util::read_val_from_user,
};

View File

@ -1,8 +1,7 @@
use crate::process::posix_thread::posix_thread_ext::PosixThreadExt;
use crate::{log_syscall_entry, prelude::*};
use super::SyscallReturn;
use super::SYS_SET_TID_ADDRESS;
use crate::process::posix_thread::PosixThreadExt;
use crate::{log_syscall_entry, prelude::*};
pub fn sys_set_tid_address(tidptr: Vaddr) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_SET_TID_ADDRESS);

View File

@ -1,11 +1,7 @@
use crate::{
log_syscall_entry,
prelude::*,
process::{
process_group::ProcessGroup,
process_table::{self, pid_to_process},
Pgid, Pid,
},
process::{process_table, Pgid, Pid, ProcessGroup},
};
use super::{SyscallReturn, SYS_SETPGID};
@ -33,8 +29,8 @@ pub fn sys_setpgid(pid: Pid, pgid: Pgid) -> Result<SyscallReturn> {
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"))?;
let process = process_table::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 process.pgid() == pgid {

View File

@ -1,4 +1,4 @@
use crate::process::posix_thread::posix_thread_ext::PosixThreadExt;
use crate::process::posix_thread::PosixThreadExt;
use crate::thread::{thread_table, Tid};
use crate::{log_syscall_entry, prelude::*};
@ -39,7 +39,6 @@ pub fn sys_tgkill(tgid: Pid, tid: Tid, sig_num: u8) -> Result<SyscallReturn> {
src_uid,
))
};
let mut sig_queue = posix_thread.sig_queues().lock();
sig_queue.enqueue(signal);
posix_thread.enqueue_signal(signal);
Ok(SyscallReturn::Return(0))
}

View File

@ -1,12 +1,12 @@
use crate::{
log_syscall_entry,
process::{process_filter::ProcessFilter, wait::wait_child_exit},
process::{wait_child_exit, ProcessFilter},
syscall::SYS_WAIT4,
util::write_val_to_user,
};
use crate::prelude::*;
use crate::process::wait::WaitOptions;
use crate::process::WaitOptions;
use super::SyscallReturn;

View File

@ -1,7 +1,7 @@
use crate::process::{process_filter::ProcessFilter, wait::wait_child_exit};
use crate::process::{wait_child_exit, ProcessFilter};
use crate::{log_syscall_entry, prelude::*};
use crate::process::wait::WaitOptions;
use crate::process::WaitOptions;
use super::SyscallReturn;
use super::SYS_WAITID;

View File

@ -57,7 +57,7 @@ fn handle_page_fault(trap_info: &TrapInformation) {
fn generate_fault_signal(trap_info: &TrapInformation) {
let current = current!();
let signal = Box::new(FaultSignal::new(trap_info));
current.sig_queues().lock().enqueue(signal);
current.enqueue_signal(signal);
}
macro_rules! log_trap_common {

View File

@ -2,7 +2,7 @@
use core::{
any::Any,
sync::atomic::{AtomicI32, Ordering},
sync::atomic::{AtomicU32, Ordering},
};
use jinux_frame::task::Task;
@ -17,9 +17,9 @@ pub mod status;
pub mod task;
pub mod thread_table;
pub type Tid = i32;
pub type Tid = u32;
static TID_ALLOCATOR: AtomicI32 = AtomicI32::new(0);
static TID_ALLOCATOR: AtomicU32 = AtomicU32::new(0);
/// A thread is a wrapper on top of task.
pub struct Thread {