Rewrite Stdin/Stdout
This commit is contained in:
parent
e5bd98973d
commit
e93a4a0b76
8 changed files with 338 additions and 42 deletions
20
os/src/fs/mod.rs
Normal file
20
os/src/fs/mod.rs
Normal file
|
@ -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<T: File>(&self) -> Option<&T> {
|
||||
self.as_any_ref().downcast_ref::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
pub use pipe::{Pipe};
|
||||
pub use stdio::{Stdin, Stdout};
|
167
os/src/fs/pipe.rs
Normal file
167
os/src/fs/pipe.rs
Normal file
|
@ -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<Mutex<PipeRingBuffer>>,
|
||||
}
|
||||
|
||||
impl Pipe {
|
||||
pub fn read_end_with_buffer(buffer: Arc<Mutex<PipeRingBuffer>>) -> Self {
|
||||
Self {
|
||||
readable: true,
|
||||
writable: false,
|
||||
buffer,
|
||||
}
|
||||
}
|
||||
pub fn write_end_with_buffer(buffer: Arc<Mutex<PipeRingBuffer>>) -> 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<Weak<Mutex<Pipe>>>,
|
||||
}
|
||||
|
||||
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<Mutex<Pipe>>) {
|
||||
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<Mutex<Pipe>>, Arc<Mutex<Pipe>>) {
|
||||
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 }
|
||||
}
|
46
os/src/fs/stdio.rs
Normal file
46
os/src/fs/stdio.rs
Normal file
|
@ -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 }
|
||||
}
|
|
@ -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"));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -138,9 +138,7 @@ impl PageTable {
|
|||
pub fn translate_va(&self, va: VirtAddr) -> Option<PhysAddr> {
|
||||
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<T>(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<Self::Item> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
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 {
|
||||
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!");
|
||||
}
|
||||
-1
|
||||
}
|
||||
}
|
|
@ -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<Weak<TaskControlBlock>>,
|
||||
pub children: Vec<Arc<TaskControlBlock>>,
|
||||
pub exit_code: i32,
|
||||
pub fd_table: Vec<Option<Arc<dyn File + Send + Sync>>>,
|
||||
}
|
||||
|
||||
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<Option<Arc<dyn File + Send + Sync>>> = 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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue