fix dead lock in tty

This commit is contained in:
Jianfeng Jiang 2023-03-21 23:52:24 -04:00 committed by Tate, Hongliang Tian
parent 6e29996720
commit 16b435e310
6 changed files with 89 additions and 64 deletions

2
.gitattributes vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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