diff --git a/os/src/fs/mod.rs b/os/src/fs/mod.rs new file mode 100644 index 00000000..e481a78f --- /dev/null +++ b/os/src/fs/mod.rs @@ -0,0 +1,20 @@ +mod pipe; +mod stdio; + +use crate::mm::UserBuffer; +use core::any::Any; + +pub trait File : Any + Send + Sync { + fn read(&self, buf: UserBuffer) -> usize; + fn write(&self, buf: UserBuffer) -> usize; + fn as_any_ref(&self) -> &dyn Any; +} + +impl dyn File { + pub fn downcast_ref(&self) -> Option<&T> { + self.as_any_ref().downcast_ref::() + } +} + +pub use pipe::{Pipe}; +pub use stdio::{Stdin, Stdout}; \ No newline at end of file diff --git a/os/src/fs/pipe.rs b/os/src/fs/pipe.rs new file mode 100644 index 00000000..266c43a0 --- /dev/null +++ b/os/src/fs/pipe.rs @@ -0,0 +1,167 @@ +use super::File; +use alloc::sync::{Arc, Weak}; +use spin::Mutex; +use crate::mm::{ + UserBuffer, +}; +use crate::task::suspend_current_and_run_next; +use core::any::Any; + +pub struct Pipe { + readable: bool, + writable: bool, + buffer: Arc>, +} + +impl Pipe { + pub fn read_end_with_buffer(buffer: Arc>) -> Self { + Self { + readable: true, + writable: false, + buffer, + } + } + pub fn write_end_with_buffer(buffer: Arc>) -> Self { + Self { + readable: false, + writable: true, + buffer, + } + } +} + +const RING_BUFFER_SIZE: usize = 32; + +#[derive(Copy, Clone, PartialEq)] +enum RingBufferStatus { + FULL, + EMPTY, + NORMAL, +} + +pub struct PipeRingBuffer { + arr: [u8; RING_BUFFER_SIZE], + head: usize, + tail: usize, + status: RingBufferStatus, + write_end: Option>>, +} + +impl PipeRingBuffer { + pub fn new() -> Self { + Self { + arr: [0; RING_BUFFER_SIZE], + head: 0, + tail: 0, + status: RingBufferStatus::EMPTY, + write_end: None, + } + } + pub fn set_write_end(&mut self, write_end: &Arc>) { + self.write_end = Some(Arc::downgrade(write_end)); + } + pub fn write_byte(&mut self, byte: u8) { + self.status = RingBufferStatus::NORMAL; + self.arr[self.tail] = byte; + self.tail = (self.tail + 1) % RING_BUFFER_SIZE; + if self.tail == self.head { + self.status = RingBufferStatus::FULL; + } + } + pub fn read_byte(&mut self) -> u8 { + self.status = RingBufferStatus::NORMAL; + let c = self.arr[self.head]; + self.head = (self.head + 1) % RING_BUFFER_SIZE; + if self.head == self.tail { + self.status = RingBufferStatus::EMPTY; + } + c + } + pub fn available_read(&self) -> usize { + if self.status == RingBufferStatus::EMPTY { + 0 + } else { + if self.tail > self.head { + self.tail - self.head + } else { + self.tail + RING_BUFFER_SIZE - self.head + } + } + } + pub fn available_write(&self) -> usize { + if self.status == RingBufferStatus::FULL { + 0 + } else { + RING_BUFFER_SIZE - self.available_read() + } + } + pub fn all_write_ends_closed(&self) -> bool { + self.write_end.as_ref().unwrap().upgrade().is_none() + } +} + +/// Return (read_end, write_end) +pub fn make_pipe() -> (Arc>, Arc>) { + let buffer = Arc::new(Mutex::new(PipeRingBuffer::new())); + let read_end = Arc::new(Mutex::new( + Pipe::read_end_with_buffer(buffer.clone()) + )); + let write_end = Arc::new(Mutex::new( + Pipe::write_end_with_buffer(buffer.clone()) + )); + buffer.lock().set_write_end(&write_end); + (read_end, write_end) +} + +impl File for Pipe { + fn read(&self, buf: UserBuffer) -> usize { + assert_eq!(self.readable, true); + let mut buf_iter = buf.into_iter(); + let mut read_size = 0usize; + loop { + let mut ring_buffer = self.buffer.lock(); + let loop_read = ring_buffer.available_read(); + if loop_read == 0 { + if ring_buffer.all_write_ends_closed() { + return read_size; + } + drop(ring_buffer); + suspend_current_and_run_next(); + continue; + } + // read at most loop_read bytes + for _ in 0..loop_read { + if let Some(byte_ref) = buf_iter.next() { + unsafe { *byte_ref = ring_buffer.read_byte(); } + read_size += 1; + } else { + return read_size; + } + } + } + } + fn write(&self, buf: UserBuffer) -> usize { + assert_eq!(self.writable, true); + let mut buf_iter = buf.into_iter(); + let mut write_size = 0usize; + loop { + let mut ring_buffer = self.buffer.lock(); + let loop_write = ring_buffer.available_write(); + if loop_write == 0 { + drop(ring_buffer); + suspend_current_and_run_next(); + continue; + } + // write at most loop_write bytes + for _ in 0..loop_write { + if let Some(byte_ref) = buf_iter.next() { + ring_buffer.write_byte(unsafe { *byte_ref }); + write_size += 1; + } else { + return write_size; + } + } + } + } + fn as_any_ref(&self) -> &dyn Any { self } +} \ No newline at end of file diff --git a/os/src/fs/stdio.rs b/os/src/fs/stdio.rs new file mode 100644 index 00000000..4854118b --- /dev/null +++ b/os/src/fs/stdio.rs @@ -0,0 +1,46 @@ +use super::File; +use crate::mm::{UserBuffer, UserBufferIterator}; +use crate::sbi::console_getchar; +use crate::task::suspend_current_and_run_next; +use core::any::Any; + +pub struct Stdin; + +pub struct Stdout; + +impl File for Stdin { + fn read(&self, mut user_buf: UserBuffer) -> usize { + assert_eq!(user_buf.len(), 1); + // busy loop + let mut c: usize; + loop { + c = console_getchar(); + if c == 0 { + suspend_current_and_run_next(); + continue; + } else { + break; + } + } + let ch = c as u8; + unsafe { user_buf.buffers[0].as_mut_ptr().write_volatile(ch); } + 1 + } + fn write(&self, _user_buf: UserBuffer) -> usize { + panic!("Cannot write to stdin!"); + } + fn as_any_ref(&self) -> &dyn Any { self } +} + +impl File for Stdout { + fn read(&self, _user_buf: UserBuffer) -> usize{ + panic!("Cannot read from stdout!"); + } + fn write(&self, user_buf: UserBuffer) -> usize { + for buffer in user_buf.buffers.iter() { + print!("{}", core::str::from_utf8(*buffer).unwrap()); + } + user_buf.len() + } + fn as_any_ref(&self) -> &dyn Any { self } +} \ No newline at end of file diff --git a/os/src/main.rs b/os/src/main.rs index aa21a5e2..02ebaeb2 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -22,6 +22,7 @@ mod config; mod task; mod timer; mod mm; +mod fs; global_asm!(include_str!("entry.asm")); global_asm!(include_str!("link_app.S")); diff --git a/os/src/mm/mod.rs b/os/src/mm/mod.rs index cdf71da1..350b4fad 100644 --- a/os/src/mm/mod.rs +++ b/os/src/mm/mod.rs @@ -13,6 +13,8 @@ pub use page_table::{ translated_byte_buffer, translated_str, translated_refmut, + UserBuffer, + UserBufferIterator, }; pub use memory_set::{MemorySet, KERNEL_SPACE, MapPermission}; pub use memory_set::remap_test; diff --git a/os/src/mm/page_table.rs b/os/src/mm/page_table.rs index 3c2f7f6f..93364e18 100644 --- a/os/src/mm/page_table.rs +++ b/os/src/mm/page_table.rs @@ -138,9 +138,7 @@ impl PageTable { pub fn translate_va(&self, va: VirtAddr) -> Option { self.find_pte(va.clone().floor()) .map(|pte| { - //println!("translate_va:va = {:?}", va); let aligned_pa: PhysAddr = pte.ppn().into(); - //println!("translate_va:pa_align = {:?}", aligned_pa); let offset = va.page_offset(); let aligned_pa_usize: usize = aligned_pa.into(); (aligned_pa_usize + offset).into() @@ -189,9 +187,60 @@ pub fn translated_str(token: usize, ptr: *const u8) -> String { } pub fn translated_refmut(token: usize, ptr: *mut T) -> &'static mut T { - //println!("into translated_refmut!"); let page_table = PageTable::from_token(token); let va = ptr as usize; - //println!("translated_refmut: before translate_va"); page_table.translate_va(VirtAddr::from(va)).unwrap().get_mut() +} + +pub struct UserBuffer { + pub buffers: Vec<&'static mut [u8]>, +} + +impl UserBuffer { + pub fn new(buffers: Vec<&'static mut [u8]>) -> Self { + Self { buffers } + } + pub fn len(&self) -> usize { + let mut total: usize = 0; + for b in self.buffers.iter() { + total += b.len(); + } + total + } +} + +impl IntoIterator for UserBuffer { + type Item = *mut u8; + type IntoIter = UserBufferIterator; + fn into_iter(self) -> Self::IntoIter { + UserBufferIterator { + buffers: self.buffers, + current_buffer: 0, + current_idx: 0, + } + } +} + +pub struct UserBufferIterator { + buffers: Vec<&'static mut [u8]>, + current_buffer: usize, + current_idx: usize, +} + +impl Iterator for UserBufferIterator { + type Item = *mut u8; + fn next(&mut self) -> Option { + if self.current_buffer >= self.buffers.len() { + None + } else { + let r = &mut self.buffers[self.current_buffer][self.current_idx] as *mut _; + if self.current_idx + 1 == self.buffers[self.current_buffer].len() { + self.current_idx = 0; + self.current_buffer += 1; + } else { + self.current_idx += 1; + } + Some(r) + } + } } \ No newline at end of file diff --git a/os/src/syscall/fs.rs b/os/src/syscall/fs.rs index a10b53e9..1192af17 100644 --- a/os/src/syscall/fs.rs +++ b/os/src/syscall/fs.rs @@ -1,46 +1,40 @@ -use crate::mm::translated_byte_buffer; -use crate::task::{current_user_token, suspend_current_and_run_next}; -use crate::sbi::console_getchar; - -const FD_STDIN: usize = 0; -const FD_STDOUT: usize = 1; +use crate::mm::{UserBuffer, translated_byte_buffer}; +use crate::task::{current_user_token, current_task}; pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize { - match fd { - FD_STDOUT => { - let buffers = translated_byte_buffer(current_user_token(), buf, len); - for buffer in buffers { - print!("{}", core::str::from_utf8(buffer).unwrap()); - } - len as isize - }, - _ => { - panic!("Unsupported fd in sys_write!"); - } + let token = current_user_token(); + let task = current_task().unwrap(); + let inner = task.acquire_inner_lock(); + if fd >= inner.fd_table.len() { + return -1; + } + if let Some(file) = &inner.fd_table[fd] { + let file = file.clone(); + // release Task lock manually to avoid deadlock + drop(inner); + file.write(UserBuffer { + buffers: translated_byte_buffer(token, buf, len), + }) as isize + } else { + -1 } } pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize { - match fd { - FD_STDIN => { - assert_eq!(len, 1, "Only support len = 1 in sys_read!"); - let mut c: usize; - loop { - c = console_getchar(); - if c == 0 { - suspend_current_and_run_next(); - continue; - } else { - break; - } - } - let ch = c as u8; - let mut buffers = translated_byte_buffer(current_user_token(), buf, len); - unsafe { buffers[0].as_mut_ptr().write_volatile(ch); } - 1 - } - _ => { - panic!("Unsupported fd in sys_read!"); - } + let token = current_user_token(); + let task = current_task().unwrap(); + let inner = task.acquire_inner_lock(); + if fd >= inner.fd_table.len() { + return -1; + } + if let Some(file) = &inner.fd_table[fd] { + let file = file.clone(); + // release Task lock manually to avoid deadlock + drop(inner); + file.read(UserBuffer { + buffers: translated_byte_buffer(token, buf, len), + }) as isize + } else { + -1 } } \ No newline at end of file diff --git a/os/src/task/task.rs b/os/src/task/task.rs index 21131736..e1be05a4 100644 --- a/os/src/task/task.rs +++ b/os/src/task/task.rs @@ -4,8 +4,10 @@ use crate::config::{TRAP_CONTEXT}; use super::TaskContext; use super::{PidHandle, pid_alloc, KernelStack}; use alloc::sync::{Weak, Arc}; +use alloc::vec; use alloc::vec::Vec; use spin::{Mutex, MutexGuard}; +use crate::fs::{File, Stdin, Stdout}; pub struct TaskControlBlock { // immutable @@ -24,6 +26,7 @@ pub struct TaskControlBlockInner { pub parent: Option>, pub children: Vec>, pub exit_code: i32, + pub fd_table: Vec>>, } impl TaskControlBlockInner { @@ -74,6 +77,10 @@ impl TaskControlBlock { parent: None, children: Vec::new(), exit_code: 0, + fd_table: vec![ + Some(Arc::new(Stdin)), + Some(Arc::new(Stdout)), + ], }), }; // prepare TrapContext in user space @@ -136,6 +143,15 @@ impl TaskControlBlock { let kernel_stack_top = kernel_stack.get_top(); // push a goto_trap_return task_cx on the top of kernel stack let task_cx_ptr = kernel_stack.push_on_top(TaskContext::goto_trap_return()); + // copy fd table + let mut new_fd_table: Vec>> = Vec::new(); + for fd in parent_inner.fd_table.iter() { + if let Some(file) = fd { + new_fd_table.push(Some(file.clone())); + } else { + new_fd_table.push(None); + } + } let task_control_block = Arc::new(TaskControlBlock { pid: pid_handle, kernel_stack, @@ -148,6 +164,7 @@ impl TaskControlBlock { parent: Some(Arc::downgrade(self)), children: Vec::new(), exit_code: 0, + fd_table: new_fd_table, }), }); // add child