mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-26 10:53:25 +00:00
Add basic structures for session, terminal and job control
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
2edbe1f725
commit
9040fb54ea
@ -275,15 +275,31 @@ fn clone_child_process(parent_context: UserContext, clone_args: CloneArgs) -> Re
|
|||||||
.file_table(child_file_table)
|
.file_table(child_file_table)
|
||||||
.fs(child_fs)
|
.fs(child_fs)
|
||||||
.umask(child_umask)
|
.umask(child_umask)
|
||||||
.sig_dispositions(child_sig_dispositions)
|
.sig_dispositions(child_sig_dispositions);
|
||||||
.process_group(current.process_group().unwrap());
|
|
||||||
|
|
||||||
process_builder.build()?
|
process_builder.build()?
|
||||||
};
|
};
|
||||||
|
|
||||||
current!().add_child(child.clone());
|
// Adds child to parent's process group, and parent's child processes.
|
||||||
process_table::add_process(child.clone());
|
let process_group = current!().process_group().unwrap();
|
||||||
|
|
||||||
|
let mut process_table_mut = process_table::process_table_mut();
|
||||||
|
let mut group_inner = process_group.inner.lock();
|
||||||
|
let mut child_group_mut = child.process_group.lock();
|
||||||
|
let mut children_mut = current.children().lock();
|
||||||
|
|
||||||
|
children_mut.insert(child.pid(), child.clone());
|
||||||
|
|
||||||
|
group_inner.processes.insert(child.pid(), child.clone());
|
||||||
|
*child_group_mut = Arc::downgrade(&process_group);
|
||||||
|
|
||||||
|
process_table_mut.insert(child.pid(), child.clone());
|
||||||
|
drop(process_table_mut);
|
||||||
|
drop(group_inner);
|
||||||
|
drop(child_group_mut);
|
||||||
|
drop(children_mut);
|
||||||
|
|
||||||
|
// Deals with clone flags
|
||||||
let child_thread = thread_table::tid_to_thread(child_tid).unwrap();
|
let child_thread = thread_table::tid_to_thread(child_tid).unwrap();
|
||||||
let child_posix_thread = child_thread.as_posix_thread().unwrap();
|
let child_posix_thread = child_thread.as_posix_thread().unwrap();
|
||||||
clone_parent_settid(child_tid, clone_args.parent_tidptr, clone_flags)?;
|
clone_parent_settid(child_tid, clone_args.parent_tidptr, clone_flags)?;
|
||||||
|
@ -37,9 +37,11 @@ pub fn do_exit_group(term_status: TermStatus) {
|
|||||||
// Move children to the init process
|
// Move children to the init process
|
||||||
if !is_init_process(¤t) {
|
if !is_init_process(¤t) {
|
||||||
if let Some(init_process) = get_init_process() {
|
if let Some(init_process) = get_init_process() {
|
||||||
|
let mut init_children = init_process.children().lock();
|
||||||
for (_, child_process) in current.children().lock().extract_if(|_, _| true) {
|
for (_, child_process) in current.children().lock().extract_if(|_, _| true) {
|
||||||
child_process.set_parent(Arc::downgrade(&init_process));
|
let mut parent = child_process.parent.lock();
|
||||||
init_process.add_child(child_process);
|
init_children.insert(child_process.pid(), child_process.clone());
|
||||||
|
*parent = Arc::downgrade(&init_process);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,7 +58,7 @@ const INIT_PROCESS_PID: Pid = 1;
|
|||||||
|
|
||||||
/// Get the init process
|
/// Get the init process
|
||||||
fn get_init_process() -> Option<Arc<Process>> {
|
fn get_init_process() -> Option<Arc<Process>> {
|
||||||
process_table::pid_to_process(INIT_PROCESS_PID)
|
process_table::get_process(&INIT_PROCESS_PID)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_init_process(process: &Process) -> bool {
|
fn is_init_process(process: &Process) -> bool {
|
||||||
|
@ -4,7 +4,6 @@ pub mod posix_thread;
|
|||||||
#[allow(clippy::module_inception)]
|
#[allow(clippy::module_inception)]
|
||||||
mod process;
|
mod process;
|
||||||
mod process_filter;
|
mod process_filter;
|
||||||
mod process_group;
|
|
||||||
pub mod process_table;
|
pub mod process_table;
|
||||||
mod process_vm;
|
mod process_vm;
|
||||||
mod program_loader;
|
mod program_loader;
|
||||||
@ -17,9 +16,10 @@ mod wait;
|
|||||||
pub use clone::{clone_child, CloneArgs, CloneFlags};
|
pub use clone::{clone_child, CloneArgs, CloneFlags};
|
||||||
pub use exit::do_exit_group;
|
pub use exit::do_exit_group;
|
||||||
pub use process::ProcessBuilder;
|
pub use process::ProcessBuilder;
|
||||||
pub use process::{current, ExitCode, Pgid, Pid, Process};
|
pub use process::{
|
||||||
|
current, ExitCode, JobControl, Pgid, Pid, Process, ProcessGroup, Session, Sid, Terminal,
|
||||||
|
};
|
||||||
pub use process_filter::ProcessFilter;
|
pub use process_filter::ProcessFilter;
|
||||||
pub use process_group::ProcessGroup;
|
|
||||||
pub use program_loader::{check_executable_file, load_program_to_vm};
|
pub use program_loader::{check_executable_file, load_program_to_vm};
|
||||||
pub use rlimit::ResourceType;
|
pub use rlimit::ResourceType;
|
||||||
pub use term_status::TermStatus;
|
pub use term_status::TermStatus;
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
use crate::fs::file_table::FileTable;
|
use crate::fs::file_table::FileTable;
|
||||||
use crate::fs::fs_resolver::FsResolver;
|
use crate::fs::fs_resolver::FsResolver;
|
||||||
use crate::fs::utils::FileCreationMask;
|
use crate::fs::utils::FileCreationMask;
|
||||||
use crate::process::posix_thread::PosixThreadBuilder;
|
use crate::process::posix_thread::{PosixThreadBuilder, PosixThreadExt};
|
||||||
use crate::process::process_group::ProcessGroup;
|
|
||||||
use crate::process::process_table;
|
|
||||||
use crate::process::process_vm::ProcessVm;
|
use crate::process::process_vm::ProcessVm;
|
||||||
use crate::process::rlimit::ResourceLimits;
|
use crate::process::rlimit::ResourceLimits;
|
||||||
use crate::process::{posix_thread::PosixThreadExt, signal::sig_disposition::SigDispositions};
|
use crate::process::signal::sig_disposition::SigDispositions;
|
||||||
use crate::thread::Thread;
|
use crate::thread::Thread;
|
||||||
|
|
||||||
use super::{Pid, Process};
|
use super::{Pid, Process};
|
||||||
@ -23,7 +21,6 @@ pub struct ProcessBuilder<'a> {
|
|||||||
argv: Option<Vec<CString>>,
|
argv: Option<Vec<CString>>,
|
||||||
envp: Option<Vec<CString>>,
|
envp: Option<Vec<CString>>,
|
||||||
process_vm: Option<ProcessVm>,
|
process_vm: Option<ProcessVm>,
|
||||||
process_group: Option<Arc<ProcessGroup>>,
|
|
||||||
file_table: Option<Arc<Mutex<FileTable>>>,
|
file_table: Option<Arc<Mutex<FileTable>>>,
|
||||||
fs: Option<Arc<RwLock<FsResolver>>>,
|
fs: Option<Arc<RwLock<FsResolver>>>,
|
||||||
umask: Option<Arc<RwLock<FileCreationMask>>>,
|
umask: Option<Arc<RwLock<FileCreationMask>>>,
|
||||||
@ -41,7 +38,6 @@ impl<'a> ProcessBuilder<'a> {
|
|||||||
argv: None,
|
argv: None,
|
||||||
envp: None,
|
envp: None,
|
||||||
process_vm: None,
|
process_vm: None,
|
||||||
process_group: None,
|
|
||||||
file_table: None,
|
file_table: None,
|
||||||
fs: None,
|
fs: None,
|
||||||
umask: None,
|
umask: None,
|
||||||
@ -60,11 +56,6 @@ impl<'a> ProcessBuilder<'a> {
|
|||||||
self
|
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 {
|
pub fn file_table(&mut self, file_table: Arc<Mutex<FileTable>>) -> &mut Self {
|
||||||
self.file_table = Some(file_table);
|
self.file_table = Some(file_table);
|
||||||
self
|
self
|
||||||
@ -126,7 +117,6 @@ impl<'a> ProcessBuilder<'a> {
|
|||||||
argv,
|
argv,
|
||||||
envp,
|
envp,
|
||||||
process_vm,
|
process_vm,
|
||||||
process_group,
|
|
||||||
file_table,
|
file_table,
|
||||||
fs,
|
fs,
|
||||||
umask,
|
umask,
|
||||||
@ -136,10 +126,6 @@ impl<'a> ProcessBuilder<'a> {
|
|||||||
|
|
||||||
let process_vm = process_vm.or_else(|| Some(ProcessVm::alloc())).unwrap();
|
let process_vm = process_vm.or_else(|| Some(ProcessVm::alloc())).unwrap();
|
||||||
|
|
||||||
let process_group_ref = process_group
|
|
||||||
.as_ref()
|
|
||||||
.map_or_else(Weak::new, Arc::downgrade);
|
|
||||||
|
|
||||||
let file_table = file_table
|
let file_table = file_table
|
||||||
.or_else(|| Some(Arc::new(Mutex::new(FileTable::new_with_stdio()))))
|
.or_else(|| Some(Arc::new(Mutex::new(FileTable::new_with_stdio()))))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -168,7 +154,6 @@ impl<'a> ProcessBuilder<'a> {
|
|||||||
threads,
|
threads,
|
||||||
executable_path.to_string(),
|
executable_path.to_string(),
|
||||||
process_vm,
|
process_vm,
|
||||||
process_group_ref,
|
|
||||||
file_table,
|
file_table,
|
||||||
fs,
|
fs,
|
||||||
umask,
|
umask,
|
||||||
@ -194,15 +179,6 @@ impl<'a> ProcessBuilder<'a> {
|
|||||||
|
|
||||||
process.threads().lock().push(thread);
|
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();
|
process.set_runnable();
|
||||||
|
|
||||||
Ok(process)
|
Ok(process)
|
||||||
|
140
services/libs/jinux-std/src/process/process/job_control.rs
Normal file
140
services/libs/jinux-std/src/process/process/job_control.rs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
use crate::process::signal::constants::{SIGCONT, SIGHUP};
|
||||||
|
use crate::process::signal::signals::kernel::KernelSignal;
|
||||||
|
use crate::process::{ProcessGroup, Session};
|
||||||
|
|
||||||
|
/// The job control for terminals like tty and pty.
|
||||||
|
///
|
||||||
|
/// This struct is used to support shell job control, which allows users to
|
||||||
|
/// run commands in the foreground or in the background. This struct manages
|
||||||
|
/// the session and foreground process group for a terminal.
|
||||||
|
pub struct JobControl {
|
||||||
|
foreground: SpinLock<Weak<ProcessGroup>>,
|
||||||
|
session: SpinLock<Weak<Session>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JobControl {
|
||||||
|
/// Creates a new `TtyJobControl`
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
foreground: SpinLock::new(Weak::new()),
|
||||||
|
session: SpinLock::new(Weak::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// *************** Session ***************
|
||||||
|
|
||||||
|
/// Returns the session whose controlling terminal is the terminal.
|
||||||
|
fn session(&self) -> Option<Arc<Session>> {
|
||||||
|
self.session.lock().upgrade()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the terminal as the controlling terminal of the `session`.
|
||||||
|
///
|
||||||
|
/// # Panic
|
||||||
|
///
|
||||||
|
/// This terminal should not belong to any session.
|
||||||
|
pub fn set_session(&self, session: &Arc<Session>) {
|
||||||
|
debug_assert!(self.session().is_none());
|
||||||
|
*self.session.lock() = Arc::downgrade(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the terminal as the controlling terminal of the session of current process.
|
||||||
|
///
|
||||||
|
/// # Panic
|
||||||
|
///
|
||||||
|
/// This function should only be called in process context.
|
||||||
|
pub fn set_current_session(&self) -> Result<()> {
|
||||||
|
if self.session().is_some() {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::EPERM,
|
||||||
|
"the terminal is already controlling terminal of another session"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let current = current!();
|
||||||
|
|
||||||
|
let process_group = current.process_group().unwrap();
|
||||||
|
*self.foreground.lock() = Arc::downgrade(&process_group);
|
||||||
|
|
||||||
|
let session = current.session().unwrap();
|
||||||
|
*self.session.lock() = Arc::downgrade(&session);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Releases the current session from this terminal.
|
||||||
|
pub fn release_current_session(&self) -> Result<()> {
|
||||||
|
let Some(session) = self.session() else {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::ENOTTY,
|
||||||
|
"the terminal is not controlling terminal now"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(foreground) = self.foreground() {
|
||||||
|
foreground.kernel_signal(KernelSignal::new(SIGHUP));
|
||||||
|
foreground.kernel_signal(KernelSignal::new(SIGCONT));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// *************** Foreground process group ***************
|
||||||
|
|
||||||
|
/// Returns the foreground process group
|
||||||
|
pub fn foreground(&self) -> Option<Arc<ProcessGroup>> {
|
||||||
|
self.foreground.lock().upgrade()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the foreground process group.
|
||||||
|
///
|
||||||
|
/// # Panic
|
||||||
|
///
|
||||||
|
/// The process group should belong to one session.
|
||||||
|
pub fn set_foreground(&self, process_group: Option<&Arc<ProcessGroup>>) -> Result<()> {
|
||||||
|
let Some(process_group) = process_group else {
|
||||||
|
// FIXME: should we allow this branch?
|
||||||
|
*self.foreground.lock() = Weak::new();
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
let session = process_group.session().unwrap();
|
||||||
|
let Some(terminal_session) = self.session() else {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::EPERM,
|
||||||
|
"the terminal does not become controlling terminal of one session."
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if !Arc::ptr_eq(&terminal_session, &session) {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::EPERM,
|
||||||
|
"the process proup belongs to different session"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
*self.foreground.lock() = Arc::downgrade(process_group);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines whether current process belongs to the foreground process group. If
|
||||||
|
/// the foreground process group is None, returns true.
|
||||||
|
///
|
||||||
|
/// # Panic
|
||||||
|
///
|
||||||
|
/// This function should only be called in process context.
|
||||||
|
pub fn current_belongs_to_foreground(&self) -> bool {
|
||||||
|
let Some(foreground) = self.foreground() else {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
foreground.contains_process(current!().pid())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for JobControl {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,4 @@
|
|||||||
mod builder;
|
|
||||||
|
|
||||||
use super::posix_thread::PosixThreadExt;
|
use super::posix_thread::PosixThreadExt;
|
||||||
use super::process_group::ProcessGroup;
|
|
||||||
use super::process_vm::user_heap::UserHeap;
|
use super::process_vm::user_heap::UserHeap;
|
||||||
use super::process_vm::ProcessVm;
|
use super::process_vm::ProcessVm;
|
||||||
use super::rlimit::ResourceLimits;
|
use super::rlimit::ResourceLimits;
|
||||||
@ -13,7 +10,7 @@ use super::signal::signals::Signal;
|
|||||||
use super::signal::{Pauser, SigEvents, SigEventsFilter};
|
use super::signal::{Pauser, SigEvents, SigEventsFilter};
|
||||||
use super::status::ProcessStatus;
|
use super::status::ProcessStatus;
|
||||||
use super::{process_table, TermStatus};
|
use super::{process_table, TermStatus};
|
||||||
use crate::device::tty::get_n_tty;
|
use crate::device::tty::open_ntty_as_controlling_terminal;
|
||||||
use crate::events::Observer;
|
use crate::events::Observer;
|
||||||
use crate::fs::file_table::FileTable;
|
use crate::fs::file_table::FileTable;
|
||||||
use crate::fs::fs_resolver::FsResolver;
|
use crate::fs::fs_resolver::FsResolver;
|
||||||
@ -23,10 +20,25 @@ use crate::thread::{allocate_tid, Thread};
|
|||||||
use crate::vm::vmar::Vmar;
|
use crate::vm::vmar::Vmar;
|
||||||
use jinux_rights::Full;
|
use jinux_rights::Full;
|
||||||
|
|
||||||
pub use builder::ProcessBuilder;
|
mod builder;
|
||||||
|
mod job_control;
|
||||||
|
mod process_group;
|
||||||
|
mod session;
|
||||||
|
mod terminal;
|
||||||
|
|
||||||
|
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;
|
pub type Pid = u32;
|
||||||
|
/// Process group id.
|
||||||
pub type Pgid = u32;
|
pub type Pgid = u32;
|
||||||
|
/// Session Id.
|
||||||
|
pub type Sid = u32;
|
||||||
|
|
||||||
pub type ExitCode = i32;
|
pub type ExitCode = i32;
|
||||||
|
|
||||||
/// Process stands for a set of threads that shares the same userspace.
|
/// Process stands for a set of threads that shares the same userspace.
|
||||||
@ -46,11 +58,11 @@ pub struct Process {
|
|||||||
/// Process status
|
/// Process status
|
||||||
status: Mutex<ProcessStatus>,
|
status: Mutex<ProcessStatus>,
|
||||||
/// Parent process
|
/// Parent process
|
||||||
parent: Mutex<Weak<Process>>,
|
pub(super) parent: Mutex<Weak<Process>>,
|
||||||
/// Children processes
|
/// Children processes
|
||||||
children: Mutex<BTreeMap<Pid, Arc<Process>>>,
|
children: Mutex<BTreeMap<Pid, Arc<Process>>>,
|
||||||
/// Process group
|
/// Process group
|
||||||
process_group: Mutex<Weak<ProcessGroup>>,
|
pub(super) process_group: Mutex<Weak<ProcessGroup>>,
|
||||||
/// File table
|
/// File table
|
||||||
file_table: Arc<Mutex<FileTable>>,
|
file_table: Arc<Mutex<FileTable>>,
|
||||||
/// FsResolver
|
/// FsResolver
|
||||||
@ -75,7 +87,6 @@ impl Process {
|
|||||||
threads: Vec<Arc<Thread>>,
|
threads: Vec<Arc<Thread>>,
|
||||||
executable_path: String,
|
executable_path: String,
|
||||||
process_vm: ProcessVm,
|
process_vm: ProcessVm,
|
||||||
process_group: Weak<ProcessGroup>,
|
|
||||||
file_table: Arc<Mutex<FileTable>>,
|
file_table: Arc<Mutex<FileTable>>,
|
||||||
fs: Arc<RwLock<FsResolver>>,
|
fs: Arc<RwLock<FsResolver>>,
|
||||||
umask: Arc<RwLock<FileCreationMask>>,
|
umask: Arc<RwLock<FileCreationMask>>,
|
||||||
@ -99,7 +110,7 @@ impl Process {
|
|||||||
status: Mutex::new(ProcessStatus::Uninit),
|
status: Mutex::new(ProcessStatus::Uninit),
|
||||||
parent: Mutex::new(parent),
|
parent: Mutex::new(parent),
|
||||||
children: Mutex::new(BTreeMap::new()),
|
children: Mutex::new(BTreeMap::new()),
|
||||||
process_group: Mutex::new(process_group),
|
process_group: Mutex::new(Weak::new()),
|
||||||
file_table,
|
file_table,
|
||||||
fs,
|
fs,
|
||||||
umask,
|
umask,
|
||||||
@ -118,11 +129,9 @@ impl Process {
|
|||||||
// spawn user process should give an absolute path
|
// spawn user process should give an absolute path
|
||||||
debug_assert!(executable_path.starts_with('/'));
|
debug_assert!(executable_path.starts_with('/'));
|
||||||
let process = Process::create_user_process(executable_path, argv, envp)?;
|
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());
|
open_ntty_as_controlling_terminal(&process)?;
|
||||||
// FIXME: tty should be a parameter?
|
|
||||||
let tty = get_n_tty();
|
|
||||||
tty.set_fg(process_group);
|
|
||||||
process.run();
|
process.run();
|
||||||
Ok(process)
|
Ok(process)
|
||||||
}
|
}
|
||||||
@ -141,7 +150,23 @@ impl Process {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let process = process_builder.build()?;
|
let process = process_builder.build()?;
|
||||||
process_table::add_process(process.clone());
|
|
||||||
|
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)
|
Ok(process)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,30 +205,25 @@ impl Process {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// *********** Parent and child ***********
|
// *********** 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>> {
|
pub fn parent(&self) -> Option<Arc<Process>> {
|
||||||
self.parent.lock().upgrade()
|
self.parent.lock().upgrade()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn children(&self) -> &Mutex<BTreeMap<Pid, Arc<Process>>> {
|
pub(super) fn children(&self) -> &Mutex<BTreeMap<Pid, Arc<Process>>> {
|
||||||
&self.children
|
&self.children
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_child(&self, pid: &Pid) -> bool {
|
||||||
|
self.children.lock().contains_key(pid)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn children_pauser(&self) -> &Arc<Pauser> {
|
pub fn children_pauser(&self) -> &Arc<Pauser> {
|
||||||
&self.children_pauser
|
&self.children_pauser
|
||||||
}
|
}
|
||||||
|
|
||||||
// *********** Process group ***********
|
// *********** Process group & Session***********
|
||||||
|
|
||||||
|
/// Returns the process group id of the process.
|
||||||
pub fn pgid(&self) -> Pgid {
|
pub fn pgid(&self) -> Pgid {
|
||||||
if let Some(process_group) = self.process_group.lock().upgrade() {
|
if let Some(process_group) = self.process_group.lock().upgrade() {
|
||||||
process_group.pgid()
|
process_group.pgid()
|
||||||
@ -212,19 +232,219 @@ impl Process {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set process group for current process. If old process group exists,
|
/// Returns the process group which the process belongs to.
|
||||||
/// 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>> {
|
pub fn process_group(&self) -> Option<Arc<ProcessGroup>> {
|
||||||
self.process_group.lock().upgrade()
|
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();
|
||||||
|
let mut session_inner = session.inner.lock();
|
||||||
|
let mut self_group_mut = self.process_group.lock();
|
||||||
|
let mut group_table_mut = process_table::group_table_mut();
|
||||||
|
let mut session_table_mut = process_table::session_table_mut();
|
||||||
|
|
||||||
|
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 session.
|
||||||
|
session_inner.remove_process(self);
|
||||||
|
|
||||||
|
// Removes the process from old group
|
||||||
|
if let Some(old_group) = self.process_group() {
|
||||||
|
let mut group_inner = old_group.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());
|
||||||
|
|
||||||
|
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();
|
||||||
|
let mut session_inner = session.inner.lock();
|
||||||
|
let mut self_group_mut = self.process_group.lock();
|
||||||
|
let mut group_table_mut = process_table::group_table_mut();
|
||||||
|
|
||||||
|
// Removes the process from old group
|
||||||
|
if let Some(old_group) = self.process_group() {
|
||||||
|
let mut group_inner = old_group.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());
|
||||||
|
*self_group_mut = Arc::downgrade(&new_group);
|
||||||
|
|
||||||
|
group_table_mut.insert(new_group.pgid(), new_group.clone());
|
||||||
|
|
||||||
|
session_inner
|
||||||
|
.process_groups
|
||||||
|
.insert(new_group.pgid(), new_group.clone());
|
||||||
|
let mut new_group_inner = new_group.inner.lock();
|
||||||
|
new_group_inner.session = Arc::downgrade(&session);
|
||||||
|
|
||||||
|
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<()> {
|
||||||
|
let mut self_group_mut = self.process_group.lock();
|
||||||
|
let mut group_table_mut = process_table::group_table_mut();
|
||||||
|
let mut group_inner = group.inner.lock();
|
||||||
|
|
||||||
|
// Removes the process from old group
|
||||||
|
if let Some(old_group) = self.process_group() {
|
||||||
|
let mut group_inner = old_group.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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds the process to the specified group
|
||||||
|
group_inner.processes.insert(self.pid, self.clone());
|
||||||
|
*self_group_mut = Arc::downgrade(group);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// ************** Virtual Memory *************
|
// ************** Virtual Memory *************
|
||||||
|
|
||||||
pub fn vm(&self) -> &ProcessVm {
|
pub fn vm(&self) -> &ProcessVm {
|
||||||
|
92
services/libs/jinux-std/src/process/process/process_group.rs
Normal file
92
services/libs/jinux-std/src/process/process/process_group.rs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
use super::{Pgid, Pid, Process, Session};
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::process::signal::signals::kernel::KernelSignal;
|
||||||
|
use crate::process::signal::signals::user::UserSignal;
|
||||||
|
|
||||||
|
/// `ProcessGroup` represents a set of processes. Each `ProcessGroup` has a unique
|
||||||
|
/// identifier `pgid`.
|
||||||
|
pub struct ProcessGroup {
|
||||||
|
pgid: Pgid,
|
||||||
|
pub(in crate::process) inner: Mutex<Inner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::process) struct Inner {
|
||||||
|
pub(in crate::process) processes: BTreeMap<Pid, Arc<Process>>,
|
||||||
|
pub(in crate::process) leader: Option<Arc<Process>>,
|
||||||
|
pub(in crate::process) session: Weak<Session>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Inner {
|
||||||
|
pub(in crate::process) fn remove_process(&mut self, pid: &Pid) {
|
||||||
|
let Some(process) = self.processes.remove(pid) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(leader) = &self.leader && Arc::ptr_eq(leader, &process) {
|
||||||
|
self.leader = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::process) fn is_empty(&self) -> bool {
|
||||||
|
self.processes.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcessGroup {
|
||||||
|
/// Creates a new process group with one process. The pgid is the same as the process
|
||||||
|
/// id. The process will become the leading process of the new process group.
|
||||||
|
///
|
||||||
|
/// The caller needs to ensure that the process does not belong to any group.
|
||||||
|
pub(super) fn new(process: Arc<Process>) -> Arc<Self> {
|
||||||
|
let pid = process.pid();
|
||||||
|
|
||||||
|
let inner = {
|
||||||
|
let mut processes = BTreeMap::new();
|
||||||
|
processes.insert(pid, process.clone());
|
||||||
|
Inner {
|
||||||
|
processes,
|
||||||
|
leader: Some(process.clone()),
|
||||||
|
session: Weak::new(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Arc::new(ProcessGroup {
|
||||||
|
pgid: pid,
|
||||||
|
inner: Mutex::new(inner),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether self contains a process with `pid`.
|
||||||
|
pub(super) fn contains_process(&self, pid: Pid) -> bool {
|
||||||
|
self.inner.lock().processes.contains_key(&pid)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the process group identifier
|
||||||
|
pub fn pgid(&self) -> Pgid {
|
||||||
|
self.pgid
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends kernel signal to all processes in the group
|
||||||
|
pub fn kernel_signal(&self, signal: KernelSignal) {
|
||||||
|
for process in self.inner.lock().processes.values() {
|
||||||
|
process.enqueue_signal(Box::new(signal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends user signal to all processes in the group
|
||||||
|
pub fn user_signal(&self, signal: UserSignal) {
|
||||||
|
for process in self.inner.lock().processes.values() {
|
||||||
|
process.enqueue_signal(Box::new(signal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the leader process.
|
||||||
|
pub(super) fn leader(&self) -> Option<Arc<Process>> {
|
||||||
|
self.inner.lock().leader.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the session which the group belongs to
|
||||||
|
pub fn session(&self) -> Option<Arc<Session>> {
|
||||||
|
self.inner.lock().session.upgrade()
|
||||||
|
}
|
||||||
|
}
|
125
services/libs/jinux-std/src/process/process/session.rs
Normal file
125
services/libs/jinux-std/src/process/process/session.rs
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
use super::{Pgid, Process, ProcessGroup, Sid, Terminal};
|
||||||
|
|
||||||
|
/// A `Session` is a collection of related process groups. Each session has a
|
||||||
|
/// unique identifier `sid`. Process groups and sessions form a two-level
|
||||||
|
/// hierarchical relationship between processes.
|
||||||
|
///
|
||||||
|
/// **Leader**: A *session leader* is the process that creates a new session and whose process
|
||||||
|
/// ID becomes the session ID.
|
||||||
|
///
|
||||||
|
/// **Controlling terminal**: The terminal can be used to manage all processes in the session. The
|
||||||
|
/// controlling terminal is established when the session leader first opens a terminal.
|
||||||
|
pub struct Session {
|
||||||
|
sid: Sid,
|
||||||
|
pub(in crate::process) inner: Mutex<Inner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::process) struct Inner {
|
||||||
|
pub(in crate::process) process_groups: BTreeMap<Pgid, Arc<ProcessGroup>>,
|
||||||
|
pub(in crate::process) leader: Option<Arc<Process>>,
|
||||||
|
pub(in crate::process) terminal: Option<Arc<dyn Terminal>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Inner {
|
||||||
|
pub(in crate::process) fn is_empty(&self) -> bool {
|
||||||
|
self.process_groups.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::process) fn remove_process(&mut self, process: &Arc<Process>) {
|
||||||
|
if let Some(leader) = &self.leader && Arc::ptr_eq(leader, process) {
|
||||||
|
self.leader = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::process) fn remove_process_group(&mut self, pgid: &Pgid) {
|
||||||
|
self.process_groups.remove(pgid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Session {
|
||||||
|
/// Creates a new session for the process group. The process group becomes the member of
|
||||||
|
/// the new session.
|
||||||
|
///
|
||||||
|
/// The caller needs to ensure that the group does not belong to any session, and the caller
|
||||||
|
/// should set the leader process after creating the session.
|
||||||
|
pub(super) fn new(group: Arc<ProcessGroup>) -> Arc<Self> {
|
||||||
|
let sid = group.pgid();
|
||||||
|
let inner = {
|
||||||
|
let mut process_groups = BTreeMap::new();
|
||||||
|
process_groups.insert(group.pgid(), group);
|
||||||
|
|
||||||
|
Inner {
|
||||||
|
process_groups,
|
||||||
|
leader: None,
|
||||||
|
terminal: None,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Arc::new(Self {
|
||||||
|
sid,
|
||||||
|
inner: Mutex::new(inner),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the session id
|
||||||
|
pub fn sid(&self) -> Sid {
|
||||||
|
self.sid
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the leader process.
|
||||||
|
pub(super) fn leader(&self) -> Option<Arc<Process>> {
|
||||||
|
self.inner.lock().leader.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether `self` contains the `process_group`
|
||||||
|
pub(super) fn contains_process_group(
|
||||||
|
self: &Arc<Self>,
|
||||||
|
process_group: &Arc<ProcessGroup>,
|
||||||
|
) -> bool {
|
||||||
|
self.inner
|
||||||
|
.lock()
|
||||||
|
.process_groups
|
||||||
|
.contains_key(&process_group.pgid())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets terminal as the controlling terminal of the session.
|
||||||
|
///
|
||||||
|
/// If the session already has controlling terminal, this method will return `Err(EPERM)`.
|
||||||
|
pub fn set_terminal<F: Fn() -> Result<Arc<dyn Terminal>>>(&self, terminal: F) -> Result<()> {
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
|
||||||
|
if inner.terminal.is_some() {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::EPERM,
|
||||||
|
"current session already has controlling terminal"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let terminal = terminal()?;
|
||||||
|
inner.terminal = Some(terminal);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Releases the controlling terminal of the session.
|
||||||
|
///
|
||||||
|
/// If the session does not have controlling terminal, this method will return `ENOTTY`.
|
||||||
|
pub fn release_terminal<F: Fn() -> Result<()>>(&self, release_session: F) -> Result<()> {
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
if inner.terminal.is_none() {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::ENOTTY,
|
||||||
|
"current session does not has controlling terminal"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
release_session()?;
|
||||||
|
inner.terminal = None;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the controlling terminal of `self`.
|
||||||
|
pub fn terminal(&self) -> Option<Arc<dyn Terminal>> {
|
||||||
|
self.inner.lock().terminal.clone()
|
||||||
|
}
|
||||||
|
}
|
104
services/libs/jinux-std/src/process/process/terminal.rs
Normal file
104
services/libs/jinux-std/src/process/process/terminal.rs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
use crate::fs::inode_handle::FileIo;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::process::{process_table, Pgid, ProcessGroup};
|
||||||
|
|
||||||
|
use super::JobControl;
|
||||||
|
|
||||||
|
/// A termial is used to interact with system. A terminal can support the shell
|
||||||
|
/// job control.
|
||||||
|
///
|
||||||
|
/// We currently support two kinds of terminal, the tty and pty.
|
||||||
|
pub trait Terminal: Send + Sync + FileIo {
|
||||||
|
// *************** Foreground ***************
|
||||||
|
|
||||||
|
/// Returns the foreground process group
|
||||||
|
fn foreground(&self) -> Option<Arc<ProcessGroup>> {
|
||||||
|
self.job_control().foreground()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the foreground process group of this terminal.
|
||||||
|
///
|
||||||
|
/// If the terminal is not controlling terminal, this method returns `ENOTTY`.
|
||||||
|
///
|
||||||
|
/// # Panic
|
||||||
|
///
|
||||||
|
/// This method should be called in process context.
|
||||||
|
fn set_foreground(&self, pgid: &Pgid) -> Result<()> {
|
||||||
|
if !self.is_controlling_terminal() {
|
||||||
|
return_errno_with_message!(Errno::ENOTTY, "self is not controlling terminal");
|
||||||
|
}
|
||||||
|
|
||||||
|
let foreground = process_table::get_process_group(pgid);
|
||||||
|
|
||||||
|
self.job_control().set_foreground(foreground.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
// *************** Session and controlling terminal ***************
|
||||||
|
|
||||||
|
/// Returns whether the terminal is the controlling terminal of current process.
|
||||||
|
///
|
||||||
|
/// # Panic
|
||||||
|
///
|
||||||
|
/// This method should be called in process context.
|
||||||
|
fn is_controlling_terminal(&self) -> bool {
|
||||||
|
let session = current!().session().unwrap();
|
||||||
|
let Some(terminal) = session.terminal() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let arc_self = self.arc_self();
|
||||||
|
Arc::ptr_eq(&terminal, &arc_self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the terminal as the controlling terminal of the session of current process.
|
||||||
|
///
|
||||||
|
/// If self is not session leader, or the terminal is controlling terminal of other session,
|
||||||
|
/// or the session already has controlling terminal, this method returns `EPERM`.
|
||||||
|
///
|
||||||
|
/// # Panic
|
||||||
|
///
|
||||||
|
/// This method should only be called in process context.
|
||||||
|
fn set_current_session(&self) -> Result<()> {
|
||||||
|
if !current!().is_session_leader() {
|
||||||
|
return_errno_with_message!(Errno::EPERM, "current process is not session leader");
|
||||||
|
}
|
||||||
|
|
||||||
|
let terminal = || {
|
||||||
|
self.job_control().set_current_session()?;
|
||||||
|
Ok(self.arc_self())
|
||||||
|
};
|
||||||
|
|
||||||
|
let session = current!().session().unwrap();
|
||||||
|
session.set_terminal(terminal)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Releases the terminal from the session of current process if the terminal is the controlling
|
||||||
|
/// terminal of the session.
|
||||||
|
///
|
||||||
|
/// If the terminal is not the controlling terminal of the session, this method will return `ENOTTY`.
|
||||||
|
///
|
||||||
|
/// # Panic
|
||||||
|
///
|
||||||
|
/// This method should only be called in process context.
|
||||||
|
fn release_current_session(&self) -> Result<()> {
|
||||||
|
if !self.is_controlling_terminal() {
|
||||||
|
return_errno_with_message!(Errno::ENOTTY, "release wrong tty");
|
||||||
|
}
|
||||||
|
|
||||||
|
let current = current!();
|
||||||
|
if !current.is_session_leader() {
|
||||||
|
warn!("TODO: release tty for process that is not session leader");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let release_session = || self.job_control().release_current_session();
|
||||||
|
|
||||||
|
let session = current.session().unwrap();
|
||||||
|
session.release_terminal(release_session)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the job control of the terminal.
|
||||||
|
fn job_control(&self) -> &JobControl;
|
||||||
|
|
||||||
|
fn arc_self(&self) -> Arc<dyn Terminal>;
|
||||||
|
}
|
@ -1,85 +0,0 @@
|
|||||||
use super::{
|
|
||||||
process_table,
|
|
||||||
signal::signals::{kernel::KernelSignal, user::UserSignal},
|
|
||||||
Pgid, Pid, Process,
|
|
||||||
};
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
pub struct ProcessGroup {
|
|
||||||
inner: Mutex<ProcessGroupInner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ProcessGroupInner {
|
|
||||||
pgid: Pgid,
|
|
||||||
processes: BTreeMap<Pid, Arc<Process>>,
|
|
||||||
leader_process: Option<Arc<Process>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProcessGroup {
|
|
||||||
fn default() -> Self {
|
|
||||||
ProcessGroup {
|
|
||||||
inner: Mutex::new(ProcessGroupInner {
|
|
||||||
pgid: 0,
|
|
||||||
processes: BTreeMap::new(),
|
|
||||||
leader_process: None,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(process: Arc<Process>) -> Self {
|
|
||||||
let process_group = ProcessGroup::default();
|
|
||||||
let pid = process.pid();
|
|
||||||
process_group.set_pgid(pid);
|
|
||||||
process_group.add_process(process.clone());
|
|
||||||
process_group.set_leader_process(process);
|
|
||||||
process_group
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_pgid(&self, pgid: Pgid) {
|
|
||||||
self.inner.lock().pgid = pgid;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_leader_process(&self, leader_process: Arc<Process>) {
|
|
||||||
self.inner.lock().leader_process = Some(leader_process);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_process(&self, process: Arc<Process>) {
|
|
||||||
self.inner.lock().processes.insert(process.pid(), process);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains_process(&self, pid: Pid) -> bool {
|
|
||||||
self.inner.lock().processes.contains_key(&pid)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// remove a process from this process group.
|
|
||||||
/// If this group contains no processes now, the group itself will be deleted from global table.
|
|
||||||
pub fn remove_process(&self, pid: Pid) {
|
|
||||||
let mut inner_lock = self.inner.lock();
|
|
||||||
inner_lock.processes.remove(&pid);
|
|
||||||
let len = inner_lock.processes.len();
|
|
||||||
let pgid = inner_lock.pgid;
|
|
||||||
// if self contains no process, remove self from table
|
|
||||||
if len == 0 {
|
|
||||||
// this must be the last statement
|
|
||||||
process_table::remove_process_group(pgid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pgid(&self) -> Pgid {
|
|
||||||
self.inner.lock().pgid
|
|
||||||
}
|
|
||||||
|
|
||||||
/// send kernel signal to all processes in the group
|
|
||||||
pub fn kernel_signal(&self, signal: KernelSignal) {
|
|
||||||
for process in self.inner.lock().processes.values() {
|
|
||||||
process.enqueue_signal(Box::new(signal));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// send user signal to all processes in the group
|
|
||||||
pub fn user_signal(&self, signal: UserSignal) {
|
|
||||||
for process in self.inner.lock().processes.values() {
|
|
||||||
process.enqueue_signal(Box::new(signal));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,35 +5,25 @@
|
|||||||
use crate::events::{Events, Observer, Subject};
|
use crate::events::{Events, Observer, Subject};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use super::{process_group::ProcessGroup, Pgid, Pid, Process};
|
use super::{Pgid, Pid, Process, ProcessGroup, Session, Sid};
|
||||||
|
|
||||||
lazy_static! {
|
static PROCESS_TABLE: Mutex<BTreeMap<Pid, Arc<Process>>> = Mutex::new(BTreeMap::new());
|
||||||
static ref PROCESS_TABLE: Mutex<BTreeMap<Pid, Arc<Process>>> = Mutex::new(BTreeMap::new());
|
static PROCESS_GROUP_TABLE: Mutex<BTreeMap<Pgid, Arc<ProcessGroup>>> = Mutex::new(BTreeMap::new());
|
||||||
static ref PROCESS_GROUP_TABLE: Mutex<BTreeMap<Pgid, Arc<ProcessGroup>>> =
|
static PROCESS_TABLE_SUBJECT: Subject<PidEvent> = Subject::new();
|
||||||
Mutex::new(BTreeMap::new());
|
static SESSION_TABLE: Mutex<BTreeMap<Sid, Arc<Session>>> = Mutex::new(BTreeMap::new());
|
||||||
static ref PROCESS_TABLE_SUBJECT: Subject<PidEvent> = Subject::new();
|
|
||||||
|
// ************ Process *************
|
||||||
|
|
||||||
|
/// Gets a process with pid
|
||||||
|
pub fn get_process(pid: &Pid) -> Option<Arc<Process>> {
|
||||||
|
PROCESS_TABLE.lock().get(pid).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// add a process to global table
|
pub(super) fn process_table_mut() -> MutexGuard<'static, BTreeMap<Pid, Arc<Process>>> {
|
||||||
pub fn add_process(process: Arc<Process>) {
|
PROCESS_TABLE.lock()
|
||||||
let pid = process.pid();
|
|
||||||
PROCESS_TABLE.lock().insert(pid, process);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// remove a process from global table
|
/// Gets all processes
|
||||||
pub fn remove_process(pid: Pid) {
|
|
||||||
PROCESS_TABLE.lock().remove(&pid);
|
|
||||||
|
|
||||||
let events = PidEvent::Exit(pid);
|
|
||||||
PROCESS_TABLE_SUBJECT.notify_observers(&events);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get a process with pid
|
|
||||||
pub fn pid_to_process(pid: Pid) -> Option<Arc<Process>> {
|
|
||||||
PROCESS_TABLE.lock().get(&pid).cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get all processes
|
|
||||||
pub fn get_all_processes() -> Vec<Arc<Process>> {
|
pub fn get_all_processes() -> Vec<Arc<Process>> {
|
||||||
PROCESS_TABLE
|
PROCESS_TABLE
|
||||||
.lock()
|
.lock()
|
||||||
@ -42,26 +32,41 @@ pub fn get_all_processes() -> Vec<Arc<Process>> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// add process group to global table
|
// ************ Process Group *************
|
||||||
pub fn add_process_group(process_group: Arc<ProcessGroup>) {
|
|
||||||
let pgid = process_group.pgid();
|
/// Gets a process group with `pgid`
|
||||||
PROCESS_GROUP_TABLE.lock().insert(pgid, process_group);
|
pub fn get_process_group(pgid: &Pgid) -> Option<Arc<ProcessGroup>> {
|
||||||
|
PROCESS_GROUP_TABLE.lock().get(pgid).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// remove process group from global table
|
/// Returns whether process table contains process group with pgid
|
||||||
pub fn remove_process_group(pgid: Pgid) {
|
pub fn contain_process_group(pgid: &Pgid) -> bool {
|
||||||
PROCESS_GROUP_TABLE.lock().remove(&pgid);
|
PROCESS_GROUP_TABLE.lock().contains_key(pgid)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get a process group with pgid
|
pub(super) fn group_table_mut() -> MutexGuard<'static, BTreeMap<Pgid, Arc<ProcessGroup>>> {
|
||||||
pub fn pgid_to_process_group(pgid: Pgid) -> Option<Arc<ProcessGroup>> {
|
PROCESS_GROUP_TABLE.lock()
|
||||||
PROCESS_GROUP_TABLE.lock().get(&pgid).cloned()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ************ Session *************
|
||||||
|
|
||||||
|
/// Gets a session with `sid`.
|
||||||
|
pub fn get_session(sid: &Sid) -> Option<Arc<Session>> {
|
||||||
|
SESSION_TABLE.lock().get(sid).map(Arc::clone)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn session_table_mut() -> MutexGuard<'static, BTreeMap<Sid, Arc<Session>>> {
|
||||||
|
SESSION_TABLE.lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ************ Observer *************
|
||||||
|
|
||||||
|
/// Registers an observer which watches `PidEvent`.
|
||||||
pub fn register_observer(observer: Weak<dyn Observer<PidEvent>>) {
|
pub fn register_observer(observer: Weak<dyn Observer<PidEvent>>) {
|
||||||
PROCESS_TABLE_SUBJECT.register_observer(observer, ());
|
PROCESS_TABLE_SUBJECT.register_observer(observer, ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unregisters an observer which watches `PidEvent`.
|
||||||
pub fn unregister_observer(observer: &Weak<dyn Observer<PidEvent>>) {
|
pub fn unregister_observer(observer: &Weak<dyn Observer<PidEvent>>) {
|
||||||
PROCESS_TABLE_SUBJECT.unregister_observer(observer);
|
PROCESS_TABLE_SUBJECT.unregister_observer(observer);
|
||||||
}
|
}
|
||||||
|
@ -81,8 +81,11 @@ impl SigQueues {
|
|||||||
// POSIX leaves unspecified which to deliver first if there are multiple
|
// POSIX leaves unspecified which to deliver first if there are multiple
|
||||||
// pending standard signals. So we are free to define our own. The
|
// pending standard signals. So we are free to define our own. The
|
||||||
// principle is to give more urgent signals higher priority (like SIGKILL).
|
// principle is to give more urgent signals higher priority (like SIGKILL).
|
||||||
|
|
||||||
|
// FIXME: the gvisor pty_test JobControlTest::ReleaseTTY requires that
|
||||||
|
// the SIGHUP signal should be handled before SIGCONT.
|
||||||
const ORDERED_STD_SIGS: [SigNum; COUNT_STD_SIGS] = [
|
const ORDERED_STD_SIGS: [SigNum; COUNT_STD_SIGS] = [
|
||||||
SIGKILL, SIGTERM, SIGSTOP, SIGCONT, SIGSEGV, SIGILL, SIGHUP, SIGINT, SIGQUIT, SIGTRAP,
|
SIGKILL, SIGTERM, SIGSTOP, SIGSEGV, SIGILL, SIGHUP, SIGCONT, SIGINT, SIGQUIT, SIGTRAP,
|
||||||
SIGABRT, SIGBUS, SIGFPE, SIGUSR1, SIGUSR2, SIGPIPE, SIGALRM, SIGSTKFLT, SIGCHLD,
|
SIGABRT, SIGBUS, SIGFPE, SIGUSR1, SIGUSR2, SIGPIPE, SIGALRM, SIGSTKFLT, SIGCHLD,
|
||||||
SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM, SIGPROF, SIGWINCH,
|
SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM, SIGPROF, SIGWINCH,
|
||||||
SIGIO, SIGPWR, SIGSYS,
|
SIGIO, SIGPWR, SIGSYS,
|
||||||
|
@ -80,9 +80,30 @@ fn reap_zombie_child(process: &Process, pid: Pid) -> u32 {
|
|||||||
for thread in &*child_process.threads().lock() {
|
for thread in &*child_process.threads().lock() {
|
||||||
thread_table::remove_thread(thread.tid());
|
thread_table::remove_thread(thread.tid());
|
||||||
}
|
}
|
||||||
process_table::remove_process(child_process.pid());
|
|
||||||
if let Some(process_group) = child_process.process_group() {
|
let mut process_table_mut = process_table::process_table_mut();
|
||||||
process_group.remove_process(child_process.pid());
|
let mut group_table_mut = process_table::group_table_mut();
|
||||||
|
let mut session_table_mut = process_table::session_table_mut();
|
||||||
|
let mut child_group_mut = child_process.process_group.lock();
|
||||||
|
|
||||||
|
let process_group = child_process.process_group().unwrap();
|
||||||
|
let mut group_inner = process_group.inner.lock();
|
||||||
|
let session = group_inner.session.upgrade().unwrap();
|
||||||
|
let mut session_inner = session.inner.lock();
|
||||||
|
|
||||||
|
group_inner.remove_process(&child_process.pid());
|
||||||
|
session_inner.remove_process(&child_process);
|
||||||
|
*child_group_mut = Weak::new();
|
||||||
|
|
||||||
|
if group_inner.is_empty() {
|
||||||
|
group_table_mut.remove(&process_group.pgid());
|
||||||
|
session_inner.remove_process_group(&process_group.pgid());
|
||||||
|
|
||||||
|
if session_inner.is_empty() {
|
||||||
|
session_table_mut.remove(&session.sid());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process_table_mut.remove(&child_process.pid());
|
||||||
child_process.exit_code().unwrap()
|
child_process.exit_code().unwrap()
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user