diff --git a/ch5/user/Cargo.toml b/ch5/user/Cargo.toml index 5e47670..63f517a 100644 --- a/ch5/user/Cargo.toml +++ b/ch5/user/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } +buddy_system_allocator = "0.6" [profile.release] debug = true diff --git a/ch5/user/Makefile b/ch5/user/Makefile index 984ed3e..ae2a2d2 100644 --- a/ch5/user/Makefile +++ b/ch5/user/Makefile @@ -25,4 +25,4 @@ build: build_elf clean: - rm -rf ./target* \ No newline at end of file + #rm -rf ./target* \ No newline at end of file diff --git a/ch5/user/src/bin/initproc.rs b/ch5/user/src/bin/initproc.rs new file mode 100644 index 0000000..01012b5 --- /dev/null +++ b/ch5/user/src/bin/initproc.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] + +use user_lib::syscall::{sys_fork, sys_yield, sys_exec}; +use user_lib::{println, wait}; + +#[no_mangle] +fn main() -> i32 { + if sys_fork() == 0 { + // 注意需要手动添加\0 转为 c_str + sys_exec("user_shell\0"); + } else { + loop { + let mut exit_code: i32 = 0; + let pid = wait(&mut exit_code); + if pid == -1 { + sys_yield(); + continue; + } + println!( + "[initproc] Released a zombie process, pid={}, exit_code={}", + pid, exit_code, + ); + } + } + 0 +} diff --git a/ch5/user/src/bin/user_shell.rs b/ch5/user/src/bin/user_shell.rs new file mode 100644 index 0000000..e771f86 --- /dev/null +++ b/ch5/user/src/bin/user_shell.rs @@ -0,0 +1,70 @@ +#![no_std] +#![no_main] +#![allow(clippy::println_empty_string)] + +extern crate alloc; + +const LF: u8 = 0x0au8; +const CR: u8 = 0x0du8; +const DL: u8 = 0x7fu8; +const BS: u8 = 0x08u8; + +use alloc::string::String; +use user_lib::{ print, println, waitpid}; +use user_lib::syscall::{sys_exec, sys_yield, sys_fork}; +use user_lib::user_console::getchar; + + +#[no_mangle] +pub fn main() -> i32 { + println!("Rust user shell"); + + // line变量 维护着当前输入的内容, 它不断发生变化 + let mut line: String = String::new(); + print!(">> "); + loop { + // 循环读取标准输入 + let c = getchar(); + + match c { + // 如果是 回车键, 开启一个新的进程, 把 line中的内容 使用exec 执行 + LF | CR => { + println!(""); + if !line.is_empty() { + line.push('\0'); + let pid = sys_fork(); + if pid == 0 { + // child process + if sys_exec(line.as_str()) == -1 { + // 返回 -1 说明没有存在 line中的程序 + println!("Error when executing!"); + return -4; + } + unreachable!(); + } else { + // 等待上方子进程结束 + let mut exit_code: i32 = 0; + let exit_pid = waitpid(pid as usize, &mut exit_code); + assert_eq!(pid, exit_pid); + println!("Shell: Process {} exited with code {}", pid, exit_code); + } + line.clear(); + } + print!(">> "); + } + // 如果是退格键 + BS | 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); + } + } + } +} diff --git a/ch5/user/src/lib.rs b/ch5/user/src/lib.rs index 8ec796b..694219c 100644 --- a/ch5/user/src/lib.rs +++ b/ch5/user/src/lib.rs @@ -8,12 +8,25 @@ pub use user_lang_items::*; pub mod syscall; 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(); // 只要使用这个包, 那么这里就会作为bin程序的开始 #[no_mangle] #[link_section = ".text.entry"] // 链接到指定的段 pub extern "C" fn _start() -> ! { + unsafe { + HEAP.lock() + .init(HEAP_SPACE.as_ptr() as usize, USER_HEAP_SIZE); + } + sys_exit(main()); // 这里执行的main程序是我们 bin文件夹里面的main, 而不是下面的, 下面的main程序只有在bin程序没有main函数的时候才会链接 // 正常是不会走到这一步的, 因为上面已经退出了程序 panic!("unreachable after sys_exit!"); @@ -24,3 +37,27 @@ pub extern "C" fn _start() -> ! { fn main() -> i32 { panic!("Cannot find main!"); } + +pub fn wait(exit_code: &mut i32) -> isize { + loop { + match sys_waitpid(-1, exit_code as *mut _) { + -2 => { + sys_yield(); + } + // -1 or a real pid + exit_pid => return exit_pid, + } + } +} + +pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize { + loop { + match sys_waitpid(pid as isize, exit_code as *mut _) { + -2 => { + sys_yield(); + } + // -1 or a real pid + exit_pid => return exit_pid, + } + } +} diff --git a/ch5/user/src/syscall.rs b/ch5/user/src/syscall.rs index 8612fe7..1b8ab7a 100644 --- a/ch5/user/src/syscall.rs +++ b/ch5/user/src/syscall.rs @@ -1,10 +1,15 @@ use core::arch::asm; +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_SBRK: usize = 214; +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 { @@ -43,4 +48,25 @@ pub fn sys_sbrk(size: i32) -> isize { syscall(SYSCALL_SBRK, [size as usize, 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, exit_code: *mut i32) -> isize { + syscall(SYSCALL_WAITPID, [pid as usize, exit_code as usize, 0]) +} +pub fn sys_read(fd: usize, buffer: &mut [u8]) -> isize { + syscall( + SYSCALL_READ, + [fd, buffer.as_mut_ptr() as usize, buffer.len()], + ) +} diff --git a/ch5/user/src/user_lang_items/user_console.rs b/ch5/user/src/user_lang_items/user_console.rs index 39f3daf..8bb791d 100644 --- a/ch5/user/src/user_lang_items/user_console.rs +++ b/ch5/user/src/user_lang_items/user_console.rs @@ -1,8 +1,9 @@ use core::fmt::{Arguments, Write, Result}; -use crate::syscall::sys_write; +use crate::syscall::{sys_read, sys_write}; struct Stdout; +const STDIN: usize = 0; // 读取 标准输入的标志 const STDOUT: usize = 1; impl Write for Stdout { @@ -12,6 +13,14 @@ impl Write for Stdout { } } +// 每次从标准输入读出一个字符 +pub fn getchar() -> u8 { + let mut c = [0u8; 1]; + sys_read(STDIN, &mut c); + c[0] +} + + pub fn print(args: Arguments) { Stdout.write_fmt(args).unwrap(); }