Refactor session & tty code

This commit is contained in:
Jianfeng Jiang
2023-11-24 08:06:56 +00:00
committed by Tate, Hongliang Tian
parent 3bde0f6bb7
commit ce5730287e
15 changed files with 211 additions and 199 deletions

View File

@ -2,6 +2,7 @@ use alloc::format;
use ringbuf::{ring_buffer::RbBase, HeapRb, Rb}; use ringbuf::{ring_buffer::RbBase, HeapRb, Rb};
use crate::device::tty::line_discipline::LineDiscipline; use crate::device::tty::line_discipline::LineDiscipline;
use crate::device::tty::new_job_control_and_ldisc;
use crate::events::IoEvents; use crate::events::IoEvents;
use crate::fs::device::{Device, DeviceId, DeviceType}; use crate::fs::device::{Device, DeviceId, DeviceType};
use crate::fs::devpts::DevPts; use crate::fs::devpts::DevPts;
@ -9,7 +10,6 @@ use crate::fs::fs_resolver::FsPath;
use crate::fs::inode_handle::FileIo; use crate::fs::inode_handle::FileIo;
use crate::fs::utils::{AccessMode, Inode, InodeMode, IoctlCmd}; use crate::fs::utils::{AccessMode, Inode, InodeMode, IoctlCmd};
use crate::prelude::*; use crate::prelude::*;
use crate::process::signal::signals::kernel::KernelSignal;
use crate::process::signal::{Pollee, Poller}; use crate::process::signal::{Pollee, Poller};
use crate::process::{JobControl, Terminal}; use crate::process::{JobControl, Terminal};
use crate::util::{read_val_from_user, write_val_to_user}; use crate::util::{read_val_from_user, write_val_to_user};
@ -25,7 +25,7 @@ pub struct PtyMaster {
index: u32, index: u32,
output: Arc<LineDiscipline>, output: Arc<LineDiscipline>,
input: SpinLock<HeapRb<u8>>, input: SpinLock<HeapRb<u8>>,
job_control: JobControl, job_control: Arc<JobControl>,
/// The state of input buffer /// The state of input buffer
pollee: Pollee, pollee: Pollee,
weak_self: Weak<Self>, weak_self: Weak<Self>,
@ -33,12 +33,13 @@ pub struct PtyMaster {
impl PtyMaster { impl PtyMaster {
pub fn new(ptmx: Arc<dyn Inode>, index: u32) -> Arc<Self> { pub fn new(ptmx: Arc<dyn Inode>, index: u32) -> Arc<Self> {
Arc::new_cyclic(|weak_ref| PtyMaster { let (job_control, ldisc) = new_job_control_and_ldisc();
Arc::new_cyclic(move |weak_ref| PtyMaster {
ptmx, ptmx,
index, index,
output: LineDiscipline::new(), output: ldisc,
input: SpinLock::new(HeapRb::new(BUFFER_CAPACITY)), input: SpinLock::new(HeapRb::new(BUFFER_CAPACITY)),
job_control: JobControl::new(), job_control,
pollee: Pollee::new(IoEvents::OUT), pollee: Pollee::new(IoEvents::OUT),
weak_self: weak_ref.clone(), weak_self: weak_ref.clone(),
}) })
@ -123,22 +124,9 @@ impl FileIo for PtyMaster {
} }
fn write(&self, buf: &[u8]) -> Result<usize> { fn write(&self, buf: &[u8]) -> Result<usize> {
let may_send_signal = || {
let Some(foreground) = self.foreground() else {
return None;
};
let send_signal = move |signal: KernelSignal| {
foreground.kernel_signal(signal);
};
Some(Arc::new(send_signal) as Arc<dyn Fn(KernelSignal) + Send + Sync>)
};
let mut input = self.input.lock(); let mut input = self.input.lock();
for character in buf { for character in buf {
self.output self.output.push_char(*character, |content| {
.push_char(*character, may_send_signal, |content| {
for byte in content.as_bytes() { for byte in content.as_bytes() {
input.push_overwrite(*byte); input.push_overwrite(*byte);
} }
@ -332,9 +320,8 @@ impl Terminal for PtySlave {
impl FileIo for PtySlave { impl FileIo for PtySlave {
fn read(&self, buf: &mut [u8]) -> Result<usize> { fn read(&self, buf: &mut [u8]) -> Result<usize> {
self.master() self.job_control.wait_until_in_foreground()?;
.output self.master().output.read(buf)
.read(buf, || self.job_control.current_belongs_to_foreground())
} }
fn write(&self, buf: &[u8]) -> Result<usize> { fn write(&self, buf: &[u8]) -> Result<usize> {

View File

@ -34,14 +34,14 @@ impl Device for TtyDevice {
impl FileIo for TtyDevice { impl FileIo for TtyDevice {
fn read(&self, buf: &mut [u8]) -> Result<usize> { fn read(&self, buf: &mut [u8]) -> Result<usize> {
unreachable!() return_errno_with_message!(Errno::EINVAL, "cannot read tty device");
} }
fn write(&self, buf: &[u8]) -> Result<usize> { fn write(&self, buf: &[u8]) -> Result<usize> {
unreachable!() return_errno_with_message!(Errno::EINVAL, "cannot write tty device");
} }
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents { fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
unreachable!() IoEvents::empty()
} }
} }

View File

@ -16,6 +16,8 @@ use super::termio::{KernelTermios, WinSize, CC_C_CHAR};
const BUFFER_CAPACITY: usize = 4096; const BUFFER_CAPACITY: usize = 4096;
pub type LdiscSignalSender = Arc<dyn Fn(KernelSignal) + Send + Sync + 'static>;
pub struct LineDiscipline { pub struct LineDiscipline {
/// current line /// current line
current_line: SpinLock<CurrentLine>, current_line: SpinLock<CurrentLine>,
@ -27,6 +29,8 @@ pub struct LineDiscipline {
winsize: SpinLock<WinSize>, winsize: SpinLock<WinSize>,
/// Pollee /// Pollee
pollee: Pollee, pollee: Pollee,
/// Used to send signal for foreground processes, when some char comes.
send_signal: LdiscSignalSender,
/// work item /// work item
work_item: Arc<WorkItem>, work_item: Arc<WorkItem>,
/// Parameters used by a work item. /// Parameters used by a work item.
@ -71,8 +75,8 @@ impl CurrentLine {
impl LineDiscipline { impl LineDiscipline {
/// Create a new line discipline /// Create a new line discipline
pub fn new() -> Arc<Self> { pub fn new(send_signal: LdiscSignalSender) -> Arc<Self> {
Arc::new_cyclic(|line_ref: &Weak<LineDiscipline>| { Arc::new_cyclic(move |line_ref: &Weak<LineDiscipline>| {
let line_discipline = line_ref.clone(); let line_discipline = line_ref.clone();
let work_item = Arc::new(WorkItem::new(Box::new(move || { let work_item = Arc::new(WorkItem::new(Box::new(move || {
if let Some(line_discipline) = line_discipline.upgrade() { if let Some(line_discipline) = line_discipline.upgrade() {
@ -85,6 +89,7 @@ impl LineDiscipline {
termios: SpinLock::new(KernelTermios::default()), termios: SpinLock::new(KernelTermios::default()),
winsize: SpinLock::new(WinSize::default()), winsize: SpinLock::new(WinSize::default()),
pollee: Pollee::new(IoEvents::empty()), pollee: Pollee::new(IoEvents::empty()),
send_signal,
work_item, work_item,
work_item_para: Arc::new(SpinLock::new(LineDisciplineWorkPara::new())), work_item_para: Arc::new(SpinLock::new(LineDisciplineWorkPara::new())),
} }
@ -92,15 +97,7 @@ impl LineDiscipline {
} }
/// Push char to line discipline. /// Push char to line discipline.
pub fn push_char< pub fn push_char<F2: FnMut(&str)>(&self, ch: u8, echo_callback: F2) {
F1: Fn() -> Option<Arc<dyn Fn(KernelSignal) + Send + Sync>>,
F2: FnMut(&str),
>(
&self,
ch: u8,
may_send_signal: F1,
echo_callback: F2,
) {
let termios = self.termios.lock_irq_disabled(); let termios = self.termios.lock_irq_disabled();
let ch = if termios.contains_icrnl() && ch == b'\r' { let ch = if termios.contains_icrnl() && ch == b'\r' {
@ -109,7 +106,7 @@ impl LineDiscipline {
ch ch
}; };
if self.may_send_signal(&termios, ch, may_send_signal) { if self.may_send_signal(&termios, ch) {
// The char is already dealt with, so just return // The char is already dealt with, so just return
return; return;
} }
@ -160,27 +157,18 @@ impl LineDiscipline {
self.update_readable_state_deferred(); self.update_readable_state_deferred();
} }
fn may_send_signal<F: Fn() -> Option<Arc<dyn Fn(KernelSignal) + Send + Sync>>>( fn may_send_signal(&self, termios: &KernelTermios, ch: u8) -> bool {
&self,
termios: &KernelTermios,
ch: u8,
may_send_signal: F,
) -> bool {
if !termios.is_canonical_mode() || !termios.contains_isig() { if !termios.is_canonical_mode() || !termios.contains_isig() {
return false; return false;
} }
let Some(send_signal) = may_send_signal() else {
return false;
};
let signal = match ch { let signal = match ch {
ch if ch == *termios.get_special_char(CC_C_CHAR::VINTR) => KernelSignal::new(SIGINT), ch if ch == *termios.get_special_char(CC_C_CHAR::VINTR) => KernelSignal::new(SIGINT),
ch if ch == *termios.get_special_char(CC_C_CHAR::VQUIT) => KernelSignal::new(SIGQUIT), ch if ch == *termios.get_special_char(CC_C_CHAR::VQUIT) => KernelSignal::new(SIGQUIT),
_ => return false, _ => return false,
}; };
// `kernel_signal()` may cause sleep, so only construct parameters here. // `kernel_signal()` may cause sleep, so only construct parameters here.
self.work_item_para.lock_irq_disabled().kernel_signal = Some((send_signal, signal)); self.work_item_para.lock_irq_disabled().kernel_signal = Some(signal);
true true
} }
@ -207,10 +195,8 @@ impl LineDiscipline {
/// include all operations that may cause sleep, and processes by a work queue. /// include all operations that may cause sleep, and processes by a work queue.
fn update_readable_state_after(&self) { fn update_readable_state_after(&self) {
if let Some((send_signal, signal)) = if let Some(signal) = self.work_item_para.lock_irq_disabled().kernel_signal.take() {
self.work_item_para.lock_irq_disabled().kernel_signal.take() (self.send_signal)(signal);
{
send_signal(signal);
}; };
if let Some(pollee_type) = self.work_item_para.lock_irq_disabled().pollee_type.take() { if let Some(pollee_type) = self.work_item_para.lock_irq_disabled().pollee_type.take() {
match pollee_type { match pollee_type {
@ -243,23 +229,14 @@ impl LineDiscipline {
} }
} }
pub fn read<F: Fn() -> bool>(&self, buf: &mut [u8], belongs_to_foreground: F) -> Result<usize> { pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
let mut poller = None;
loop { loop {
if !belongs_to_foreground() {
init_poller(&mut poller);
if self.poll(IoEvents::IN, poller.as_ref()).is_empty() {
poller.as_ref().unwrap().wait()?
}
continue;
}
let res = self.try_read(buf); let res = self.try_read(buf);
match res { match res {
Ok(len) => return Ok(len), Ok(len) => return Ok(len),
Err(e) if e.error() != Errno::EAGAIN => return Err(e), Err(e) if e.error() != Errno::EAGAIN => return Err(e),
Err(_) => { Err(_) => {
init_poller(&mut poller); let poller = Some(Poller::new());
if self.poll(IoEvents::IN, poller.as_ref()).is_empty() { if self.poll(IoEvents::IN, poller.as_ref()).is_empty() {
poller.as_ref().unwrap().wait()? poller.as_ref().unwrap().wait()?
} }
@ -427,14 +404,6 @@ fn get_printable_char(ctrl_char: u8) -> u8 {
ctrl_char + b'A' - 1 ctrl_char + b'A' - 1
} }
fn init_poller(poller: &mut Option<Poller>) {
if poller.is_some() {
return;
}
*poller = Some(Poller::new());
}
enum PolleeType { enum PolleeType {
Add, Add,
Del, Del,
@ -442,7 +411,7 @@ enum PolleeType {
struct LineDisciplineWorkPara { struct LineDisciplineWorkPara {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
kernel_signal: Option<(Arc<dyn Fn(KernelSignal) + Send + Sync>, KernelSignal)>, kernel_signal: Option<KernelSignal>,
pollee_type: Option<PolleeType>, pollee_type: Option<PolleeType>,
} }

View File

@ -33,7 +33,7 @@ pub struct Tty {
name: CString, name: CString,
/// line discipline /// line discipline
ldisc: Arc<LineDiscipline>, ldisc: Arc<LineDiscipline>,
job_control: JobControl, job_control: Arc<JobControl>,
/// driver /// driver
driver: SpinLock<Weak<TtyDriver>>, driver: SpinLock<Weak<TtyDriver>>,
weak_self: Weak<Self>, weak_self: Weak<Self>,
@ -41,10 +41,11 @@ pub struct Tty {
impl Tty { impl Tty {
pub fn new(name: CString) -> Arc<Self> { pub fn new(name: CString) -> Arc<Self> {
Arc::new_cyclic(|weak_ref| Tty { let (job_control, ldisc) = new_job_control_and_ldisc();
Arc::new_cyclic(move |weak_ref| Tty {
name, name,
ldisc: LineDiscipline::new(), ldisc,
job_control: JobControl::new(), job_control,
driver: SpinLock::new(Weak::new()), driver: SpinLock::new(Weak::new()),
weak_self: weak_ref.clone(), weak_self: weak_ref.clone(),
}) })
@ -55,27 +56,14 @@ impl Tty {
} }
pub fn receive_char(&self, ch: u8) { pub fn receive_char(&self, ch: u8) {
let may_send_signal = || { self.ldisc.push_char(ch, |content| print!("{}", content));
let Some(foreground) = self.foreground() else {
return None;
};
let send_signal = move |signal: KernelSignal| {
foreground.kernel_signal(signal);
};
Some(Arc::new(send_signal) as Arc<dyn Fn(KernelSignal) + Send + Sync>)
};
self.ldisc
.push_char(ch, may_send_signal, |content| print!("{}", content));
} }
} }
impl FileIo for Tty { impl FileIo for Tty {
fn read(&self, buf: &mut [u8]) -> Result<usize> { fn read(&self, buf: &mut [u8]) -> Result<usize> {
self.ldisc self.job_control.wait_until_in_foreground()?;
.read(buf, || self.job_control.current_belongs_to_foreground()) self.ldisc.read(buf)
} }
fn write(&self, buf: &[u8]) -> Result<usize> { fn write(&self, buf: &[u8]) -> Result<usize> {
@ -187,6 +175,25 @@ impl Device for Tty {
} }
} }
pub fn new_job_control_and_ldisc() -> (Arc<JobControl>, Arc<LineDiscipline>) {
let job_control = Arc::new(JobControl::new());
let send_signal = {
let cloned_job_control = job_control.clone();
move |signal: KernelSignal| {
let Some(foreground) = cloned_job_control.foreground() else {
return;
};
foreground.broadcast_signal(signal);
}
};
let ldisc = LineDiscipline::new(Arc::new(send_signal));
(job_control, ldisc)
}
pub fn get_n_tty() -> &'static Arc<Tty> { pub fn get_n_tty() -> &'static Arc<Tty> {
N_TTY.get().unwrap() N_TTY.get().unwrap()
} }
@ -198,8 +205,13 @@ pub fn open_ntty_as_controlling_terminal(process: &Process) -> Result<()> {
let session = &process.session().unwrap(); let session = &process.session().unwrap();
let process_group = process.process_group().unwrap(); let process_group = process.process_group().unwrap();
session.set_terminal(|| {
tty.job_control.set_session(session); tty.job_control.set_session(session);
Ok(tty.clone())
})?;
tty.job_control.set_foreground(Some(&process_group))?; tty.job_control.set_foreground(Some(&process_group))?;
session.set_terminal(|| Ok(tty.clone())) Ok(())
} }

View File

@ -148,14 +148,14 @@ impl Device for Inner {
impl FileIo for Inner { impl FileIo for Inner {
fn read(&self, buf: &mut [u8]) -> Result<usize> { fn read(&self, buf: &mut [u8]) -> Result<usize> {
unreachable!() return_errno_with_message!(Errno::EINVAL, "cannot read ptmx");
} }
fn write(&self, buf: &[u8]) -> Result<usize> { fn write(&self, buf: &[u8]) -> Result<usize> {
unreachable!() return_errno_with_message!(Errno::EINVAL, "cannot write ptmx");
} }
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents { fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
unreachable!() IoEvents::empty()
} }
} }

View File

@ -1,26 +1,20 @@
use jinux_frame::{cpu::UserContext, user::UserSpace, vm::VmIo}; use super::posix_thread::{PosixThread, PosixThreadBuilder, PosixThreadExt, ThreadName};
use super::process_vm::ProcessVm;
use super::signal::sig_disposition::SigDispositions;
use super::{process_table, Process, ProcessBuilder};
use crate::current_thread;
use crate::fs::file_table::FileTable;
use crate::fs::fs_resolver::FsResolver;
use crate::fs::utils::FileCreationMask;
use crate::prelude::*;
use crate::thread::{allocate_tid, thread_table, Thread, Tid};
use crate::util::write_val_to_user;
use crate::vm::vmar::Vmar;
use jinux_frame::cpu::UserContext;
use jinux_frame::user::UserSpace;
use jinux_frame::vm::VmIo;
use jinux_rights::Full; use jinux_rights::Full;
use crate::{
current_thread,
fs::file_table::FileTable,
fs::{fs_resolver::FsResolver, utils::FileCreationMask},
prelude::*,
process::{
posix_thread::{PosixThreadBuilder, PosixThreadExt, ThreadName},
process_table,
},
thread::{allocate_tid, thread_table, Thread, Tid},
util::write_val_to_user,
vm::vmar::Vmar,
};
use super::{
posix_thread::PosixThread, process_vm::ProcessVm, signal::sig_disposition::SigDispositions,
Process, ProcessBuilder,
};
bitflags! { bitflags! {
pub struct CloneFlags: u32 { pub struct CloneFlags: u32 {
const CLONE_VM = 0x00000100; /* Set if VM shared between processes. */ const CLONE_VM = 0x00000100; /* Set if VM shared between processes. */
@ -280,27 +274,8 @@ fn clone_child_process(parent_context: UserContext, clone_args: CloneArgs) -> Re
process_builder.build()? process_builder.build()?
}; };
// Adds child to parent's process group, and parent's child processes.
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 // Deals with clone flags
let child_thread = thread_table::tid_to_thread(child_tid).unwrap(); let child_thread = thread_table::get_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)?;
clone_child_cleartid(child_posix_thread, clone_args.child_tidptr, clone_flags)?; clone_child_cleartid(child_posix_thread, clone_args.child_tidptr, clone_flags)?;
@ -312,6 +287,10 @@ fn clone_child_process(parent_context: UserContext, clone_args: CloneArgs) -> Re
clone_args.child_tidptr, clone_args.child_tidptr,
clone_flags, clone_flags,
)?; )?;
// Sets parent process and group for child process.
set_parent_and_group(&current, &child);
Ok(child) Ok(child)
} }
@ -430,3 +409,19 @@ fn clone_sysvsem(clone_flags: CloneFlags) -> Result<()> {
} }
Ok(()) Ok(())
} }
fn set_parent_and_group(parent: &Arc<Process>, child: &Arc<Process>) {
let process_group = parent.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 = parent.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());
}

View File

@ -1,6 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use crate::process::signal::constants::{SIGCONT, SIGHUP}; use crate::process::signal::constants::{SIGCONT, SIGHUP};
use crate::process::signal::signals::kernel::KernelSignal; use crate::process::signal::signals::kernel::KernelSignal;
use crate::process::signal::Pauser;
use crate::process::{ProcessGroup, Session}; use crate::process::{ProcessGroup, Session};
/// The job control for terminals like tty and pty. /// The job control for terminals like tty and pty.
@ -11,6 +12,7 @@ use crate::process::{ProcessGroup, Session};
pub struct JobControl { pub struct JobControl {
foreground: SpinLock<Weak<ProcessGroup>>, foreground: SpinLock<Weak<ProcessGroup>>,
session: SpinLock<Weak<Session>>, session: SpinLock<Weak<Session>>,
pauser: Arc<Pauser>,
} }
impl JobControl { impl JobControl {
@ -19,6 +21,7 @@ impl JobControl {
Self { Self {
foreground: SpinLock::new(Weak::new()), foreground: SpinLock::new(Weak::new()),
session: SpinLock::new(Weak::new()), session: SpinLock::new(Weak::new()),
pauser: Pauser::new(),
} }
} }
@ -60,6 +63,7 @@ impl JobControl {
let session = current.session().unwrap(); let session = current.session().unwrap();
*self.session.lock() = Arc::downgrade(&session); *self.session.lock() = Arc::downgrade(&session);
self.pauser.resume_all();
Ok(()) Ok(())
} }
@ -73,8 +77,8 @@ impl JobControl {
}; };
if let Some(foreground) = self.foreground() { if let Some(foreground) = self.foreground() {
foreground.kernel_signal(KernelSignal::new(SIGHUP)); foreground.broadcast_signal(KernelSignal::new(SIGHUP));
foreground.kernel_signal(KernelSignal::new(SIGCONT)); foreground.broadcast_signal(KernelSignal::new(SIGCONT));
} }
Ok(()) Ok(())
@ -115,16 +119,33 @@ impl JobControl {
} }
*self.foreground.lock() = Arc::downgrade(process_group); *self.foreground.lock() = Arc::downgrade(process_group);
self.pauser.resume_all();
Ok(()) Ok(())
} }
/// Determines whether current process belongs to the foreground process group. If /// Wait until the current process is the foreground process group. If
/// the foreground process group is None, returns true. /// the foreground process group is None, returns true.
/// ///
/// # Panic /// # Panic
/// ///
/// This function should only be called in process context. /// This function should only be called in process context.
pub fn current_belongs_to_foreground(&self) -> bool { pub fn wait_until_in_foreground(&self) -> Result<()> {
// Fast path
if self.current_belongs_to_foreground() {
return Ok(());
}
// Slow path
self.pauser.pause_until(|| {
if self.current_belongs_to_foreground() {
Some(())
} else {
None
}
})
}
fn current_belongs_to_foreground(&self) -> bool {
let Some(foreground) = self.foreground() else { let Some(foreground) = self.foreground() else {
return true; return true;
}; };

View File

@ -151,6 +151,8 @@ impl Process {
let process = process_builder.build()?; let process = process_builder.build()?;
// Lock order: session table -> group table -> process table -> group of process
// -> group inner -> session inner
let mut session_table_mut = process_table::session_table_mut(); let mut session_table_mut = process_table::session_table_mut();
let mut group_table_mut = process_table::group_table_mut(); let mut group_table_mut = process_table::group_table_mut();
let mut process_table_mut = process_table::process_table_mut(); let mut process_table_mut = process_table::process_table_mut();
@ -290,10 +292,11 @@ impl Process {
} }
let session = self.session().unwrap(); let session = self.session().unwrap();
let mut session_inner = session.inner.lock();
let mut self_group_mut = self.process_group.lock(); // Lock order: session table -> group table -> group of process -> group inner -> session inner
let mut group_table_mut = process_table::group_table_mut();
let mut session_table_mut = process_table::session_table_mut(); let mut session_table_mut = process_table::session_table_mut();
let mut group_table_mut = process_table::group_table_mut();
let mut self_group_mut = self.process_group.lock();
if session_table_mut.contains_key(&self.pid) { if session_table_mut.contains_key(&self.pid) {
return_errno_with_message!(Errno::EPERM, "cannot create new session"); return_errno_with_message!(Errno::EPERM, "cannot create new session");
@ -303,12 +306,10 @@ impl Process {
return_errno_with_message!(Errno::EPERM, "cannot create process group"); 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 // Removes the process from old group
if let Some(old_group) = self.process_group() { if let Some(old_group) = self_group_mut.upgrade() {
let mut group_inner = old_group.inner.lock(); let mut group_inner = old_group.inner.lock();
let mut session_inner = session.inner.lock();
group_inner.remove_process(&self.pid); group_inner.remove_process(&self.pid);
*self_group_mut = Weak::new(); *self_group_mut = Weak::new();
@ -335,6 +336,10 @@ impl Process {
new_session.inner.lock().leader = Some(self.clone()); new_session.inner.lock().leader = Some(self.clone());
session_table_mut.insert(new_session.sid(), new_session.clone()); session_table_mut.insert(new_session.sid(), new_session.clone());
// Removes the process from session.
let mut session_inner = session.inner.lock();
session_inner.remove_process(self);
Ok(new_session) Ok(new_session)
} }
@ -386,13 +391,14 @@ impl Process {
/// The new group will be added to the same session as the process. /// The new group will be added to the same session as the process.
fn to_new_group(self: &Arc<Self>) -> Result<()> { fn to_new_group(self: &Arc<Self>) -> Result<()> {
let session = self.session().unwrap(); let session = self.session().unwrap();
let mut session_inner = session.inner.lock(); // Lock order: group table -> group of process -> group inner -> session inner
let mut self_group_mut = self.process_group.lock();
let mut group_table_mut = process_table::group_table_mut(); let mut group_table_mut = process_table::group_table_mut();
let mut self_group_mut = self.process_group.lock();
// Removes the process from old group // Removes the process from old group
if let Some(old_group) = self.process_group() { if let Some(old_group) = self_group_mut.upgrade() {
let mut group_inner = old_group.inner.lock(); let mut group_inner = old_group.inner.lock();
let mut session_inner = session.inner.lock();
group_inner.remove_process(&self.pid); group_inner.remove_process(&self.pid);
*self_group_mut = Weak::new(); *self_group_mut = Weak::new();
@ -406,15 +412,18 @@ impl Process {
// Creates a new process group. Adds the new group to group table and session. // Creates a new process group. Adds the new group to group table and session.
let new_group = ProcessGroup::new(self.clone()); let new_group = ProcessGroup::new(self.clone());
let mut new_group_inner = new_group.inner.lock();
let mut session_inner = session.inner.lock();
*self_group_mut = Arc::downgrade(&new_group); *self_group_mut = Arc::downgrade(&new_group);
group_table_mut.insert(new_group.pgid(), new_group.clone()); group_table_mut.insert(new_group.pgid(), new_group.clone());
new_group_inner.session = Arc::downgrade(&session);
session_inner session_inner
.process_groups .process_groups
.insert(new_group.pgid(), new_group.clone()); .insert(new_group.pgid(), new_group.clone());
let mut new_group_inner = new_group.inner.lock();
new_group_inner.session = Arc::downgrade(&session);
Ok(()) Ok(())
} }
@ -423,20 +432,33 @@ impl Process {
/// ///
/// The caller needs to ensure that the process and the group belongs to the same session. /// 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<()> { fn to_specified_group(self: &Arc<Process>, group: &Arc<ProcessGroup>) -> Result<()> {
let mut self_group_mut = self.process_group.lock(); // Lock order: group table -> group of process -> group inner (small pgid -> big pgid)
let mut group_table_mut = process_table::group_table_mut(); let mut group_table_mut = process_table::group_table_mut();
let mut group_inner = group.inner.lock(); let mut self_group_mut = self.process_group.lock();
// Removes the process from old group // Removes the process from old group
if let Some(old_group) = self.process_group() { let mut group_inner = if let Some(old_group) = self_group_mut.upgrade() {
let mut group_inner = old_group.inner.lock(); // Lock order: group with smaller pgid first
group_inner.remove_process(&self.pid); let (mut old_group_inner, group_inner) = match old_group.pgid().cmp(&group.pgid()) {
core::cmp::Ordering::Equal => return Ok(()),
core::cmp::Ordering::Less => (old_group.inner.lock(), group.inner.lock()),
core::cmp::Ordering::Greater => {
let group_inner = group.inner.lock();
let old_group_inner = old_group.inner.lock();
(old_group_inner, group_inner)
}
};
old_group_inner.remove_process(&self.pid);
*self_group_mut = Weak::new(); *self_group_mut = Weak::new();
if group_inner.is_empty() { if old_group_inner.is_empty() {
group_table_mut.remove(&old_group.pgid()); group_table_mut.remove(&old_group.pgid());
} }
}
group_inner
} else {
group.inner.lock()
};
// Adds the process to the specified group // Adds the process to the specified group
group_inner.processes.insert(self.pid, self.clone()); group_inner.processes.insert(self.pid, self.clone());
@ -567,8 +589,10 @@ mod test {
} }
fn new_process_in_session(parent: Option<Arc<Process>>) -> Arc<Process> { fn new_process_in_session(parent: Option<Arc<Process>>) -> Arc<Process> {
let mut group_table_mut = process_table::group_table_mut(); // Lock order: session table -> group table -> group of process -> group inner
// -> session inner
let mut session_table_mut = process_table::session_table_mut(); let mut session_table_mut = process_table::session_table_mut();
let mut group_table_mut = process_table::group_table_mut();
let process = new_process(parent); let process = new_process(parent);
// Creates new group // Creates new group
@ -587,6 +611,7 @@ mod test {
} }
fn remove_session_and_group(process: Arc<Process>) { fn remove_session_and_group(process: Arc<Process>) {
// Lock order: session table -> group table
let mut session_table_mut = process_table::session_table_mut(); let mut session_table_mut = process_table::session_table_mut();
let mut group_table_mut = process_table::group_table_mut(); let mut group_table_mut = process_table::group_table_mut();
if let Some(sess) = process.session() { if let Some(sess) = process.session() {

View File

@ -1,7 +1,6 @@
use super::{Pgid, Pid, Process, Session}; use super::{Pgid, Pid, Process, Session};
use crate::prelude::*; use crate::prelude::*;
use crate::process::signal::signals::kernel::KernelSignal; use crate::process::signal::signals::Signal;
use crate::process::signal::signals::user::UserSignal;
/// `ProcessGroup` represents a set of processes. Each `ProcessGroup` has a unique /// `ProcessGroup` represents a set of processes. Each `ProcessGroup` has a unique
/// identifier `pgid`. /// identifier `pgid`.
@ -37,7 +36,7 @@ impl ProcessGroup {
/// id. The process will become the leading process of the new process group. /// 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. /// The caller needs to ensure that the process does not belong to any group.
pub(super) fn new(process: Arc<Process>) -> Arc<Self> { pub(in crate::process) fn new(process: Arc<Process>) -> Arc<Self> {
let pid = process.pid(); let pid = process.pid();
let inner = { let inner = {
@ -57,7 +56,7 @@ impl ProcessGroup {
} }
/// Returns whether self contains a process with `pid`. /// Returns whether self contains a process with `pid`.
pub(super) fn contains_process(&self, pid: Pid) -> bool { pub(in crate::process) fn contains_process(&self, pid: Pid) -> bool {
self.inner.lock().processes.contains_key(&pid) self.inner.lock().processes.contains_key(&pid)
} }
@ -66,22 +65,15 @@ impl ProcessGroup {
self.pgid self.pgid
} }
/// Sends kernel signal to all processes in the group /// Broadcasts signal to all processes in the group.
pub fn kernel_signal(&self, signal: KernelSignal) { pub fn broadcast_signal(&self, signal: impl Signal + Clone + 'static) {
for process in self.inner.lock().processes.values() { for process in self.inner.lock().processes.values() {
process.enqueue_signal(Box::new(signal)); process.enqueue_signal(Box::new(signal.clone()));
}
}
/// 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. /// Returns the leader process.
pub(super) fn leader(&self) -> Option<Arc<Process>> { pub fn leader(&self) -> Option<Arc<Process>> {
self.inner.lock().leader.clone() self.inner.lock().leader.clone()
} }

View File

@ -44,7 +44,7 @@ impl Session {
/// ///
/// The caller needs to ensure that the group does not belong to any session, and the caller /// 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. /// should set the leader process after creating the session.
pub(super) fn new(group: Arc<ProcessGroup>) -> Arc<Self> { pub(in crate::process) fn new(group: Arc<ProcessGroup>) -> Arc<Self> {
let sid = group.pgid(); let sid = group.pgid();
let inner = { let inner = {
let mut process_groups = BTreeMap::new(); let mut process_groups = BTreeMap::new();
@ -68,12 +68,12 @@ impl Session {
} }
/// Returns the leader process. /// Returns the leader process.
pub(super) fn leader(&self) -> Option<Arc<Process>> { pub fn leader(&self) -> Option<Arc<Process>> {
self.inner.lock().leader.clone() self.inner.lock().leader.clone()
} }
/// Returns whether `self` contains the `process_group` /// Returns whether `self` contains the `process_group`
pub(super) fn contains_process_group( pub(in crate::process) fn contains_process_group(
self: &Arc<Self>, self: &Arc<Self>,
process_group: &Arc<ProcessGroup>, process_group: &Arc<ProcessGroup>,
) -> bool { ) -> bool {
@ -83,10 +83,14 @@ impl Session {
.contains_key(&process_group.pgid()) .contains_key(&process_group.pgid())
} }
/// Sets terminal as the controlling terminal of the session. /// Sets terminal as the controlling terminal of the session. The `get_terminal` method
/// should set the session for the terminal and returns the session.
/// ///
/// If the session already has controlling terminal, this method will return `Err(EPERM)`. /// 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<()> { pub fn set_terminal<F>(&self, get_terminal: F) -> Result<()>
where
F: Fn() -> Result<Arc<dyn Terminal>>,
{
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
if inner.terminal.is_some() { if inner.terminal.is_some() {
@ -96,7 +100,7 @@ impl Session {
); );
} }
let terminal = terminal()?; let terminal = get_terminal()?;
inner.terminal = Some(terminal); inner.terminal = Some(terminal);
Ok(()) Ok(())
} }
@ -104,7 +108,10 @@ impl Session {
/// Releases the controlling terminal of the session. /// Releases the controlling terminal of the session.
/// ///
/// If the session does not have controlling terminal, this method will return `ENOTTY`. /// 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<()> { pub fn release_terminal<F>(&self, release_session: F) -> Result<()>
where
F: Fn(&Arc<dyn Terminal>) -> Result<()>,
{
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
if inner.terminal.is_none() { if inner.terminal.is_none() {
return_errno_with_message!( return_errno_with_message!(
@ -113,7 +120,8 @@ impl Session {
); );
} }
release_session()?; let terminal = inner.terminal.as_ref().unwrap();
release_session(terminal)?;
inner.terminal = None; inner.terminal = None;
Ok(()) Ok(())
} }

View File

@ -63,13 +63,13 @@ pub trait Terminal: Send + Sync + FileIo {
return_errno_with_message!(Errno::EPERM, "current process is not session leader"); return_errno_with_message!(Errno::EPERM, "current process is not session leader");
} }
let terminal = || { let get_terminal = || {
self.job_control().set_current_session()?; self.job_control().set_current_session()?;
Ok(self.arc_self()) Ok(self.arc_self())
}; };
let session = current!().session().unwrap(); let session = current!().session().unwrap();
session.set_terminal(terminal) session.set_terminal(get_terminal)
} }
/// Releases the terminal from the session of current process if the terminal is the controlling /// Releases the terminal from the session of current process if the terminal is the controlling
@ -91,7 +91,7 @@ pub trait Terminal: Send + Sync + FileIo {
return Ok(()); return Ok(());
} }
let release_session = || self.job_control().release_current_session(); let release_session = |_: &Arc<dyn Terminal>| self.job_control().release_current_session();
let session = current.session().unwrap(); let session = current.session().unwrap();
session.release_terminal(release_session) session.release_terminal(release_session)

View File

@ -81,12 +81,15 @@ fn reap_zombie_child(process: &Process, pid: Pid) -> u32 {
thread_table::remove_thread(thread.tid()); thread_table::remove_thread(thread.tid());
} }
let mut process_table_mut = process_table::process_table_mut(); // Lock order: session table -> group table -> process table -> group of process
let mut group_table_mut = process_table::group_table_mut(); // -> group inner -> session inner
let mut session_table_mut = process_table::session_table_mut(); 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();
let mut child_group_mut = child_process.process_group.lock(); let mut child_group_mut = child_process.process_group.lock();
let process_group = child_process.process_group().unwrap(); let process_group = child_group_mut.upgrade().unwrap();
let mut group_inner = process_group.inner.lock(); let mut group_inner = process_group.inner.lock();
let session = group_inner.session.upgrade().unwrap(); let session = group_inner.session.upgrade().unwrap();
let mut session_inner = session.inner.lock(); let mut session_inner = session.inner.lock();

View File

@ -42,7 +42,7 @@ pub fn do_sys_kill(filter: ProcessFilter, sig_num: SigNum) -> Result<()> {
} }
ProcessFilter::WithPgid(pgid) => { ProcessFilter::WithPgid(pgid) => {
if let Some(process_group) = process_table::get_process_group(&pgid) { if let Some(process_group) = process_table::get_process_group(&pgid) {
process_group.user_signal(signal); process_group.broadcast_signal(signal);
} else { } else {
return_errno_with_message!(Errno::ESRCH, "No such process group in process table"); return_errno_with_message!(Errno::ESRCH, "No such process group in process table");
} }

View File

@ -16,8 +16,8 @@ pub fn sys_tgkill(tgid: Pid, tid: Tid, sig_num: u8) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_TGKILL); log_syscall_entry!(SYS_TGKILL);
let sig_num = SigNum::from_u8(sig_num); let sig_num = SigNum::from_u8(sig_num);
info!("tgid = {}, pid = {}, sig_num = {:?}", tgid, tid, sig_num); info!("tgid = {}, pid = {}, sig_num = {:?}", tgid, tid, sig_num);
let target_thread = thread_table::tid_to_thread(tid) let target_thread =
.ok_or(Error::with_message(Errno::EINVAL, "Invalid pid"))?; thread_table::get_thread(tid).ok_or(Error::with_message(Errno::EINVAL, "Invalid pid"))?;
let posix_thread = target_thread.as_posix_thread().unwrap(); let posix_thread = target_thread.as_posix_thread().unwrap();
let pid = posix_thread.process().pid(); let pid = posix_thread.process().pid();
if pid != tgid { if pid != tgid {

View File

@ -15,6 +15,6 @@ pub fn remove_thread(tid: Tid) {
THREAD_TABLE.lock().remove(&tid); THREAD_TABLE.lock().remove(&tid);
} }
pub fn tid_to_thread(tid: Tid) -> Option<Arc<Thread>> { pub fn get_thread(tid: Tid) -> Option<Arc<Thread>> {
THREAD_TABLE.lock().get(&tid).cloned() THREAD_TABLE.lock().get(&tid).cloned()
} }