diff --git a/kernel/src/device/pty/pty.rs b/kernel/src/device/pty/pty.rs index 8e6cc0a48..5dd28609e 100644 --- a/kernel/src/device/pty/pty.rs +++ b/kernel/src/device/pty/pty.rs @@ -6,7 +6,7 @@ use ostd::task::Task; use crate::{ current_userspace, - device::tty::{line_discipline::LineDiscipline, new_job_control_and_ldisc}, + device::tty::line_discipline::LineDiscipline, events::IoEvents, fs::{ device::{Device, DeviceId, DeviceType}, @@ -18,6 +18,7 @@ use crate::{ }, prelude::*, process::{ + broadcast_signal_async, posix_thread::{AsPosixThread, AsThreadLocal}, signal::{PollHandle, Pollable, Pollee}, JobControl, Terminal, @@ -43,10 +44,9 @@ pub struct PtyMaster { impl PtyMaster { pub fn new(ptmx: Arc, index: u32) -> Arc { Arc::new_cyclic(move |master| { - let (job_control, ldisc) = new_job_control_and_ldisc(); let slave = Arc::new_cyclic(move |weak_self| PtySlave { - ldisc, - job_control, + ldisc: LineDiscipline::new(), + job_control: JobControl::new(), master: master.clone(), weak_self: weak_self.clone(), }); @@ -166,11 +166,19 @@ impl FileIo for PtyMaster { let write_len = buf.len(); let mut input = self.input.lock(); for character in buf { - self.slave.ldisc.push_char(character, |content| { - for byte in content.as_bytes() { - input.push_overwrite(*byte); - } - }); + self.slave.ldisc.push_char( + character, + |signum| { + if let Some(foreground) = self.slave.job_control.foreground() { + broadcast_signal_async(Arc::downgrade(&foreground), signum); + } + }, + |content| { + for byte in content.as_bytes() { + input.push_overwrite(*byte); + } + }, + ); } self.pollee.notify(IoEvents::IN); @@ -240,8 +248,8 @@ impl Drop for PtyMaster { } pub struct PtySlave { - ldisc: Arc, - job_control: Arc, + ldisc: LineDiscipline, + job_control: JobControl, master: Weak, weak_self: Weak, } diff --git a/kernel/src/device/tty/line_discipline.rs b/kernel/src/device/tty/line_discipline.rs index d2b313650..2cfd85d02 100644 --- a/kernel/src/device/tty/line_discipline.rs +++ b/kernel/src/device/tty/line_discipline.rs @@ -2,10 +2,7 @@ use alloc::format; -use ostd::{ - sync::LocalIrqDisabled, - trap::{disable_local, in_interrupt_context}, -}; +use ostd::{sync::LocalIrqDisabled, trap::disable_local}; use super::termio::{KernelTermios, WinSize, CC_C_CHAR}; use crate::{ @@ -13,10 +10,9 @@ use crate::{ prelude::*, process::signal::{ constants::{SIGINT, SIGQUIT}, - signals::kernel::KernelSignal, + sig_num::SigNum, PollHandle, Pollable, Pollee, }, - thread::work_queue::{submit_work_item, work_item::WorkItem, WorkPriority}, util::ring_buffer::RingBuffer, }; @@ -25,8 +21,6 @@ use crate::{ const BUFFER_CAPACITY: usize = 4096; -pub type LdiscSignalSender = Arc; - // Lock ordering to prevent deadlock (circular dependencies): // 1. `termios` // 2. `current_line` @@ -43,12 +37,6 @@ pub struct LineDiscipline { winsize: SpinLock, /// Pollee pollee: Pollee, - /// Used to send signal for foreground processes, when some char comes. - send_signal: LdiscSignalSender, - /// Work item - work_item: Arc, - /// Parameters used by a work item. - work_item_para: Arc>, } pub struct CurrentLine { @@ -99,29 +87,23 @@ impl Pollable for LineDiscipline { impl LineDiscipline { /// Creates a new line discipline - pub fn new(send_signal: LdiscSignalSender) -> Arc { - Arc::new_cyclic(move |line_ref: &Weak| { - let line_discipline = line_ref.clone(); - let work_item = WorkItem::new(Box::new(move || { - if let Some(line_discipline) = line_discipline.upgrade() { - line_discipline.send_signal_after(); - } - })); - Self { - current_line: SpinLock::new(CurrentLine::default()), - read_buffer: SpinLock::new(RingBuffer::new(BUFFER_CAPACITY)), - termios: SpinLock::new(KernelTermios::default()), - winsize: SpinLock::new(WinSize::default()), - pollee: Pollee::new(), - send_signal, - work_item, - work_item_para: Arc::new(SpinLock::new(LineDisciplineWorkPara::new())), - } - }) + pub fn new() -> Self { + Self { + current_line: SpinLock::new(CurrentLine::default()), + read_buffer: SpinLock::new(RingBuffer::new(BUFFER_CAPACITY)), + termios: SpinLock::new(KernelTermios::default()), + winsize: SpinLock::new(WinSize::default()), + pollee: Pollee::new(), + } } /// Pushes a char to the line discipline - pub fn push_char(&self, ch: u8, echo_callback: F2) { + pub fn push_char( + &self, + ch: u8, + mut signal_callback: F1, + echo_callback: F2, + ) { let termios = self.termios.lock(); let ch = if termios.contains_icrnl() && ch == b'\r' { @@ -130,8 +112,8 @@ impl LineDiscipline { ch }; - if self.may_send_signal(&termios, ch) { - submit_work_item(self.work_item.clone(), WorkPriority::High); + if let Some(signum) = char_to_signal(ch, &termios) { + signal_callback(signum); // CBREAK mode may require the character to be outputted, so just go ahead. } @@ -180,27 +162,6 @@ impl LineDiscipline { } } - fn may_send_signal(&self, termios: &KernelTermios, ch: u8) -> bool { - if !termios.is_canonical_mode() || !termios.contains_isig() { - return false; - } - - 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::VQUIT) => KernelSignal::new(SIGQUIT), - _ => return false, - }; - - if in_interrupt_context() { - // `kernel_signal()` may cause sleep, so only construct parameters here. - self.work_item_para.lock().kernel_signal = Some(signal); - } else { - (self.send_signal)(signal); - } - - true - } - fn check_io_events(&self) -> IoEvents { let buffer = self.read_buffer.lock(); @@ -211,13 +172,6 @@ impl LineDiscipline { } } - /// Sends a signal later. The signal will be handled by a work queue. - fn send_signal_after(&self) { - if let Some(signal) = self.work_item_para.lock().kernel_signal.take() { - (self.send_signal)(signal); - }; - } - // TODO: respect output flags fn output_char(&self, ch: u8, termios: &KernelTermios, mut echo_callback: F) { match ch { @@ -230,7 +184,7 @@ impl LineDiscipline { } ch if is_printable_char(ch) => print!("{}", char::from(ch)), ch if is_ctrl_char(ch) && termios.contains_echo_ctl() => { - let ctrl_char = format!("^{}", get_printable_char(ch)); + let ctrl_char = format!("^{}", ctrl_char_to_printable(ch)); echo_callback(&ctrl_char); } _ => {} @@ -393,19 +347,19 @@ fn is_ctrl_char(ch: u8) -> bool { (0..0x20).contains(&ch) } -fn get_printable_char(ctrl_char: u8) -> char { - debug_assert!(is_ctrl_char(ctrl_char)); - char::from_u32((ctrl_char + b'A' - 1) as u32).unwrap() -} +fn char_to_signal(ch: u8, termios: &KernelTermios) -> Option { + if !termios.is_canonical_mode() || !termios.contains_isig() { + return None; + } -struct LineDisciplineWorkPara { - kernel_signal: Option, -} - -impl LineDisciplineWorkPara { - fn new() -> Self { - Self { - kernel_signal: None, - } + match ch { + ch if ch == *termios.get_special_char(CC_C_CHAR::VINTR) => Some(SIGINT), + ch if ch == *termios.get_special_char(CC_C_CHAR::VQUIT) => Some(SIGQUIT), + _ => None, } } + +fn ctrl_char_to_printable(ch: u8) -> char { + debug_assert!(is_ctrl_char(ch)); + char::from_u32((ch + b'A' - 1) as u32).unwrap() +} diff --git a/kernel/src/device/tty/mod.rs b/kernel/src/device/tty/mod.rs index 75cc0f4de..7c95d16f1 100644 --- a/kernel/src/device/tty/mod.rs +++ b/kernel/src/device/tty/mod.rs @@ -14,7 +14,8 @@ use crate::{ }, prelude::*, process::{ - signal::{signals::kernel::KernelSignal, PollHandle, Pollable}, + broadcast_signal_async, + signal::{PollHandle, Pollable}, JobControl, Terminal, }, }; @@ -40,8 +41,8 @@ pub struct Tty { #[expect(unused)] name: CString, /// line discipline - ldisc: Arc, - job_control: Arc, + ldisc: LineDiscipline, + job_control: JobControl, /// driver driver: SpinLock>, weak_self: Weak, @@ -49,11 +50,10 @@ pub struct Tty { impl Tty { pub fn new(name: CString) -> Arc { - let (job_control, ldisc) = new_job_control_and_ldisc(); Arc::new_cyclic(move |weak_ref| Tty { name, - ldisc, - job_control, + ldisc: LineDiscipline::new(), + job_control: JobControl::new(), driver: SpinLock::new(Weak::new()), weak_self: weak_ref.clone(), }) @@ -66,8 +66,15 @@ impl Tty { pub fn push_char(&self, ch: u8) { // FIXME: Use `early_print` to avoid calling virtio-console. // This is only a workaround - self.ldisc - .push_char(ch, |content| early_print!("{}", content)) + self.ldisc.push_char( + ch, + |signum| { + if let Some(foreground) = self.job_control.foreground() { + broadcast_signal_async(Arc::downgrade(&foreground), signum); + } + }, + |content| early_print!("{}", content), + ) } } @@ -156,25 +163,6 @@ impl Device for Tty { } } -pub fn new_job_control_and_ldisc() -> (Arc, Arc) { - 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 { N_TTY.get().unwrap() } diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index 6e37b5e5e..888a0a0cc 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -23,8 +23,8 @@ pub use clone::{clone_child, CloneArgs, CloneFlags}; pub use credentials::{Credentials, Gid, Uid}; pub use kill::{kill, kill_all, kill_group, tgkill}; pub use process::{ - enqueue_signal_async, spawn_init_process, ExitCode, JobControl, Pgid, Pid, Process, - ProcessGroup, Session, Sid, Terminal, + broadcast_signal_async, enqueue_signal_async, spawn_init_process, ExitCode, JobControl, Pgid, + Pid, Process, ProcessGroup, Session, Sid, Terminal, }; pub use process_filter::ProcessFilter; pub use process_vm::{ diff --git a/kernel/src/process/process/mod.rs b/kernel/src/process/process/mod.rs index acf1658d1..06d4c3098 100644 --- a/kernel/src/process/process/mod.rs +++ b/kernel/src/process/process/mod.rs @@ -725,3 +725,23 @@ pub fn enqueue_signal_async(process: Weak, signum: SigNum) { work_queue::WorkPriority::High, ); } + +/// Broadcasts a process-directed kernel signal asynchronously. +/// +/// This is the asynchronous version of [`ProcessGroup::broadcast_signal`]. By asynchronous, this +/// method submits a work item and returns, so this method doesn't sleep and can be used in atomic +/// mode. +pub fn broadcast_signal_async(process_group: Weak, signal: SigNum) { + use super::signal::signals::kernel::KernelSignal; + use crate::thread::work_queue; + + let signal = KernelSignal::new(signal); + work_queue::submit_work_func( + move || { + if let Some(process_group) = process_group.upgrade() { + process_group.broadcast_signal(signal); + } + }, + work_queue::WorkPriority::High, + ); +}