mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-24 09:53:24 +00:00
756 lines
24 KiB
Rust
756 lines
24 KiB
Rust
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
use self::timer_manager::PosixTimerManager;
|
|
use super::{
|
|
posix_thread::PosixThreadExt,
|
|
process_table,
|
|
process_vm::{Heap, InitStackReader, ProcessVm},
|
|
rlimit::ResourceLimits,
|
|
signal::{
|
|
constants::SIGCHLD,
|
|
sig_disposition::SigDispositions,
|
|
sig_mask::SigMask,
|
|
sig_num::{AtomicSigNum, SigNum},
|
|
signals::Signal,
|
|
Pauser,
|
|
},
|
|
status::ProcessStatus,
|
|
Credentials, TermStatus,
|
|
};
|
|
use crate::{
|
|
device::tty::open_ntty_as_controlling_terminal,
|
|
fs::{file_table::FileTable, fs_resolver::FsResolver, utils::FileCreationMask},
|
|
prelude::*,
|
|
sched::nice::Nice,
|
|
thread::{allocate_tid, Thread},
|
|
time::clocks::ProfClock,
|
|
vm::vmar::Vmar,
|
|
};
|
|
|
|
mod builder;
|
|
mod job_control;
|
|
mod process_group;
|
|
mod session;
|
|
mod terminal;
|
|
mod timer_manager;
|
|
|
|
use aster_rights::Full;
|
|
use atomic::Atomic;
|
|
pub use builder::ProcessBuilder;
|
|
pub use job_control::JobControl;
|
|
pub use process_group::ProcessGroup;
|
|
pub use session::Session;
|
|
pub use terminal::Terminal;
|
|
|
|
/// Process id.
|
|
pub type Pid = u32;
|
|
/// Process group id.
|
|
pub type Pgid = u32;
|
|
/// Session Id.
|
|
pub type Sid = u32;
|
|
|
|
pub type ExitCode = u32;
|
|
|
|
pub(super) fn init() {
|
|
timer_manager::init();
|
|
}
|
|
|
|
/// 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
|
|
children_pauser: Arc<Pauser>,
|
|
|
|
// Mutable Part
|
|
/// The executable path.
|
|
executable_path: RwLock<String>,
|
|
/// The threads
|
|
threads: Mutex<Vec<Arc<Thread>>>,
|
|
/// Process status
|
|
status: Mutex<ProcessStatus>,
|
|
/// Parent process
|
|
pub(super) parent: Mutex<Weak<Process>>,
|
|
/// Children processes
|
|
children: Mutex<BTreeMap<Pid, Arc<Process>>>,
|
|
/// Process group
|
|
pub(super) process_group: Mutex<Weak<ProcessGroup>>,
|
|
/// File table
|
|
file_table: Arc<Mutex<FileTable>>,
|
|
/// FsResolver
|
|
fs: Arc<RwMutex<FsResolver>>,
|
|
/// umask
|
|
umask: Arc<RwLock<FileCreationMask>>,
|
|
/// resource limits
|
|
resource_limits: Mutex<ResourceLimits>,
|
|
/// Scheduling priority nice value
|
|
/// According to POSIX.1, the nice value is a per-process attribute,
|
|
/// the threads in a process should share a nice value.
|
|
nice: Atomic<Nice>,
|
|
|
|
// Signal
|
|
/// Sig dispositions
|
|
sig_dispositions: Arc<Mutex<SigDispositions>>,
|
|
/// The signal that the process should receive when parent process exits.
|
|
parent_death_signal: AtomicSigNum,
|
|
|
|
/// A profiling clock measures the user CPU time and kernel CPU time of the current process.
|
|
prof_clock: Arc<ProfClock>,
|
|
|
|
/// A manager that manages timer resources and utilities of the process.
|
|
timer_manager: PosixTimerManager,
|
|
}
|
|
|
|
impl Process {
|
|
#[allow(clippy::too_many_arguments)]
|
|
fn new(
|
|
pid: Pid,
|
|
parent: Weak<Process>,
|
|
threads: Vec<Arc<Thread>>,
|
|
executable_path: String,
|
|
process_vm: ProcessVm,
|
|
|
|
fs: Arc<RwMutex<FsResolver>>,
|
|
file_table: Arc<Mutex<FileTable>>,
|
|
|
|
umask: Arc<RwLock<FileCreationMask>>,
|
|
resource_limits: ResourceLimits,
|
|
nice: Nice,
|
|
sig_dispositions: Arc<Mutex<SigDispositions>>,
|
|
) -> Arc<Self> {
|
|
let children_pauser = {
|
|
// SIGCHID does not interrupt pauser. Child process will
|
|
// resume paused parent when doing exit.
|
|
let sigmask = SigMask::from(SIGCHLD);
|
|
Pauser::new_with_mask(sigmask)
|
|
};
|
|
|
|
let prof_clock = ProfClock::new();
|
|
|
|
Arc::new_cyclic(|process_ref: &Weak<Process>| Self {
|
|
pid,
|
|
threads: Mutex::new(threads),
|
|
executable_path: RwLock::new(executable_path),
|
|
process_vm,
|
|
children_pauser,
|
|
status: Mutex::new(ProcessStatus::Uninit),
|
|
parent: Mutex::new(parent),
|
|
children: Mutex::new(BTreeMap::new()),
|
|
process_group: Mutex::new(Weak::new()),
|
|
file_table,
|
|
fs,
|
|
umask,
|
|
sig_dispositions,
|
|
parent_death_signal: AtomicSigNum::new_empty(),
|
|
resource_limits: Mutex::new(resource_limits),
|
|
nice: Atomic::new(nice),
|
|
timer_manager: PosixTimerManager::new(&prof_clock, process_ref),
|
|
prof_clock,
|
|
})
|
|
}
|
|
|
|
/// 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)?;
|
|
|
|
open_ntty_as_controlling_terminal(&process)?;
|
|
|
|
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 credentials = Credentials::new_root();
|
|
|
|
let mut builder = ProcessBuilder::new(pid, executable_path, parent);
|
|
builder.argv(argv).envp(envp).credentials(credentials);
|
|
builder
|
|
};
|
|
|
|
let process = process_builder.build()?;
|
|
|
|
// Lock order: session table -> group table -> process table -> group of process
|
|
// -> group inner -> session inner
|
|
let mut session_table_mut = process_table::session_table_mut();
|
|
let mut group_table_mut = process_table::group_table_mut();
|
|
let mut process_table_mut = process_table::process_table_mut();
|
|
|
|
// Creates new group
|
|
let group = ProcessGroup::new(process.clone());
|
|
*process.process_group.lock() = Arc::downgrade(&group);
|
|
group_table_mut.insert(group.pgid(), group.clone());
|
|
|
|
// Creates new session
|
|
let session = Session::new(group.clone());
|
|
group.inner.lock().session = Arc::downgrade(&session);
|
|
session.inner.lock().leader = Some(process.clone());
|
|
session_table_mut.insert(session.sid(), session);
|
|
|
|
process_table_mut.insert(process.pid(), 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
|
|
}
|
|
|
|
/// Gets the profiling clock of the process.
|
|
pub fn prof_clock(&self) -> &Arc<ProfClock> {
|
|
&self.prof_clock
|
|
}
|
|
|
|
/// Gets the timer resources and utilities of the process.
|
|
pub fn timer_manager(&self) -> &PosixTimerManager {
|
|
&self.timer_manager
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
pub fn nice(&self) -> &Atomic<Nice> {
|
|
&self.nice
|
|
}
|
|
|
|
pub fn main_thread(&self) -> Option<Arc<Thread>> {
|
|
self.threads
|
|
.lock()
|
|
.iter()
|
|
.find(|thread| thread.tid() == self.pid)
|
|
.cloned()
|
|
}
|
|
|
|
// *********** Parent and child ***********
|
|
pub fn parent(&self) -> Option<Arc<Process>> {
|
|
self.parent.lock().upgrade()
|
|
}
|
|
|
|
pub fn is_init_process(&self) -> bool {
|
|
self.parent().is_none()
|
|
}
|
|
|
|
pub(super) fn children(&self) -> &Mutex<BTreeMap<Pid, Arc<Process>>> {
|
|
&self.children
|
|
}
|
|
|
|
pub fn has_child(&self, pid: &Pid) -> bool {
|
|
self.children.lock().contains_key(pid)
|
|
}
|
|
|
|
pub fn children_pauser(&self) -> &Arc<Pauser> {
|
|
&self.children_pauser
|
|
}
|
|
|
|
// *********** Process group & Session***********
|
|
|
|
/// 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
|
|
}
|
|
}
|
|
|
|
/// Returns the process group which the process belongs to.
|
|
pub fn process_group(&self) -> Option<Arc<ProcessGroup>> {
|
|
self.process_group.lock().upgrade()
|
|
}
|
|
|
|
/// Returns whether `self` is the leader of process group.
|
|
fn is_group_leader(self: &Arc<Self>) -> bool {
|
|
let Some(process_group) = self.process_group() else {
|
|
return false;
|
|
};
|
|
|
|
let Some(leader) = process_group.leader() else {
|
|
return false;
|
|
};
|
|
|
|
Arc::ptr_eq(self, &leader)
|
|
}
|
|
|
|
/// Returns the session which the process belongs to.
|
|
pub fn session(&self) -> Option<Arc<Session>> {
|
|
let process_group = self.process_group()?;
|
|
process_group.session()
|
|
}
|
|
|
|
/// Returns whether the process is session leader.
|
|
pub fn is_session_leader(self: &Arc<Self>) -> bool {
|
|
let session = self.session().unwrap();
|
|
|
|
let Some(leading_process) = session.leader() else {
|
|
return false;
|
|
};
|
|
|
|
Arc::ptr_eq(self, &leading_process)
|
|
}
|
|
|
|
/// Moves the process to the new session.
|
|
///
|
|
/// If the process is already session leader, this method does nothing.
|
|
///
|
|
/// Otherwise, this method creates a new process group in a new session
|
|
/// and moves the process to the session, returning the new session.
|
|
///
|
|
/// This method may return the following errors:
|
|
/// * `EPERM`, if the process is a process group leader, or some existing session
|
|
/// or process group has the same ID as the process.
|
|
pub fn to_new_session(self: &Arc<Self>) -> Result<Arc<Session>> {
|
|
if self.is_session_leader() {
|
|
return Ok(self.session().unwrap());
|
|
}
|
|
|
|
if self.is_group_leader() {
|
|
return_errno_with_message!(
|
|
Errno::EPERM,
|
|
"process group leader cannot be moved to new session."
|
|
);
|
|
}
|
|
|
|
let session = self.session().unwrap();
|
|
|
|
// Lock order: session table -> group table -> group of process -> group inner -> session inner
|
|
let mut session_table_mut = process_table::session_table_mut();
|
|
let mut group_table_mut = process_table::group_table_mut();
|
|
let mut self_group_mut = self.process_group.lock();
|
|
|
|
if session_table_mut.contains_key(&self.pid) {
|
|
return_errno_with_message!(Errno::EPERM, "cannot create new session");
|
|
}
|
|
|
|
if group_table_mut.contains_key(&self.pid) {
|
|
return_errno_with_message!(Errno::EPERM, "cannot create process group");
|
|
}
|
|
|
|
// Removes the process from old group
|
|
if let Some(old_group) = self_group_mut.upgrade() {
|
|
let mut group_inner = old_group.inner.lock();
|
|
let mut session_inner = session.inner.lock();
|
|
group_inner.remove_process(&self.pid);
|
|
*self_group_mut = Weak::new();
|
|
|
|
if group_inner.is_empty() {
|
|
group_table_mut.remove(&old_group.pgid());
|
|
debug_assert!(session_inner.process_groups.contains_key(&old_group.pgid()));
|
|
session_inner.process_groups.remove(&old_group.pgid());
|
|
|
|
if session_inner.is_empty() {
|
|
session_table_mut.remove(&session.sid());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Creates a new process group
|
|
let new_group = ProcessGroup::new(self.clone());
|
|
*self_group_mut = Arc::downgrade(&new_group);
|
|
group_table_mut.insert(new_group.pgid(), new_group.clone());
|
|
|
|
// Creates a new session
|
|
let new_session = Session::new(new_group.clone());
|
|
let mut new_group_inner = new_group.inner.lock();
|
|
new_group_inner.session = Arc::downgrade(&new_session);
|
|
new_session.inner.lock().leader = Some(self.clone());
|
|
session_table_mut.insert(new_session.sid(), new_session.clone());
|
|
|
|
// Removes the process from session.
|
|
let mut session_inner = session.inner.lock();
|
|
session_inner.remove_process(self);
|
|
|
|
Ok(new_session)
|
|
}
|
|
|
|
/// Moves the process to other process group.
|
|
///
|
|
/// * If the group already exists, the process and the group should belong to the same session.
|
|
/// * If the group does not exist, this method creates a new group for the process and move the
|
|
/// process to the group. The group is added to the session of the process.
|
|
///
|
|
/// This method may return `EPERM` in following cases:
|
|
/// * The process is session leader;
|
|
/// * The group already exists, but the group does not belong to the same session as the process;
|
|
/// * The group does not exist, but `pgid` is not equal to `pid` of the process.
|
|
pub fn to_other_group(self: &Arc<Self>, pgid: Pgid) -> Result<()> {
|
|
// if the process already belongs to the process group
|
|
if self.pgid() == pgid {
|
|
return Ok(());
|
|
}
|
|
|
|
if self.is_session_leader() {
|
|
return_errno_with_message!(Errno::EPERM, "the process cannot be a session leader");
|
|
}
|
|
|
|
if let Some(process_group) = process_table::get_process_group(&pgid) {
|
|
let session = self.session().unwrap();
|
|
if !session.contains_process_group(&process_group) {
|
|
return_errno_with_message!(
|
|
Errno::EPERM,
|
|
"the group and process does not belong to same session"
|
|
);
|
|
}
|
|
self.to_specified_group(&process_group)?;
|
|
} else {
|
|
if pgid != self.pid() {
|
|
return_errno_with_message!(
|
|
Errno::EPERM,
|
|
"the new process group should have the same ID as the process."
|
|
);
|
|
}
|
|
|
|
self.to_new_group()?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Creates a new process group and moves the process to the group.
|
|
///
|
|
/// The new group will be added to the same session as the process.
|
|
fn to_new_group(self: &Arc<Self>) -> Result<()> {
|
|
let session = self.session().unwrap();
|
|
// Lock order: group table -> group of process -> group inner -> session inner
|
|
let mut group_table_mut = process_table::group_table_mut();
|
|
let mut self_group_mut = self.process_group.lock();
|
|
|
|
// Removes the process from old group
|
|
if let Some(old_group) = self_group_mut.upgrade() {
|
|
let mut group_inner = old_group.inner.lock();
|
|
let mut session_inner = session.inner.lock();
|
|
group_inner.remove_process(&self.pid);
|
|
*self_group_mut = Weak::new();
|
|
|
|
if group_inner.is_empty() {
|
|
group_table_mut.remove(&old_group.pgid());
|
|
debug_assert!(session_inner.process_groups.contains_key(&old_group.pgid()));
|
|
// The old session won't be empty, since we will add a new group to the session.
|
|
session_inner.process_groups.remove(&old_group.pgid());
|
|
}
|
|
}
|
|
|
|
// Creates a new process group. Adds the new group to group table and session.
|
|
let new_group = ProcessGroup::new(self.clone());
|
|
|
|
let mut new_group_inner = new_group.inner.lock();
|
|
let mut session_inner = session.inner.lock();
|
|
|
|
*self_group_mut = Arc::downgrade(&new_group);
|
|
|
|
group_table_mut.insert(new_group.pgid(), new_group.clone());
|
|
|
|
new_group_inner.session = Arc::downgrade(&session);
|
|
session_inner
|
|
.process_groups
|
|
.insert(new_group.pgid(), new_group.clone());
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Moves the process to a specified group.
|
|
///
|
|
/// The caller needs to ensure that the process and the group belongs to the same session.
|
|
fn to_specified_group(self: &Arc<Process>, group: &Arc<ProcessGroup>) -> Result<()> {
|
|
// Lock order: group table -> group of process -> group inner (small pgid -> big pgid)
|
|
let mut group_table_mut = process_table::group_table_mut();
|
|
let mut self_group_mut = self.process_group.lock();
|
|
|
|
// Removes the process from old group
|
|
let mut group_inner = if let Some(old_group) = self_group_mut.upgrade() {
|
|
// Lock order: group with smaller pgid first
|
|
let (mut old_group_inner, group_inner) = match old_group.pgid().cmp(&group.pgid()) {
|
|
core::cmp::Ordering::Equal => return Ok(()),
|
|
core::cmp::Ordering::Less => (old_group.inner.lock(), group.inner.lock()),
|
|
core::cmp::Ordering::Greater => {
|
|
let group_inner = group.inner.lock();
|
|
let old_group_inner = old_group.inner.lock();
|
|
(old_group_inner, group_inner)
|
|
}
|
|
};
|
|
old_group_inner.remove_process(&self.pid);
|
|
*self_group_mut = Weak::new();
|
|
|
|
if old_group_inner.is_empty() {
|
|
group_table_mut.remove(&old_group.pgid());
|
|
}
|
|
|
|
group_inner
|
|
} else {
|
|
group.inner.lock()
|
|
};
|
|
|
|
// Adds the process to the specified group
|
|
group_inner.processes.insert(self.pid, self.clone());
|
|
*self_group_mut = Arc::downgrade(group);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// ************** Virtual Memory *************
|
|
|
|
pub fn vm(&self) -> &ProcessVm {
|
|
&self.process_vm
|
|
}
|
|
|
|
pub fn root_vmar(&self) -> &Vmar<Full> {
|
|
self.process_vm.root_vmar()
|
|
}
|
|
|
|
pub fn heap(&self) -> &Heap {
|
|
self.process_vm.heap()
|
|
}
|
|
|
|
pub fn init_stack_reader(&self) -> InitStackReader {
|
|
self.process_vm.init_stack_reader()
|
|
}
|
|
|
|
// ************** File system ****************
|
|
|
|
pub fn file_table(&self) -> &Arc<Mutex<FileTable>> {
|
|
&self.file_table
|
|
}
|
|
|
|
pub fn fs(&self) -> &Arc<RwMutex<FsResolver>> {
|
|
&self.fs
|
|
}
|
|
|
|
pub fn umask(&self) -> &Arc<RwLock<FileCreationMask>> {
|
|
&self.umask
|
|
}
|
|
|
|
// ****************** Signal ******************
|
|
|
|
pub fn sig_dispositions(&self) -> &Arc<Mutex<SigDispositions>> {
|
|
&self.sig_dispositions
|
|
}
|
|
|
|
/// Enqueues a process-directed signal. This method should only be used for enqueue kernel
|
|
/// signal and fault signal.
|
|
///
|
|
/// The signal may be delivered to any one of the threads that does not currently have the
|
|
/// signal blocked. If more than one of the threads has the signal unblocked, then this method
|
|
/// chooses an arbitrary thread to which to deliver the signal.
|
|
///
|
|
/// TODO: restrict these method with access control tool.
|
|
pub fn enqueue_signal(&self, signal: impl Signal + Clone + 'static) {
|
|
if self.is_zombie() {
|
|
return;
|
|
}
|
|
|
|
// TODO: check that the signal is not user signal
|
|
|
|
// Enqueue signal to the first thread that does not block the signal
|
|
let threads = self.threads.lock();
|
|
for thread in threads.iter() {
|
|
let posix_thread = thread.as_posix_thread().unwrap();
|
|
if !posix_thread.has_signal_blocked(&signal) {
|
|
posix_thread.enqueue_signal(Box::new(signal));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If all threads block the signal, enqueue signal to the first thread
|
|
let thread = threads.iter().next().unwrap();
|
|
let posix_thread = thread.as_posix_thread().unwrap();
|
|
posix_thread.enqueue_signal(Box::new(signal));
|
|
}
|
|
|
|
/// Clears the parent death signal.
|
|
pub fn clear_parent_death_signal(&self) {
|
|
self.parent_death_signal.clear();
|
|
}
|
|
|
|
/// Sets the parent death signal as `signum`.
|
|
pub fn set_parent_death_signal(&self, sig_num: SigNum) {
|
|
self.parent_death_signal.set(sig_num);
|
|
}
|
|
|
|
/// Returns the parent death signal.
|
|
///
|
|
/// The parent death signal is the signal will be sent to child processes
|
|
/// when the process exits.
|
|
pub fn parent_death_signal(&self) -> Option<SigNum> {
|
|
self.parent_death_signal.as_sig_num()
|
|
}
|
|
|
|
// ******************* 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<ExitCode> {
|
|
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 = current_thread!();
|
|
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");
|
|
}
|
|
}
|
|
|
|
#[cfg(ktest)]
|
|
mod test {
|
|
|
|
use ostd::prelude::*;
|
|
|
|
use super::*;
|
|
|
|
fn new_process(parent: Option<Arc<Process>>) -> Arc<Process> {
|
|
crate::util::random::init();
|
|
crate::fs::rootfs::init_root_mount();
|
|
let pid = allocate_tid();
|
|
let parent = if let Some(parent) = parent {
|
|
Arc::downgrade(&parent)
|
|
} else {
|
|
Weak::new()
|
|
};
|
|
Process::new(
|
|
pid,
|
|
parent,
|
|
vec![],
|
|
String::new(),
|
|
ProcessVm::alloc(),
|
|
Arc::new(RwMutex::new(FsResolver::new())),
|
|
Arc::new(Mutex::new(FileTable::new())),
|
|
Arc::new(RwLock::new(FileCreationMask::default())),
|
|
ResourceLimits::default(),
|
|
Nice::default(),
|
|
Arc::new(Mutex::new(SigDispositions::default())),
|
|
)
|
|
}
|
|
|
|
fn new_process_in_session(parent: Option<Arc<Process>>) -> Arc<Process> {
|
|
// Lock order: session table -> group table -> group of process -> group inner
|
|
// -> session inner
|
|
let mut session_table_mut = process_table::session_table_mut();
|
|
let mut group_table_mut = process_table::group_table_mut();
|
|
|
|
let process = new_process(parent);
|
|
// Creates new group
|
|
let group = ProcessGroup::new(process.clone());
|
|
*process.process_group.lock() = Arc::downgrade(&group);
|
|
|
|
// Creates new session
|
|
let sess = Session::new(group.clone());
|
|
group.inner.lock().session = Arc::downgrade(&sess);
|
|
sess.inner.lock().leader = Some(process.clone());
|
|
|
|
group_table_mut.insert(group.pgid(), group);
|
|
session_table_mut.insert(sess.sid(), sess);
|
|
|
|
process
|
|
}
|
|
|
|
fn remove_session_and_group(process: Arc<Process>) {
|
|
// Lock order: session table -> group table
|
|
let mut session_table_mut = process_table::session_table_mut();
|
|
let mut group_table_mut = process_table::group_table_mut();
|
|
if let Some(sess) = process.session() {
|
|
session_table_mut.remove(&sess.sid());
|
|
}
|
|
|
|
if let Some(group) = process.process_group() {
|
|
group_table_mut.remove(&group.pgid());
|
|
}
|
|
}
|
|
|
|
#[ktest]
|
|
fn init_process() {
|
|
crate::time::clocks::init_for_ktest();
|
|
let process = new_process(None);
|
|
assert!(process.process_group().is_none());
|
|
assert!(process.session().is_none());
|
|
}
|
|
|
|
#[ktest]
|
|
fn init_process_in_session() {
|
|
crate::time::clocks::init_for_ktest();
|
|
let process = new_process_in_session(None);
|
|
assert!(process.is_group_leader());
|
|
assert!(process.is_session_leader());
|
|
remove_session_and_group(process);
|
|
}
|
|
|
|
#[ktest]
|
|
fn to_new_session() {
|
|
crate::time::clocks::init_for_ktest();
|
|
let process = new_process_in_session(None);
|
|
let sess = process.session().unwrap();
|
|
sess.inner.lock().leader = None;
|
|
|
|
assert!(!process.is_session_leader());
|
|
assert!(process
|
|
.to_new_session()
|
|
.is_err_and(|e| e.error() == Errno::EPERM));
|
|
|
|
let group = process.process_group().unwrap();
|
|
group.inner.lock().leader = None;
|
|
assert!(!process.is_group_leader());
|
|
|
|
assert!(process
|
|
.to_new_session()
|
|
.is_err_and(|e| e.error() == Errno::EPERM));
|
|
}
|
|
}
|