Add basic structures for session, terminal and job control

This commit is contained in:
Jianfeng Jiang
2023-11-16 17:21:15 +08:00
committed by Tate, Hongliang Tian
parent 2edbe1f725
commit 9040fb54ea
13 changed files with 814 additions and 195 deletions

View File

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

View File

@ -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(&current) { if !is_init_process(&current) {
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 {

View File

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

View File

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

View 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()
}
}

View File

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

View 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()
}
}

View 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()
}
}

View 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>;
}

View File

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

View File

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

View File

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

View File

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