fix dead lock in tty
This commit is contained in:
parent
6e29996720
commit
16b435e310
|
|
@ -1,6 +1,6 @@
|
|||
src/apps/hello_world/hello_world filter=lfs diff=lfs merge=lfs -text
|
||||
src/apps/fork/fork filter=lfs diff=lfs merge=lfs -text
|
||||
src/appshello_c/hello filter=lfs diff=lfs merge=lfs -text
|
||||
src/apps/hello_c/hello filter=lfs diff=lfs merge=lfs -text
|
||||
src/apps/execve/execve filter=lfs diff=lfs merge=lfs -text
|
||||
src/apps/execve/hello filter=lfs diff=lfs merge=lfs -text
|
||||
src/apps/fork_c/fork filter=lfs diff=lfs merge=lfs -text
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR=/scripts
|
||||
cd ${SCRIPT_DIR}/..
|
||||
|
||||
echo "Running tests......"
|
||||
tests="hello_world/hello_world fork/fork execve/execve fork_c/fork signal_c/signal_test pthread/pthread_test"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
set -e
|
||||
set -x
|
||||
|
||||
cd scripts
|
||||
SCRIPT_DIR=/scripts
|
||||
cd ${SCRIPT_DIR}
|
||||
|
||||
touch hello.txt
|
||||
mv hello.txt hello_world.txt
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
pub const MAX_THREAD_NAME_LEN: usize = 16;
|
||||
pub const MAX_THREAD_NAME_LEN: usize = 256;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ThreadName {
|
||||
|
|
@ -34,7 +34,7 @@ impl ThreadName {
|
|||
// if len > MAX_THREAD_NAME_LEN, truncate it.
|
||||
self.count = MAX_THREAD_NAME_LEN;
|
||||
self.inner[..MAX_THREAD_NAME_LEN].clone_from_slice(&bytes[..MAX_THREAD_NAME_LEN]);
|
||||
self.inner[MAX_THREAD_NAME_LEN] = 0;
|
||||
self.inner[MAX_THREAD_NAME_LEN - 1] = 0;
|
||||
return Ok(());
|
||||
}
|
||||
self.count = bytes_len;
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ const BUFFER_CAPACITY: usize = 4096;
|
|||
|
||||
pub struct LineDiscipline {
|
||||
/// current line
|
||||
current_line: CurrentLine,
|
||||
current_line: RwLock<CurrentLine>,
|
||||
/// The read buffer
|
||||
read_buffer: Mutex<ConstGenericRingBuffer<u8, BUFFER_CAPACITY>>,
|
||||
/// The foreground process group
|
||||
foreground: Option<Pgid>,
|
||||
foreground: RwLock<Option<Pgid>>,
|
||||
/// termios
|
||||
termios: KernelTermios,
|
||||
termios: RwLock<KernelTermios>,
|
||||
/// wait until self is readable
|
||||
read_wait_queue: WaitQueue,
|
||||
}
|
||||
|
|
@ -67,59 +67,62 @@ impl LineDiscipline {
|
|||
/// create a new line discipline
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current_line: CurrentLine::new(),
|
||||
current_line: RwLock::new(CurrentLine::new()),
|
||||
read_buffer: Mutex::new(ConstGenericRingBuffer::new()),
|
||||
foreground: None,
|
||||
termios: KernelTermios::default(),
|
||||
foreground: RwLock::new(None),
|
||||
termios: RwLock::new(KernelTermios::default()),
|
||||
read_wait_queue: WaitQueue::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// push char to line discipline. This function should be called in input interrupt handler.
|
||||
pub fn push_char(&mut self, mut item: u8) {
|
||||
if self.termios.contains_icrnl() {
|
||||
pub fn push_char(&self, mut item: u8) {
|
||||
let termios = self.termios.read();
|
||||
if termios.contains_icrnl() {
|
||||
if item == b'\r' {
|
||||
item = b'\n'
|
||||
}
|
||||
}
|
||||
if self.termios.is_canonical_mode() {
|
||||
if item == *self.termios.get_special_char(CC_C_CHAR::VINTR) {
|
||||
if termios.is_canonical_mode() {
|
||||
if item == *termios.get_special_char(CC_C_CHAR::VINTR) {
|
||||
// type Ctrl + C, signal SIGINT
|
||||
if self.termios.contains_isig() {
|
||||
if let Some(fg) = self.foreground {
|
||||
if termios.contains_isig() {
|
||||
if let Some(fg) = *self.foreground.read() {
|
||||
let kernel_signal = KernelSignal::new(SIGINT);
|
||||
let fg_group = process_table::pgid_to_process_group(fg).unwrap();
|
||||
fg_group.kernel_signal(kernel_signal);
|
||||
}
|
||||
}
|
||||
} else if item == *self.termios.get_special_char(CC_C_CHAR::VQUIT) {
|
||||
} else if item == *termios.get_special_char(CC_C_CHAR::VQUIT) {
|
||||
// type Ctrl + \, signal SIGQUIT
|
||||
if self.termios.contains_isig() {
|
||||
if let Some(fg) = self.foreground {
|
||||
if termios.contains_isig() {
|
||||
if let Some(fg) = *self.foreground.read() {
|
||||
let kernel_signal = KernelSignal::new(SIGQUIT);
|
||||
let fg_group = process_table::pgid_to_process_group(fg).unwrap();
|
||||
fg_group.kernel_signal(kernel_signal);
|
||||
}
|
||||
}
|
||||
} else if item == *self.termios.get_special_char(CC_C_CHAR::VKILL) {
|
||||
} else if item == *termios.get_special_char(CC_C_CHAR::VKILL) {
|
||||
// erase current line
|
||||
self.current_line.drain();
|
||||
} else if item == *self.termios.get_special_char(CC_C_CHAR::VERASE) {
|
||||
self.current_line.write().drain();
|
||||
} else if item == *termios.get_special_char(CC_C_CHAR::VERASE) {
|
||||
// type backspace
|
||||
if !self.current_line.is_empty() {
|
||||
self.current_line.backspace();
|
||||
let mut current_line = self.current_line.write();
|
||||
if !current_line.is_empty() {
|
||||
current_line.backspace();
|
||||
}
|
||||
} else if meet_new_line(item, &self.get_termios()) {
|
||||
// a new line was met. We currently add the item to buffer.
|
||||
// when we read content, the item should be skipped if it's EOF.
|
||||
self.current_line.push_char(item);
|
||||
let current_line_chars = self.current_line.drain();
|
||||
let mut current_line = self.current_line.write();
|
||||
current_line.push_char(item);
|
||||
let current_line_chars = current_line.drain();
|
||||
for char in current_line_chars {
|
||||
self.read_buffer.lock().push(char);
|
||||
}
|
||||
} else if item >= 0x20 && item < 0x7f {
|
||||
// printable character
|
||||
self.current_line.push_char(item);
|
||||
self.current_line.write().push_char(item);
|
||||
}
|
||||
} else {
|
||||
// raw mode
|
||||
|
|
@ -127,7 +130,7 @@ impl LineDiscipline {
|
|||
// debug!("push char: {}", char::from(item))
|
||||
}
|
||||
|
||||
if self.termios.contain_echo() {
|
||||
if termios.contain_echo() {
|
||||
self.output_char(item);
|
||||
}
|
||||
|
||||
|
|
@ -147,13 +150,14 @@ impl LineDiscipline {
|
|||
let ch = char::from(item);
|
||||
print!("{}", ch);
|
||||
}
|
||||
if item == *self.termios.get_special_char(CC_C_CHAR::VERASE) {
|
||||
let termios = self.termios.read();
|
||||
if item == *termios.get_special_char(CC_C_CHAR::VERASE) {
|
||||
// write a space to overwrite current character
|
||||
let bytes: [u8; 3] = [b'\x08', b' ', b'\x08'];
|
||||
let backspace = core::str::from_utf8(&bytes).unwrap();
|
||||
print!("{}", backspace);
|
||||
}
|
||||
if self.termios.contains_echo_ctl() {
|
||||
if termios.contains_echo_ctl() {
|
||||
// The unprintable chars between 1-31 are mapped to ctrl characters between 65-95.
|
||||
// e.g., 0x3 is mapped to 0x43, which is C. So, we will print ^C when 0x3 is met.
|
||||
if 0 < item && item < 0x20 {
|
||||
|
|
@ -165,9 +169,11 @@ impl LineDiscipline {
|
|||
}
|
||||
|
||||
/// read all bytes buffered to dst, return the actual read length.
|
||||
pub fn read(&mut self, dst: &mut [u8]) -> Result<usize> {
|
||||
let vmin = *self.termios.get_special_char(CC_C_CHAR::VMIN);
|
||||
let vtime = *self.termios.get_special_char(CC_C_CHAR::VTIME);
|
||||
pub fn read(&self, dst: &mut [u8]) -> Result<usize> {
|
||||
let termios = self.termios.read();
|
||||
let vmin = *termios.get_special_char(CC_C_CHAR::VMIN);
|
||||
let vtime = *termios.get_special_char(CC_C_CHAR::VTIME);
|
||||
drop(termios);
|
||||
let read_len: usize = self.read_wait_queue.wait_until(|| {
|
||||
// if current process does not belong to foreground process group,
|
||||
// block until current process become foreground.
|
||||
|
|
@ -216,10 +222,10 @@ impl LineDiscipline {
|
|||
let mut read_len = 0;
|
||||
for i in 0..max_read_len {
|
||||
if let Some(next_char) = buffer.dequeue() {
|
||||
if self.termios.is_canonical_mode() {
|
||||
if self.termios.read().is_canonical_mode() {
|
||||
// canonical mode, read until meet new line
|
||||
if meet_new_line(next_char, self.get_termios()) {
|
||||
if !should_not_be_read(next_char, self.get_termios()) {
|
||||
if meet_new_line(next_char, &self.termios.read()) {
|
||||
if !should_not_be_read(next_char, &self.termios.read()) {
|
||||
dst[i] = next_char;
|
||||
read_len += 1;
|
||||
}
|
||||
|
|
@ -261,7 +267,7 @@ impl LineDiscipline {
|
|||
/// whether the current process belongs to foreground process group
|
||||
fn current_belongs_to_foreground(&self) -> bool {
|
||||
let current = current!();
|
||||
if let Some(fg_pgid) = self.foreground {
|
||||
if let Some(fg_pgid) = *self.foreground.read() {
|
||||
if let Some(process_group) = process_table::pgid_to_process_group(fg_pgid) {
|
||||
if process_group.contains_process(current.pid()) {
|
||||
return true;
|
||||
|
|
@ -273,8 +279,8 @@ impl LineDiscipline {
|
|||
}
|
||||
|
||||
/// set foreground process group
|
||||
pub fn set_fg(&mut self, fg_pgid: Pgid) {
|
||||
self.foreground = Some(fg_pgid);
|
||||
pub fn set_fg(&self, fg_pgid: Pgid) {
|
||||
*self.foreground.write() = Some(fg_pgid);
|
||||
// Some background processes may be waiting on the wait queue, when set_fg, the background processes may be able to read.
|
||||
if self.is_readable() {
|
||||
self.read_wait_queue.wake_all();
|
||||
|
|
@ -282,8 +288,8 @@ impl LineDiscipline {
|
|||
}
|
||||
|
||||
/// get foreground process group id
|
||||
pub fn get_fg(&self) -> Option<&Pgid> {
|
||||
self.foreground.as_ref()
|
||||
pub fn get_fg(&self) -> Option<Pgid> {
|
||||
*self.foreground.read()
|
||||
}
|
||||
|
||||
/// whether there is buffered data
|
||||
|
|
@ -291,12 +297,12 @@ impl LineDiscipline {
|
|||
self.read_buffer.lock().len() == 0
|
||||
}
|
||||
|
||||
pub fn get_termios(&self) -> &KernelTermios {
|
||||
&self.termios
|
||||
pub fn get_termios(&self) -> KernelTermios {
|
||||
*self.termios.read()
|
||||
}
|
||||
|
||||
pub fn set_termios(&mut self, termios: KernelTermios) {
|
||||
self.termios = termios;
|
||||
pub fn set_termios(&self, termios: KernelTermios) {
|
||||
*self.termios.write() = termios;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use self::line_discipline::LineDiscipline;
|
||||
use crate::driver::tty::TtyDriver;
|
||||
use crate::fs::utils::{InodeMode, InodeType, Metadata};
|
||||
use crate::fs::{
|
||||
file_handle::File,
|
||||
utils::{IoEvents, IoctlCmd},
|
||||
|
|
@ -22,7 +23,7 @@ pub struct Tty {
|
|||
/// tty_name
|
||||
name: CString,
|
||||
/// line discipline
|
||||
ldisc: Mutex<LineDiscipline>,
|
||||
ldisc: LineDiscipline,
|
||||
/// driver
|
||||
driver: Mutex<Weak<TtyDriver>>,
|
||||
}
|
||||
|
|
@ -31,14 +32,14 @@ impl Tty {
|
|||
pub fn new(name: CString) -> Self {
|
||||
Tty {
|
||||
name,
|
||||
ldisc: Mutex::new(LineDiscipline::new()),
|
||||
ldisc: LineDiscipline::new(),
|
||||
driver: Mutex::new(Weak::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set foreground process group
|
||||
pub fn set_fg(&self, pgid: Pgid) {
|
||||
self.ldisc.lock().set_fg(pgid);
|
||||
self.ldisc.set_fg(pgid);
|
||||
}
|
||||
|
||||
pub fn set_driver(&self, driver: Weak<TtyDriver>) {
|
||||
|
|
@ -48,23 +49,22 @@ impl Tty {
|
|||
/// Wake up foreground process group that wait on IO events.
|
||||
/// This function should be called when the interrupt handler of IO events is called.
|
||||
fn wake_fg_proc_grp(&self) {
|
||||
let ldisc = self.ldisc.lock();
|
||||
if let Some(fg_pgid) = ldisc.get_fg() {
|
||||
if let Some(fg_proc_grp) = process_table::pgid_to_process_group(*fg_pgid) {
|
||||
if let Some(fg_pgid) = self.ldisc.get_fg() {
|
||||
if let Some(fg_proc_grp) = process_table::pgid_to_process_group(fg_pgid) {
|
||||
fg_proc_grp.wake_all_polling_procs();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive_char(&self, item: u8) {
|
||||
self.ldisc.lock().push_char(item);
|
||||
self.ldisc.push_char(item);
|
||||
self.wake_fg_proc_grp();
|
||||
}
|
||||
}
|
||||
|
||||
impl File for Tty {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
self.ldisc.lock().read(buf)
|
||||
self.ldisc.read(buf)
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
|
|
@ -77,28 +77,26 @@ impl File for Tty {
|
|||
}
|
||||
|
||||
fn poll(&self) -> IoEvents {
|
||||
self.ldisc.lock().poll()
|
||||
self.ldisc.poll()
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
match cmd {
|
||||
IoctlCmd::TCGETS => {
|
||||
// Get terminal attributes
|
||||
let ldist_lock = self.ldisc.lock();
|
||||
let termios = ldist_lock.get_termios();
|
||||
let termios = self.ldisc.get_termios();
|
||||
debug!("get termios = {:?}", termios);
|
||||
write_val_to_user(arg, termios)?;
|
||||
write_val_to_user(arg, &termios)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCGPGRP => {
|
||||
// FIXME: Get the process group ID of the foreground process group on this terminal.
|
||||
let ldist_lock = self.ldisc.lock();
|
||||
let fg_pgid = ldist_lock.get_fg();
|
||||
let fg_pgid = self.ldisc.get_fg();
|
||||
match fg_pgid {
|
||||
None => return_errno_with_message!(Errno::ENOENT, "No fg process group"),
|
||||
Some(fg_pgid) => {
|
||||
debug!("fg_pgid = {}", fg_pgid);
|
||||
write_val_to_user(arg, fg_pgid)?;
|
||||
write_val_to_user(arg, &fg_pgid)?;
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
|
@ -106,16 +104,14 @@ impl File for Tty {
|
|||
IoctlCmd::TIOCSPGRP => {
|
||||
// Set the process group id of fg progress group
|
||||
let pgid = read_val_from_user::<i32>(arg)?;
|
||||
let mut ldist_lock = self.ldisc.lock();
|
||||
ldist_lock.set_fg(pgid);
|
||||
self.ldisc.set_fg(pgid);
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TCSETS => {
|
||||
// Set terminal attributes
|
||||
let termios = read_val_from_user(arg)?;
|
||||
debug!("set termios = {:?}", termios);
|
||||
let mut ldist_lock = self.ldisc.lock();
|
||||
ldist_lock.set_termios(termios);
|
||||
self.ldisc.set_termios(termios);
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCGWINSZ => {
|
||||
|
|
@ -125,6 +121,25 @@ impl File for Tty {
|
|||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
Metadata {
|
||||
dev: 0,
|
||||
ino: 0,
|
||||
size: 0,
|
||||
blk_size: 1024,
|
||||
blocks: 0,
|
||||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
type_: InodeType::CharDevice,
|
||||
mode: InodeMode::from_bits_truncate(0o666),
|
||||
nlinks: 1,
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
rdev: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// FIXME: should we maintain a static console?
|
||||
|
|
|
|||
Loading…
Reference in New Issue