From 3642f9c56dfcc363a2a6227d32fcd82d3f903046 Mon Sep 17 00:00:00 2001 From: Yifan Wu Date: Thu, 10 Dec 2020 11:57:26 +0800 Subject: [PATCH] Implement many process syscalls. --- os/src/config.rs | 6 -- os/src/loader.rs | 3 +- os/src/main.rs | 11 +--- os/src/mm/address.rs | 11 +++- os/src/mm/memory_set.rs | 30 ++++++++- os/src/mm/mod.rs | 7 +- os/src/mm/page_table.rs | 46 ++++++++++++- os/src/syscall/fs.rs | 4 +- os/src/syscall/mod.rs | 8 +++ os/src/syscall/process.rs | 79 ++++++++++++++++++++++- os/src/task/manager.rs | 10 +-- os/src/task/mod.rs | 59 ++++++++++++----- os/src/task/pid.rs | 3 +- os/src/task/processor.rs | 27 ++++---- os/src/task/task.rs | 129 +++++++++++++++++++++++++++++++++---- os/src/trap/context.rs | 2 + os/src/trap/mod.rs | 15 +++-- user/src/bin/user_shell.rs | 15 ++++- user/src/lib.rs | 2 +- user/src/syscall.rs | 4 +- 20 files changed, 383 insertions(+), 88 deletions(-) diff --git a/os/src/config.rs b/os/src/config.rs index aac95791..c4b90f77 100644 --- a/os/src/config.rs +++ b/os/src/config.rs @@ -7,12 +7,6 @@ pub const PAGE_SIZE_BITS: usize = 0xc; pub const TRAMPOLINE: usize = usize::MAX - PAGE_SIZE + 1; pub const TRAP_CONTEXT: usize = TRAMPOLINE - PAGE_SIZE; -/// Return (bottom, top) of a kernel stack in kernel space. -pub fn kernel_stack_position(app_id: usize) -> (usize, usize) { - let top = TRAMPOLINE - app_id * (KERNEL_STACK_SIZE + PAGE_SIZE); - let bottom = top - KERNEL_STACK_SIZE; - (bottom, top) -} #[cfg(feature = "board_k210")] pub const CPU_FREQ: usize = 10000000; diff --git a/os/src/loader.rs b/os/src/loader.rs index 1a23b979..4f10ab65 100644 --- a/os/src/loader.rs +++ b/os/src/loader.rs @@ -45,7 +45,8 @@ fn app_names() -> Vec<&'static str> { let slice = core::slice::from_raw_parts(start, end as usize - start as usize); let str = core::str::from_utf8(slice).unwrap(); v.push(str); - start = end.add(1); + // Mention that there is a extra char between names + start = end.add(2); } } v diff --git a/os/src/main.rs b/os/src/main.rs index 0f04c1fe..78c08e2a 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -41,20 +41,11 @@ pub fn rust_main() -> ! { clear_bss(); println!("[kernel] Hello, world!"); mm::init(); - println!("[kernel] back to world!"); mm::remap_test(); - - // add apps - let num_app = loader::get_num_app(); - println!("num_app={}", num_app); - for i in 0..num_app { - println!("i={}", i); - task::add_application(loader::get_app_data(i), i); - } + task::add_initproc(); trap::init(); trap::enable_timer_interrupt(); timer::set_next_trigger(); - println!("before task::run_tasks!"); task::run_tasks(); panic!("Unreachable in rust_main!"); } \ No newline at end of file diff --git a/os/src/mm/address.rs b/os/src/mm/address.rs index dd25b636..5a3d6161 100644 --- a/os/src/mm/address.rs +++ b/os/src/mm/address.rs @@ -110,6 +110,13 @@ impl VirtPageNum { } } +impl PhysAddr { + pub fn get_mut(&self) -> &'static mut T { + unsafe { + (self.0 as *mut T).as_mut().unwrap() + } + } +} impl PhysPageNum { pub fn get_pte_array(&self) -> &'static mut [PageTableEntry] { let pa: PhysAddr = self.clone().into(); @@ -125,9 +132,7 @@ impl PhysPageNum { } pub fn get_mut(&self) -> &'static mut T { let pa: PhysAddr = self.clone().into(); - unsafe { - (pa.0 as *mut T).as_mut().unwrap() - } + pa.get_mut() } } diff --git a/os/src/mm/memory_set.rs b/os/src/mm/memory_set.rs index 5247cbdf..55c04614 100644 --- a/os/src/mm/memory_set.rs +++ b/os/src/mm/memory_set.rs @@ -47,6 +47,7 @@ impl MemorySet { areas: Vec::new(), } } + #[allow(unused)] pub fn dealloc_all_frames(&mut self) { *self = Self::new_bare(); } @@ -68,7 +69,6 @@ impl MemorySet { area.unmap(&mut self.page_table); self.areas.remove(idx); } - panic!("Area not found!"); } fn push(&mut self, mut map_area: MapArea, data: Option<&[u8]>) { map_area.map(&mut self.page_table); @@ -189,6 +189,23 @@ impl MemorySet { ), None); (memory_set, user_stack_top, elf.header.pt2.entry_point() as usize) } + pub fn from_existed_user(user_space: &MemorySet) -> MemorySet { + let mut memory_set = Self::new_bare(); + // map trampoline + memory_set.map_trampoline(); + // copy data sections/trap_context/user_stack + for area in user_space.areas.iter() { + let new_area = MapArea::from_another(area); + memory_set.push(new_area, None); + // copy data from another space + for vpn in area.vpn_range { + let src_ppn = user_space.translate(vpn).unwrap().ppn(); + let dst_ppn = memory_set.translate(vpn).unwrap().ppn(); + dst_ppn.get_bytes_array().copy_from_slice(src_ppn.get_bytes_array()); + } + } + memory_set + } pub fn activate(&self) { let satp = self.page_table.token(); unsafe { @@ -199,6 +216,9 @@ impl MemorySet { pub fn translate(&self, vpn: VirtPageNum) -> Option { self.page_table.translate(vpn) } + pub fn clear(&mut self) { + *self = Self::new_bare(); + } } pub struct MapArea { @@ -224,6 +244,14 @@ impl MapArea { map_perm, } } + pub fn from_another(another: &MapArea) -> Self { + Self { + vpn_range: VPNRange::new(another.vpn_range.get_start(), another.vpn_range.get_end()), + data_frames: BTreeMap::new(), + map_type: another.map_type, + map_perm: another.map_perm, + } + } pub fn map_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) { let ppn: PhysPageNum; match self.map_type { diff --git a/os/src/mm/mod.rs b/os/src/mm/mod.rs index 0118b194..cdf71da1 100644 --- a/os/src/mm/mod.rs +++ b/os/src/mm/mod.rs @@ -8,7 +8,12 @@ use page_table::{PageTable, PTEFlags}; use address::{VPNRange, StepByOne}; pub use address::{PhysAddr, VirtAddr, PhysPageNum, VirtPageNum}; pub use frame_allocator::{FrameTracker, frame_alloc}; -pub use page_table::{PageTableEntry, translated_byte_buffer}; +pub use page_table::{ + PageTableEntry, + translated_byte_buffer, + translated_str, + translated_refmut, +}; 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 bb21aef1..3c2f7f6f 100644 --- a/os/src/mm/page_table.rs +++ b/os/src/mm/page_table.rs @@ -1,6 +1,15 @@ -use super::{frame_alloc, PhysPageNum, FrameTracker, VirtPageNum, VirtAddr, StepByOne}; +use super::{ + frame_alloc, + PhysPageNum, + FrameTracker, + VirtPageNum, + VirtAddr, + PhysAddr, + StepByOne +}; use alloc::vec::Vec; use alloc::vec; +use alloc::string::String; use bitflags::*; bitflags! { @@ -126,6 +135,17 @@ impl PageTable { self.find_pte(vpn) .map(|pte| {pte.clone()}) } + 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() + }) + } pub fn token(&self) -> usize { 8usize << 60 | self.root_ppn.0 } @@ -150,4 +170,28 @@ pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<& start = end_va.into(); } v +} + +pub fn translated_str(token: usize, ptr: *const u8) -> String { + let page_table = PageTable::from_token(token); + let mut string = String::new(); + let mut va = ptr as usize; + loop { + let ch: u8 = *(page_table.translate_va(VirtAddr::from(va)).unwrap().get_mut()); + if ch == 0 { + break; + } else { + string.push(ch as char); + va += 1; + } + } + 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() } \ No newline at end of file diff --git a/os/src/syscall/fs.rs b/os/src/syscall/fs.rs index a10b53e9..39ef5f48 100644 --- a/os/src/syscall/fs.rs +++ b/os/src/syscall/fs.rs @@ -1,5 +1,5 @@ use crate::mm::translated_byte_buffer; -use crate::task::{current_user_token, suspend_current_and_run_next}; +use crate::task::{current_user_token}; use crate::sbi::console_getchar; const FD_STDIN: usize = 0; @@ -28,7 +28,7 @@ pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize { loop { c = console_getchar(); if c == 0 { - suspend_current_and_run_next(); + //suspend_current_and_run_next(); continue; } else { break; diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs index c3f39c33..81ade837 100644 --- a/os/src/syscall/mod.rs +++ b/os/src/syscall/mod.rs @@ -3,6 +3,10 @@ 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; mod fs; mod process; @@ -17,6 +21,10 @@ pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { SYSCALL_EXIT => sys_exit(args[0] as i32), SYSCALL_YIELD => sys_yield(), SYSCALL_GET_TIME => sys_get_time(), + SYSCALL_GETPID => sys_getpid(), + SYSCALL_FORK => sys_fork(), + SYSCALL_EXEC => sys_exec(args[0] as *const u8), + SYSCALL_WAITPID => sys_waitpid(args[0] as isize, args[1] as *mut i32), _ => panic!("Unsupported syscall_id: {}", syscall_id), } } diff --git a/os/src/syscall/process.rs b/os/src/syscall/process.rs index e0bdebae..656837e6 100644 --- a/os/src/syscall/process.rs +++ b/os/src/syscall/process.rs @@ -1,11 +1,30 @@ use crate::task::{ suspend_current_and_run_next, exit_current_and_run_next, + current_task, + current_user_token, + add_task, }; use crate::timer::get_time; +use crate::mm::{ + translated_str, + translated_refmut, +}; +use crate::loader::get_app_data_by_name; +use alloc::sync::Arc; + +pub fn sys_exit(exit_code: i32) -> ! { + // save exit code + let task = current_task().unwrap(); + + // ---- hold current PCB lock + task.acquire_inner_lock().exit_code = exit_code; + // ---- release current PCB lock + + // this function will not return + // drop task manually to maintain rc correctly + drop(task); -pub fn sys_exit(xstate: i32) -> ! { - println!("[kernel] Application exited with code {}", xstate); exit_current_and_run_next(); panic!("Unreachable in sys_exit!"); } @@ -17,4 +36,60 @@ pub fn sys_yield() -> isize { pub fn sys_get_time() -> isize { get_time() as isize +} + +pub fn sys_getpid() -> isize { + current_task().unwrap().pid.0 as isize +} + +pub fn sys_fork() -> isize { + let current_task = current_task().unwrap(); + let new_task = current_task.fork(); + let new_pid = new_task.pid.0; + // modify trap context of new_task, because it returns immediately after switching + let trap_cx = new_task.acquire_inner_lock().get_trap_cx(); + // we do not have to move to next instruction since we have done it before + // for child process, fork returns 0 + trap_cx.x[10] = 0; + // add new task to scheduler + add_task(new_task); + new_pid as isize +} + +pub fn sys_exec(path: *const u8) -> isize { + let token = current_user_token(); + let path = translated_str(token, path); + let data = get_app_data_by_name(path.as_str()).unwrap(); + let task = current_task().unwrap(); + task.exec(data); + 0 +} + +pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize { + let task = current_task().unwrap(); + // find a child process + // ---- hold current PCB lock + let mut inner = task.acquire_inner_lock(); + let pair = inner.children + .iter() + .enumerate() + .find(|(_, p)| { + // ++++ temporarily hold child PCB lock + p.acquire_inner_lock().is_zombie() && (pid == -1 || pid as usize == p.getpid()) + // ++++ release child PCB lock + }); + if let Some((idx, _)) = pair { + let child = inner.children.remove(idx); + // confirm that child will be deallocated after removing from children list + assert_eq!(Arc::strong_count(&child), 1); + let found_pid = child.getpid(); + // ++++ temporarily hold child lock + let exit_code = child.acquire_inner_lock().exit_code; + // ++++ release child PCB lock + *translated_refmut(inner.memory_set.token(), exit_code_ptr) = exit_code; + found_pid as isize + } else { + -1 + } + // ---- release current PCB lock automatically } \ No newline at end of file diff --git a/os/src/task/manager.rs b/os/src/task/manager.rs index 1b9a5ad8..ed223916 100644 --- a/os/src/task/manager.rs +++ b/os/src/task/manager.rs @@ -5,7 +5,7 @@ use spin::Mutex; use lazy_static::*; pub struct TaskManager { - ready_queue: VecDeque>>, + ready_queue: VecDeque>, } /// A simple FIFO scheduler. @@ -13,10 +13,10 @@ impl TaskManager { pub fn new() -> Self { Self { ready_queue: VecDeque::new(), } } - pub fn add(&mut self, task: Arc>) { + pub fn add(&mut self, task: Arc) { self.ready_queue.push_back(task); } - pub fn fetch(&mut self) -> Option>> { + pub fn fetch(&mut self) -> Option> { self.ready_queue.pop_front() } } @@ -25,10 +25,10 @@ lazy_static! { pub static ref TASK_MANAGER: Mutex = Mutex::new(TaskManager::new()); } -pub fn add_task(task: Arc>) { +pub fn add_task(task: Arc) { TASK_MANAGER.lock().add(task); } -pub fn fetch_task() -> Option>> { +pub fn fetch_task() -> Option> { TASK_MANAGER.lock().fetch() } \ No newline at end of file diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs index 6aff698e..fd7fea8d 100644 --- a/os/src/task/mod.rs +++ b/os/src/task/mod.rs @@ -5,17 +5,11 @@ mod manager; mod processor; mod pid; -use crate::loader::{get_num_app, get_app_data}; -use crate::trap::TrapContext; -use core::cell::RefCell; -use lazy_static::*; +use crate::loader::{get_app_data_by_name}; use switch::__switch; use task::{TaskControlBlock, TaskStatus}; -use alloc::vec::Vec; use alloc::sync::Arc; -use spin::Mutex; use manager::fetch_task; -use pid::{PidHandle, pid_alloc, KernelStack}; pub use context::TaskContext; pub use processor::{ @@ -27,13 +21,21 @@ pub use processor::{ schedule, }; pub use manager::add_task; +pub use pid::{PidHandle, pid_alloc, KernelStack}; pub fn suspend_current_and_run_next() { // There must be an application running. - let task = current_task().unwrap(); - let task_cx_ptr = task.lock().get_task_cx_ptr2(); - // Change status to Ready. - task.lock().task_status = TaskStatus::Ready; + let task = take_current_task().unwrap(); + + // ---- temporarily hold current PCB lock + let task_cx_ptr = task.acquire_inner_lock().get_task_cx_ptr2(); + // ---- release current PCB lock + + // ++++ temporarily hold current PCB lock + // Change status to Ready + task.acquire_inner_lock().task_status = TaskStatus::Ready; + // ++++ release current PCB lock + // push back to ready queue. add_task(task); // jump to scheduling cycle @@ -41,10 +43,37 @@ pub fn suspend_current_and_run_next() { } pub fn exit_current_and_run_next() { - // The resource recycle mechanism needs child processes. Now we just panic! - panic!("An application exited!"); + // take from Processor + let task = take_current_task().unwrap(); + // **** hold current PCB lock + let mut inner = task.acquire_inner_lock(); + // Change status to Zombie + inner.task_status = TaskStatus::Zombie; + // move any child to its parent + + // ++++++ hold parent PCB lock here + { + let parent = inner.parent.as_ref().unwrap().upgrade().unwrap(); + let mut parent_inner = parent.acquire_inner_lock(); + for child in inner.children.iter() { + parent_inner.children.push(child.clone()); + } + } + // ++++++ release parent PCB lock here + + inner.children.clear(); + // deallocate user space + inner.memory_set.clear(); + drop(inner); + // **** release current PCB lock + // drop task manually to maintain rc correctly + drop(task); + // we do not have to save task context + let _unused: usize = 0; + schedule(&_unused as *const _); } -pub fn add_application(elf_data: &[u8], app_id: usize) { - add_task(Arc::new(Mutex::new(TaskControlBlock::new(elf_data, app_id)))); +pub fn add_initproc() { + let data = get_app_data_by_name("initproc").unwrap(); + add_task(Arc::new(TaskControlBlock::new(data))); } diff --git a/os/src/task/pid.rs b/os/src/task/pid.rs index 8ca67c44..7cfdb83d 100644 --- a/os/src/task/pid.rs +++ b/os/src/task/pid.rs @@ -46,6 +46,7 @@ pub struct PidHandle(pub usize); impl Drop for PidHandle { fn drop(&mut self) { + //println!("drop pid {}", self.0); PID_ALLOCATOR.lock().dealloc(self.0); } } @@ -82,7 +83,7 @@ impl KernelStack { } pub fn push_on_top(&self, value: T) -> *mut T where T: Sized, { - let (_, kernel_stack_top) = kernel_stack_position(self.pid); + let kernel_stack_top = self.get_top(); let ptr_mut = (kernel_stack_top - core::mem::size_of::()) as *mut T; unsafe { *ptr_mut = value; } ptr_mut diff --git a/os/src/task/processor.rs b/os/src/task/processor.rs index 5551fb72..677d8792 100644 --- a/os/src/task/processor.rs +++ b/os/src/task/processor.rs @@ -2,7 +2,7 @@ use super::TaskControlBlock; use alloc::sync::Arc; use spin::Mutex; use lazy_static::*; -use super::{add_task, fetch_task}; +use super::{fetch_task, TaskStatus}; use super::__switch; use crate::trap::TrapContext; @@ -13,7 +13,7 @@ pub struct Processor { unsafe impl Sync for Processor {} struct ProcessorInner { - current: Option>>, + current: Option>, idle_task_cx_ptr: usize, } @@ -31,13 +31,13 @@ impl Processor { &inner.idle_task_cx_ptr as *const usize } pub fn run(&self) { - //println!("into Processor::run"); loop { if let Some(task) = fetch_task() { - //println!("found task!"); let idle_task_cx_ptr = self.get_idle_task_cx_ptr2(); - let next_task_cx_ptr = task.lock().get_task_cx_ptr2(); - //println!("next_task_cx_ptr={:p}", next_task_cx_ptr); + // acquire + let next_task_cx_ptr = task.acquire_inner_lock().get_task_cx_ptr2(); + task.acquire_inner_lock().task_status = TaskStatus::Running; + // release self.inner.lock().current = Some(task); unsafe { __switch( @@ -48,10 +48,10 @@ impl Processor { } } } - pub fn take_current(&self) -> Option>> { + pub fn take_current(&self) -> Option> { self.inner.lock().current.take() } - pub fn current(&self) -> Option>> { + pub fn current(&self) -> Option> { self.inner.lock().current.as_ref().map(|task| task.clone()) } } @@ -64,25 +64,22 @@ pub fn run_tasks() { PROCESSOR.run(); } -pub fn take_current_task() -> Option>> { +pub fn take_current_task() -> Option> { PROCESSOR.take_current() } -pub fn current_task() -> Option>> { - //println!("into current_task!"); +pub fn current_task() -> Option> { PROCESSOR.current() } pub fn current_user_token() -> usize { - //println!("into current_user_token!"); let task = current_task().unwrap(); - //println!("Got task in current_user_token!"); - let token = task.lock().get_user_token(); + let token = task.acquire_inner_lock().get_user_token(); token } pub fn current_trap_cx() -> &'static mut TrapContext { - current_task().unwrap().as_ref().lock().get_trap_cx() + current_task().unwrap().acquire_inner_lock().get_trap_cx() } pub fn schedule(switched_task_cx_ptr2: *const usize) { diff --git a/os/src/task/task.rs b/os/src/task/task.rs index 0925c7cf..21131736 100644 --- a/os/src/task/task.rs +++ b/os/src/task/task.rs @@ -1,22 +1,32 @@ -use crate::mm::{MemorySet, MapPermission, PhysPageNum, KERNEL_SPACE, VirtAddr}; +use crate::mm::{MemorySet, PhysPageNum, KERNEL_SPACE, VirtAddr}; use crate::trap::{TrapContext, trap_handler}; -use crate::config::{TRAP_CONTEXT, kernel_stack_position}; +use crate::config::{TRAP_CONTEXT}; use super::TaskContext; use super::{PidHandle, pid_alloc, KernelStack}; +use alloc::sync::{Weak, Arc}; +use alloc::vec::Vec; +use spin::{Mutex, MutexGuard}; pub struct TaskControlBlock { // immutable - pub trap_cx_ppn: PhysPageNum, - pub base_size: usize, pub pid: PidHandle, pub kernel_stack: KernelStack, // mutable + inner: Mutex, +} + +pub struct TaskControlBlockInner { + pub trap_cx_ppn: PhysPageNum, + pub base_size: usize, pub task_cx_ptr: usize, pub task_status: TaskStatus, pub memory_set: MemorySet, + pub parent: Option>, + pub children: Vec>, + pub exit_code: i32, } -impl TaskControlBlock { +impl TaskControlBlockInner { pub fn get_task_cx_ptr2(&self) -> *const usize { &self.task_cx_ptr as *const usize } @@ -26,7 +36,19 @@ impl TaskControlBlock { pub fn get_user_token(&self) -> usize { self.memory_set.token() } - pub fn new(elf_data: &[u8], app_id: usize) -> Self { + fn get_status(&self) -> TaskStatus { + self.task_status + } + pub fn is_zombie(&self) -> bool { + self.get_status() == TaskStatus::Zombie + } +} + +impl TaskControlBlock { + pub fn acquire_inner_lock(&self) -> MutexGuard { + self.inner.lock() + } + pub fn new(elf_data: &[u8]) -> Self { // memory_set with elf program headers/trampoline/trap context/user stack let (memory_set, user_sp, entry_point) = MemorySet::from_elf(elf_data); let trap_cx_ppn = memory_set @@ -41,16 +63,23 @@ impl TaskControlBlock { // push a task context which goes to trap_return to the top of kernel stack let task_cx_ptr = kernel_stack.push_on_top(TaskContext::goto_trap_return()); let task_control_block = Self { - trap_cx_ppn, - base_size: user_sp, pid: pid_handle, kernel_stack, - task_cx_ptr: task_cx_ptr as usize, - task_status, - memory_set, + inner: Mutex::new(TaskControlBlockInner { + trap_cx_ppn, + base_size: user_sp, + task_cx_ptr: task_cx_ptr as usize, + task_status, + memory_set, + parent: None, + children: Vec::new(), + exit_code: 0, + }), }; // prepare TrapContext in user space - let trap_cx = task_control_block.get_trap_cx(); + // ---- acquire child PCB lock + let trap_cx = task_control_block.acquire_inner_lock().get_trap_cx(); + // ---- release child PCB lock *trap_cx = TrapContext::app_init_context( entry_point, user_sp, @@ -60,12 +89,86 @@ impl TaskControlBlock { ); task_control_block } + pub fn exec(&self, elf_data: &[u8]) { + // memory_set with elf program headers/trampoline/trap context/user stack + let (memory_set, user_sp, entry_point) = MemorySet::from_elf(elf_data); + let trap_cx_ppn = memory_set + .translate(VirtAddr::from(TRAP_CONTEXT).into()) + .unwrap() + .ppn(); + + // **** hold current PCB lock + let mut inner = self.inner.lock(); + // substitute memory_set + inner.memory_set = memory_set; + // update trap_cx ppn + inner.trap_cx_ppn = trap_cx_ppn; + drop(inner); + // **** release current PCB lock manually + + // initialize trap_cx + // **** acquire current PCB lock + let trap_cx = self.acquire_inner_lock().get_trap_cx(); + // **** release current PCB lock + *trap_cx = TrapContext::app_init_context( + entry_point, + user_sp, + KERNEL_SPACE.lock().token(), + self.kernel_stack.get_top(), + trap_handler as usize, + ); + } + pub fn fork(self: &Arc) -> Arc { + // ---- hold parent PCB lock + let mut parent_inner = self.inner.lock(); + // copy user space(include trap context) + let memory_set = MemorySet::from_existed_user( + &parent_inner.memory_set + ); + let trap_cx_ppn = memory_set + .translate(VirtAddr::from(TRAP_CONTEXT).into()) + .unwrap() + .ppn(); + let task_status = TaskStatus::Ready; + // alloc a pid and a kernel stack in kernel space + let pid_handle = pid_alloc(); + let kernel_stack = KernelStack::new(&pid_handle); + 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()); + let task_control_block = Arc::new(TaskControlBlock { + pid: pid_handle, + kernel_stack, + inner: Mutex::new(TaskControlBlockInner { + trap_cx_ppn, + base_size: parent_inner.base_size, + task_cx_ptr: task_cx_ptr as usize, + task_status, + memory_set, + parent: Some(Arc::downgrade(self)), + children: Vec::new(), + exit_code: 0, + }), + }); + // add child + parent_inner.children.push(task_control_block.clone()); + // modify kernel_sp in trap_cx + // **** acquire child PCB lock + let trap_cx = task_control_block.acquire_inner_lock().get_trap_cx(); + // **** release child PCB lock + trap_cx.kernel_sp = kernel_stack_top; + // return + task_control_block + // ---- release parent PCB lock + } + pub fn getpid(&self) -> usize { + self.pid.0 + } } #[derive(Copy, Clone, PartialEq)] pub enum TaskStatus { Ready, Running, - Exited, Zombie, } \ No newline at end of file diff --git a/os/src/trap/context.rs b/os/src/trap/context.rs index ce076fcc..665f5fb1 100644 --- a/os/src/trap/context.rs +++ b/os/src/trap/context.rs @@ -20,7 +20,9 @@ impl TrapContext { trap_handler: usize, ) -> Self { let mut sstatus = sstatus::read(); + // set CPU privilege to User after trapping back sstatus.set_spp(SPP::User); + // enable Supervisor mode interrupt after trapping back sstatus.set_spie(true); let mut cx = Self { x: [0; 32], diff --git a/os/src/trap/mod.rs b/os/src/trap/mod.rs index 01d096f5..66f463da 100644 --- a/os/src/trap/mod.rs +++ b/os/src/trap/mod.rs @@ -53,17 +53,22 @@ pub fn enable_timer_interrupt() { #[no_mangle] pub fn trap_handler() -> ! { set_kernel_trap_entry(); - let cx = current_trap_cx(); let scause = scause::read(); let stval = stval::read(); match scause.cause() { Trap::Exception(Exception::UserEnvCall) => { + // jump to next instruction anyway + let mut cx = current_trap_cx(); cx.sepc += 4; - cx.x[10] = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]) as usize; + // get system call return value + let result = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]); + // cx is changed during sys_exec, so we have to call it again + cx = current_trap_cx(); + cx.x[10] = result as usize; } Trap::Exception(Exception::StoreFault) | Trap::Exception(Exception::StorePageFault) => { - println!("[kernel] PageFault in application, bad addr = {:#x}, bad instruction = {:#x}, core dumped.", stval, cx.sepc); + println!("[kernel] PageFault in application, bad addr = {:#x}, bad instruction = {:#x}, core dumped.", stval, current_trap_cx().sepc); exit_current_and_run_next(); } Trap::Exception(Exception::IllegalInstruction) => { @@ -83,7 +88,6 @@ pub fn trap_handler() -> ! { #[no_mangle] pub fn trap_return() -> ! { - //println!("into trap_return!"); set_user_trap_entry(); let trap_cx_ptr = TRAP_CONTEXT; let user_satp = current_user_token(); @@ -92,7 +96,6 @@ pub fn trap_return() -> ! { fn __restore(); } let restore_va = __restore as usize - __alltraps as usize + TRAMPOLINE; - //println!("before jr!"); unsafe { llvm_asm!("jr $0" :: "r"(restore_va), "{a0}"(trap_cx_ptr), "{a1}"(user_satp) :: "volatile"); } @@ -101,7 +104,7 @@ pub fn trap_return() -> ! { #[no_mangle] pub fn trap_from_kernel() -> ! { - panic!("a trap from kernel!"); + panic!("a trap {:?} from kernel!", scause::read().cause()); } pub use context::{TrapContext}; diff --git a/user/src/bin/user_shell.rs b/user/src/bin/user_shell.rs index 35dfbcf1..3c59f123 100644 --- a/user/src/bin/user_shell.rs +++ b/user/src/bin/user_shell.rs @@ -12,7 +12,7 @@ const DL: u8 = 0x7fu8; const BS: u8 = 0x08u8; use alloc::string::String; -use user_lib::{fork, exec, wait}; +use user_lib::{fork, exec, waitpid, yield_}; use user_lib::console::getchar; #[no_mangle] @@ -37,8 +37,17 @@ pub fn main() -> i32 { unreachable!(); } else { let mut xstate: i32 = 0; - wait(&mut xstate); - println!("Shell: Process {} exited with code {}", pid, xstate); + let mut exit_pid: isize = 0; + loop { + exit_pid = waitpid(pid as usize, &mut xstate); + if exit_pid == -1 { + yield_(); + } else { + assert_eq!(pid, exit_pid); + println!("Shell: Process {} exited with code {}", pid, xstate); + break; + } + } } line.clear(); } diff --git a/user/src/lib.rs b/user/src/lib.rs index c71798ff..995bb36c 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -42,7 +42,7 @@ fn main() -> i32 { 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 exit(exit_code: i32) -> ! { sys_exit(exit_code); } pub fn yield_() -> isize { sys_yield() } pub fn get_time() -> isize { sys_get_time() } pub fn getpid() -> isize { sys_getpid() } diff --git a/user/src/syscall.rs b/user/src/syscall.rs index c5e23538..6a6721f2 100644 --- a/user/src/syscall.rs +++ b/user/src/syscall.rs @@ -29,8 +29,8 @@ 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) -> ! { - syscall(SYSCALL_EXIT, [xstate as usize, 0, 0]); +pub fn sys_exit(exit_code: i32) -> ! { + syscall(SYSCALL_EXIT, [exit_code as usize, 0, 0]); panic!("sys_exit never returns!"); }