From 81bef97f0942f6f3d432c366f96d580485e2f77f Mon Sep 17 00:00:00 2001 From: Yifan Wu Date: Wed, 9 Dec 2020 09:56:06 +0800 Subject: [PATCH] Add user program initproc/user_shell, allow user programs allocate data on heap. --- user/Cargo.toml | 1 + user/src/bin/03sleep.rs | 8 ++--- user/src/bin/initproc.rs | 30 +++++++++++++++++++ user/src/bin/user_shell.rs | 61 ++++++++++++++++++++++++++++++++++++++ user/src/console.rs | 16 ++++++++-- user/src/lib.rs | 37 ++++++++++++++++++++--- user/src/syscall.rs | 32 +++++++++++++++++--- 7 files changed, 170 insertions(+), 15 deletions(-) create mode 100644 user/src/bin/initproc.rs create mode 100644 user/src/bin/user_shell.rs diff --git a/user/Cargo.toml b/user/Cargo.toml index 817ca525..aa6e20fe 100644 --- a/user/Cargo.toml +++ b/user/Cargo.toml @@ -7,3 +7,4 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +buddy_system_allocator = "0.6" \ No newline at end of file diff --git a/user/src/bin/03sleep.rs b/user/src/bin/03sleep.rs index f3d9f31b..d500f5af 100644 --- a/user/src/bin/03sleep.rs +++ b/user/src/bin/03sleep.rs @@ -4,14 +4,14 @@ #[macro_use] extern crate user_lib; -use user_lib::{sys_get_time, sys_yield}; +use user_lib::{get_time, yield_}; #[no_mangle] fn main() -> i32 { - let current_timer = sys_get_time(); + let current_timer = get_time(); let wait_for = current_timer + 10000000; - while sys_get_time() < wait_for { - sys_yield(); + while get_time() < wait_for { + yield_(); } println!("Test sleep OK!"); 0 diff --git a/user/src/bin/initproc.rs b/user/src/bin/initproc.rs new file mode 100644 index 00000000..2ceb8488 --- /dev/null +++ b/user/src/bin/initproc.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{ + fork, + wait, + exec, + yield_, +}; + +#[no_mangle] +fn main() -> i32 { + if fork() == 0 { + exec("user_shell\0"); + } else { + loop { + let mut xstatus: i32 = 0; + let pid = wait(&mut xstatus); + if pid == -1 { + yield_(); + continue; + } + println!("[initproc] Release a zombie process!"); + } + } + 0 +} \ No newline at end of file diff --git a/user/src/bin/user_shell.rs b/user/src/bin/user_shell.rs new file mode 100644 index 00000000..35dfbcf1 --- /dev/null +++ b/user/src/bin/user_shell.rs @@ -0,0 +1,61 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +#[macro_use] +extern crate user_lib; + +const LF: u8 = 0x0au8; +const CR: u8 = 0x0du8; +const DL: u8 = 0x7fu8; +const BS: u8 = 0x08u8; + +use alloc::string::String; +use user_lib::{fork, exec, wait}; +use user_lib::console::getchar; + +#[no_mangle] +pub fn main() -> i32 { + println!("Rust user shell"); + let mut line: String = String::new(); + print!(">> "); + loop { + let c = getchar(); + match c { + LF | CR => { + println!(""); + if !line.is_empty() { + line.push('\0'); + let pid = fork(); + if pid == 0 { + // child process + if exec(line.as_str()) == -1 { + println!("Command not found!"); + return 0; + } + unreachable!(); + } else { + let mut xstate: i32 = 0; + wait(&mut xstate); + println!("Shell: Process {} exited with code {}", pid, xstate); + } + line.clear(); + } + print!(">> "); + } + DL => { + if !line.is_empty() { + print!("{}", BS as char); + print!(" "); + print!("{}", BS as char); + line.pop(); + } + } + _ => { + print!("{}", c as char); + line.push(c as char); + } + } + } +} \ No newline at end of file diff --git a/user/src/console.rs b/user/src/console.rs index a826b8fe..810ebba1 100644 --- a/user/src/console.rs +++ b/user/src/console.rs @@ -1,11 +1,15 @@ use core::fmt::{self, Write}; -use crate::syscall::{STDOUT, sys_write}; + +const STDIN: usize = 0; +const STDOUT: usize = 1; + +use super::{read, write}; struct Stdout; impl Write for Stdout { fn write_str(&mut self, s: &str) -> fmt::Result { - sys_write(STDOUT, s.as_bytes()); + write(STDOUT, s.as_bytes()); Ok(()) } } @@ -26,4 +30,10 @@ macro_rules! println { ($fmt: literal $(, $($arg: tt)+)?) => { $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)); } -} \ No newline at end of file +} + +pub fn getchar() -> u8 { + let mut c = [0u8; 1]; + read(STDIN, &mut c); + c[0] +} diff --git a/user/src/lib.rs b/user/src/lib.rs index c072ef49..c71798ff 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -2,17 +2,36 @@ #![feature(llvm_asm)] #![feature(linkage)] #![feature(panic_info_message)] +#![feature(alloc_error_handler)] #[macro_use] pub mod console; mod syscall; mod lang_items; +use syscall::*; +use buddy_system_allocator::LockedHeap; + +const USER_HEAP_SIZE: usize = 16384; + +static mut HEAP_SPACE: [u8; USER_HEAP_SIZE] = [0; USER_HEAP_SIZE]; + +#[global_allocator] +static HEAP: LockedHeap = LockedHeap::empty(); + +#[alloc_error_handler] +pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! { + panic!("Heap allocation error, layout = {:?}", layout); +} + #[no_mangle] #[link_section = ".text.entry"] pub extern "C" fn _start() -> ! { - syscall::sys_exit(main()); - panic!("unreachable after sys_exit!"); + unsafe { + HEAP.lock() + .init(HEAP_SPACE.as_ptr() as usize, USER_HEAP_SIZE); + } + exit(main()); } #[linkage = "weak"] @@ -21,5 +40,15 @@ fn main() -> i32 { panic!("Cannot find main!"); } - -pub use syscall::*; \ No newline at end of file +pub fn read(fd: usize, buf: &mut [u8]) -> isize { sys_read(fd, buf) } +pub fn write(fd: usize, buf: &[u8]) -> isize { sys_write(fd, buf) } +pub fn exit(xstate: i32) -> ! { sys_exit(xstate); } +pub fn yield_() -> isize { sys_yield() } +pub fn get_time() -> isize { sys_get_time() } +pub fn getpid() -> isize { sys_getpid() } +pub fn fork() -> isize { sys_fork() } +pub fn exec(path: &str) -> isize { sys_exec(path) } +pub fn wait(xstate: &mut i32) -> isize { sys_waitpid(-1, xstate as *mut _) } +pub fn waitpid(pid: usize, xstate: &mut i32) -> isize { + sys_waitpid(pid as isize, xstate as *mut _) +} \ No newline at end of file diff --git a/user/src/syscall.rs b/user/src/syscall.rs index 8e4ff50b..c5e23538 100644 --- a/user/src/syscall.rs +++ b/user/src/syscall.rs @@ -1,9 +1,12 @@ -pub const STDOUT: usize = 1; - +const SYSCALL_READ: usize = 63; const SYSCALL_WRITE: usize = 64; const SYSCALL_EXIT: usize = 93; const SYSCALL_YIELD: usize = 124; const SYSCALL_GET_TIME: usize = 169; +const SYSCALL_GETPID: usize = 172; +const SYSCALL_FORK: usize = 220; +const SYSCALL_EXEC: usize = 221; +const SYSCALL_WAITPID: usize = 260; fn syscall(id: usize, args: [usize; 3]) -> isize { let mut ret: isize; @@ -18,12 +21,17 @@ fn syscall(id: usize, args: [usize; 3]) -> isize { ret } +pub fn sys_read(fd: usize, buffer: &mut [u8]) -> isize { + syscall(SYSCALL_READ, [fd, buffer.as_mut_ptr() as usize, buffer.len()]) +} + pub fn sys_write(fd: usize, buffer: &[u8]) -> isize { syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()]) } -pub fn sys_exit(xstate: i32) -> isize { - syscall(SYSCALL_EXIT, [xstate as usize, 0, 0]) +pub fn sys_exit(xstate: i32) -> ! { + syscall(SYSCALL_EXIT, [xstate as usize, 0, 0]); + panic!("sys_exit never returns!"); } pub fn sys_yield() -> isize { @@ -32,4 +40,20 @@ pub fn sys_yield() -> isize { pub fn sys_get_time() -> isize { syscall(SYSCALL_GET_TIME, [0, 0, 0]) +} + +pub fn sys_getpid() -> isize { + syscall(SYSCALL_GETPID, [0, 0, 0]) +} + +pub fn sys_fork() -> isize { + syscall(SYSCALL_FORK, [0, 0, 0]) +} + +pub fn sys_exec(path: &str) -> isize { + syscall(SYSCALL_EXEC, [path.as_ptr() as usize, 0, 0]) +} + +pub fn sys_waitpid(pid: isize, xstatus: *mut i32) -> isize { + syscall(SYSCALL_WAITPID, [pid as usize, xstatus as usize, 0]) } \ No newline at end of file