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 task;
|
||||||
mod timer;
|
mod timer;
|
||||||
mod mm;
|
mod mm;
|
||||||
|
mod fs;
|
||||||
|
|
||||||
global_asm!(include_str!("entry.asm"));
|
global_asm!(include_str!("entry.asm"));
|
||||||
global_asm!(include_str!("link_app.S"));
|
global_asm!(include_str!("link_app.S"));
|
||||||
|
|
|
@ -13,6 +13,8 @@ pub use page_table::{
|
||||||
translated_byte_buffer,
|
translated_byte_buffer,
|
||||||
translated_str,
|
translated_str,
|
||||||
translated_refmut,
|
translated_refmut,
|
||||||
|
UserBuffer,
|
||||||
|
UserBufferIterator,
|
||||||
};
|
};
|
||||||
pub use memory_set::{MemorySet, KERNEL_SPACE, MapPermission};
|
pub use memory_set::{MemorySet, KERNEL_SPACE, MapPermission};
|
||||||
pub use memory_set::remap_test;
|
pub use memory_set::remap_test;
|
||||||
|
|
|
@ -138,9 +138,7 @@ impl PageTable {
|
||||||
pub fn translate_va(&self, va: VirtAddr) -> Option<PhysAddr> {
|
pub fn translate_va(&self, va: VirtAddr) -> Option<PhysAddr> {
|
||||||
self.find_pte(va.clone().floor())
|
self.find_pte(va.clone().floor())
|
||||||
.map(|pte| {
|
.map(|pte| {
|
||||||
//println!("translate_va:va = {:?}", va);
|
|
||||||
let aligned_pa: PhysAddr = pte.ppn().into();
|
let aligned_pa: PhysAddr = pte.ppn().into();
|
||||||
//println!("translate_va:pa_align = {:?}", aligned_pa);
|
|
||||||
let offset = va.page_offset();
|
let offset = va.page_offset();
|
||||||
let aligned_pa_usize: usize = aligned_pa.into();
|
let aligned_pa_usize: usize = aligned_pa.into();
|
||||||
(aligned_pa_usize + offset).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 {
|
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 page_table = PageTable::from_token(token);
|
||||||
let va = ptr as usize;
|
let va = ptr as usize;
|
||||||
//println!("translated_refmut: before translate_va");
|
|
||||||
page_table.translate_va(VirtAddr::from(va)).unwrap().get_mut()
|
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::mm::{UserBuffer, translated_byte_buffer};
|
||||||
use crate::task::{current_user_token, suspend_current_and_run_next};
|
use crate::task::{current_user_token, current_task};
|
||||||
use crate::sbi::console_getchar;
|
|
||||||
|
|
||||||
const FD_STDIN: usize = 0;
|
|
||||||
const FD_STDOUT: usize = 1;
|
|
||||||
|
|
||||||
pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize {
|
pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize {
|
||||||
match fd {
|
let token = current_user_token();
|
||||||
FD_STDOUT => {
|
let task = current_task().unwrap();
|
||||||
let buffers = translated_byte_buffer(current_user_token(), buf, len);
|
let inner = task.acquire_inner_lock();
|
||||||
for buffer in buffers {
|
if fd >= inner.fd_table.len() {
|
||||||
print!("{}", core::str::from_utf8(buffer).unwrap());
|
return -1;
|
||||||
}
|
}
|
||||||
len as isize
|
if let Some(file) = &inner.fd_table[fd] {
|
||||||
},
|
let file = file.clone();
|
||||||
_ => {
|
// release Task lock manually to avoid deadlock
|
||||||
panic!("Unsupported fd in sys_write!");
|
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 {
|
pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize {
|
||||||
match fd {
|
let token = current_user_token();
|
||||||
FD_STDIN => {
|
let task = current_task().unwrap();
|
||||||
assert_eq!(len, 1, "Only support len = 1 in sys_read!");
|
let inner = task.acquire_inner_lock();
|
||||||
let mut c: usize;
|
if fd >= inner.fd_table.len() {
|
||||||
loop {
|
return -1;
|
||||||
c = console_getchar();
|
}
|
||||||
if c == 0 {
|
if let Some(file) = &inner.fd_table[fd] {
|
||||||
suspend_current_and_run_next();
|
let file = file.clone();
|
||||||
continue;
|
// release Task lock manually to avoid deadlock
|
||||||
} else {
|
drop(inner);
|
||||||
break;
|
file.read(UserBuffer {
|
||||||
}
|
buffers: translated_byte_buffer(token, buf, len),
|
||||||
}
|
}) as isize
|
||||||
let ch = c as u8;
|
} else {
|
||||||
let mut buffers = translated_byte_buffer(current_user_token(), buf, len);
|
-1
|
||||||
unsafe { buffers[0].as_mut_ptr().write_volatile(ch); }
|
|
||||||
1
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
panic!("Unsupported fd in sys_read!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,8 +4,10 @@ use crate::config::{TRAP_CONTEXT};
|
||||||
use super::TaskContext;
|
use super::TaskContext;
|
||||||
use super::{PidHandle, pid_alloc, KernelStack};
|
use super::{PidHandle, pid_alloc, KernelStack};
|
||||||
use alloc::sync::{Weak, Arc};
|
use alloc::sync::{Weak, Arc};
|
||||||
|
use alloc::vec;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use spin::{Mutex, MutexGuard};
|
use spin::{Mutex, MutexGuard};
|
||||||
|
use crate::fs::{File, Stdin, Stdout};
|
||||||
|
|
||||||
pub struct TaskControlBlock {
|
pub struct TaskControlBlock {
|
||||||
// immutable
|
// immutable
|
||||||
|
@ -24,6 +26,7 @@ pub struct TaskControlBlockInner {
|
||||||
pub parent: Option<Weak<TaskControlBlock>>,
|
pub parent: Option<Weak<TaskControlBlock>>,
|
||||||
pub children: Vec<Arc<TaskControlBlock>>,
|
pub children: Vec<Arc<TaskControlBlock>>,
|
||||||
pub exit_code: i32,
|
pub exit_code: i32,
|
||||||
|
pub fd_table: Vec<Option<Arc<dyn File + Send + Sync>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TaskControlBlockInner {
|
impl TaskControlBlockInner {
|
||||||
|
@ -74,6 +77,10 @@ impl TaskControlBlock {
|
||||||
parent: None,
|
parent: None,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
exit_code: 0,
|
exit_code: 0,
|
||||||
|
fd_table: vec![
|
||||||
|
Some(Arc::new(Stdin)),
|
||||||
|
Some(Arc::new(Stdout)),
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
// prepare TrapContext in user space
|
// prepare TrapContext in user space
|
||||||
|
@ -136,6 +143,15 @@ impl TaskControlBlock {
|
||||||
let kernel_stack_top = kernel_stack.get_top();
|
let kernel_stack_top = kernel_stack.get_top();
|
||||||
// push a goto_trap_return task_cx on the top of kernel stack
|
// 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());
|
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 {
|
let task_control_block = Arc::new(TaskControlBlock {
|
||||||
pid: pid_handle,
|
pid: pid_handle,
|
||||||
kernel_stack,
|
kernel_stack,
|
||||||
|
@ -148,6 +164,7 @@ impl TaskControlBlock {
|
||||||
parent: Some(Arc::downgrade(self)),
|
parent: Some(Arc::downgrade(self)),
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
exit_code: 0,
|
exit_code: 0,
|
||||||
|
fd_table: new_fd_table,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
// add child
|
// add child
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue