From d798c8084a56438c4d0ce1bccfd8c1cc8c1b6358 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Sat, 19 May 2018 16:32:18 +0800 Subject: [PATCH] Pass uCore `forktest` --- src/arch/x86_64/idt/mod.rs | 5 +- src/arch/x86_64/interrupt/handler.rs | 6 +- src/arch/x86_64/interrupt/template.rs | 27 +++--- src/fs.rs | 7 +- src/memory/mod.rs | 2 +- src/memory/stack_allocator.rs | 2 +- src/process/mod.rs | 31 +++++-- src/process/process.rs | 29 +++++- src/process/processor.rs | 127 +++++++++++++++++++++----- src/syscall.rs | 2 + 10 files changed, 180 insertions(+), 58 deletions(-) diff --git a/src/arch/x86_64/idt/mod.rs b/src/arch/x86_64/idt/mod.rs index ea23d53..b5ba560 100644 --- a/src/arch/x86_64/idt/mod.rs +++ b/src/arch/x86_64/idt/mod.rs @@ -23,10 +23,11 @@ pub fn init() { idt[T_SWITCH_TOU].set_handler_fn(to_user); idt[T_SWITCH_TOK].set_handler_fn(to_kernel) .set_flags(IdtFlags::PRESENT | IdtFlags::RING_3 | IdtFlags::INTERRUPT); + // TODO: Enable interrupt during syscall idt[T_SYSCALL].set_handler_fn(syscall) - .set_flags(IdtFlags::PRESENT | IdtFlags::RING_3 | IdtFlags::TRAP); + .set_flags(IdtFlags::PRESENT | IdtFlags::RING_3 | IdtFlags::INTERRUPT); idt[0x80].set_handler_fn(syscall32) - .set_flags(IdtFlags::PRESENT | IdtFlags::RING_3 | IdtFlags::TRAP); + .set_flags(IdtFlags::PRESENT | IdtFlags::RING_3 | IdtFlags::INTERRUPT); unsafe { idt[T_DBLFLT].set_handler_fn(double_fault) diff --git a/src/arch/x86_64/interrupt/handler.rs b/src/arch/x86_64/interrupt/handler.rs index bb280ea..55fce27 100644 --- a/src/arch/x86_64/interrupt/handler.rs +++ b/src/arch/x86_64/interrupt/handler.rs @@ -84,7 +84,7 @@ interrupt_switch!(timer, stack, rsp, { if tick % 100 == 0 { println!("\nInterupt: Timer\ntick = {}", tick); use process; - process::schedule(rsp); + process::schedule(&mut rsp); } ack(IRQ_TIMER); }); @@ -110,13 +110,13 @@ interrupt_stack_p!(to_kernel, stack, { interrupt_switch!(syscall, stack, rsp, { println!("\nInterupt: Syscall {:#x?}", stack.scratch.rax); use syscall::syscall; - let ret = syscall(stack, rsp, false); + let ret = syscall(stack, &mut rsp, false); stack.scratch.rax = ret as usize; }); interrupt_switch!(syscall32, stack, rsp, { // println!("\nInterupt: Syscall {:#x?}", stack.scratch.rax); use syscall::syscall; - let ret = syscall(stack, rsp, true); + let ret = syscall(stack, &mut rsp, true); stack.scratch.rax = ret as usize; }); \ No newline at end of file diff --git a/src/arch/x86_64/interrupt/template.rs b/src/arch/x86_64/interrupt/template.rs index 33480b2..64020cc 100644 --- a/src/arch/x86_64/interrupt/template.rs +++ b/src/arch/x86_64/interrupt/template.rs @@ -318,9 +318,21 @@ macro_rules! interrupt_switch { ($name:ident, $stack: ident, $rsp: ident, $func:block) => { #[naked] pub unsafe extern fn $name () { + // WARNING: Don't do anything outside the inner function. + // rbp is not pointing to kernel stack! #[inline(never)] - unsafe fn inner($stack: &mut InterruptStackP, $rsp: &mut usize) { + unsafe fn inner($stack: &mut InterruptStackP) -> usize { + let mut $rsp = $stack as *const _ as usize; $func + + // Set return rsp if to user + use arch::gdt; + use core::mem::size_of; + let tf = &mut *($rsp as *mut TrapFrame); + if tf.iret.cs & 0x3 == 3 { + gdt::set_ring0_rsp($rsp + size_of::()); + } + $rsp } // Push scratch registers @@ -329,20 +341,11 @@ macro_rules! interrupt_switch { fs_push!(); // Get reference to stack variables - let mut rsp: usize; + let rsp: usize; asm!("" : "={rsp}"(rsp) : : : "intel", "volatile"); // Call inner rust function - inner(&mut *(rsp as *mut InterruptStackP), &mut rsp); - - // Set return rsp if to user - use arch::gdt; - use core::mem::size_of; - let tf = &mut *(rsp as *mut TrapFrame); - if tf.iret.cs & 0x3 == 3 { - gdt::set_ring0_rsp(rsp + size_of::()); - } - + let rsp = inner(&mut *(rsp as *mut InterruptStackP)); asm!("" : : "{rsp}"(rsp) : : "intel", "volatile"); // Pop scratch registers and return diff --git a/src/fs.rs b/src/fs.rs index e53a2d4..da69a3a 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -37,14 +37,15 @@ pub fn load_sfs() { let files = root.borrow().list().unwrap(); debug!("Loading programs: {:?}", files); - for name in files.iter().filter(|&f| f != "." && f != "..") { +// for name in files.iter().filter(|&f| f != "." && f != "..") { + for name in files.iter().filter(|&f| f == "forktest") { static mut BUF: [u8; 64 << 12] = [0; 64 << 12]; let file = root.borrow().lookup(name.as_str()).unwrap(); let len = file.borrow().read_at(0, unsafe { &mut BUF }).unwrap(); - process::add_user_process(name.as_str(), unsafe { &BUF[..len] }); + process::add_user_process(name, unsafe { &BUF[..len] }); } - process::add_user_process("forktest", unsafe { MemBuf::new(_binary_user_forktest_start, _binary_user_forktest_end).0 }); +// process::add_user_process("forktest", unsafe { MemBuf::new(_binary_user_forktest_start, _binary_user_forktest_end).0 }); process::print(); } \ No newline at end of file diff --git a/src/memory/mod.rs b/src/memory/mod.rs index 3212924..676c23f 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -66,7 +66,7 @@ pub fn init(boot_info: BootInformation) -> MemoryController { let stack_allocator = { let stack_alloc_range = Page::range_of(KERNEL_HEAP_OFFSET + KERNEL_HEAP_SIZE, - KERNEL_HEAP_OFFSET + KERNEL_HEAP_SIZE + 0x100000); + KERNEL_HEAP_OFFSET + KERNEL_HEAP_SIZE + 0x1000000); stack_allocator::StackAllocator::new(stack_alloc_range) }; diff --git a/src/memory/stack_allocator.rs b/src/memory/stack_allocator.rs index c422472..d4cba16 100644 --- a/src/memory/stack_allocator.rs +++ b/src/memory/stack_allocator.rs @@ -84,6 +84,6 @@ impl Stack { impl Drop for Stack { fn drop(&mut self) { - panic!("stack leak: {:#x?}", self); + debug!("WARNING: stack leak: {:#x?}", self); } } \ No newline at end of file diff --git a/src/process/mod.rs b/src/process/mod.rs index 15bb749..7040544 100644 --- a/src/process/mod.rs +++ b/src/process/mod.rs @@ -48,13 +48,32 @@ extern fn idle_thread() { } } -/// Fork the current process +/// Fork the current process. Return the child's PID. pub fn sys_fork(tf: &TrapFrame) -> i32 { let mut processor = PROCESSOR.try().unwrap().lock(); let mut mc = MC.try().unwrap().lock(); let new = processor.current().fork(tf, &mut mc); - processor.add(new); - 0 + let pid = processor.add(new); + debug!("fork: {}", pid); + pid as i32 +} + +/// Wait the process exit. Return the exit code. +pub fn sys_wait(rsp: &mut usize, pid: usize) -> i32 { + debug!("wait: {}", pid); + let mut processor = PROCESSOR.try().unwrap().lock(); + let target = match pid { + 0 => WaitTarget::AnyChild, + _ => WaitTarget::Proc(pid), + }; + match processor.current_wait_for(target) { + WaitResult::Ok(error_code) => error_code as i32, + WaitResult::Blocked => { + processor.schedule(rsp); + 0 /* unused */ + }, + WaitResult::NotExist => { -1 } + } } /// Kill the process @@ -69,7 +88,7 @@ pub fn sys_getpid() -> i32 { } /// Exit the current process -pub fn sys_exit(rsp: &mut usize, error_code: usize) -> i32 { +pub fn sys_exit(rsp: &mut usize, error_code: ErrorCode) -> i32 { let mut processor = PROCESSOR.try().unwrap().lock(); let pid = processor.current().pid; processor.schedule(rsp); @@ -77,11 +96,11 @@ pub fn sys_exit(rsp: &mut usize, error_code: usize) -> i32 { 0 } -pub fn add_user_process(name: &str, data: &[u8]) { +pub fn add_user_process(name: impl AsRef, data: &[u8]) { let mut processor = PROCESSOR.try().unwrap().lock(); let mut mc = MC.try().unwrap().lock(); let mut new = Process::new_user(data, &mut mc); - new.name = String::from(name); + new.name = String::from(name.as_ref()); processor.add(new); } diff --git a/src/process/process.rs b/src/process/process.rs index 7dd18d6..208d3b6 100644 --- a/src/process/process.rs +++ b/src/process/process.rs @@ -7,6 +7,7 @@ use alloc::{rc::Rc, String}; #[derive(Debug)] pub struct Process { pub(in process) pid: Pid, + pub(in process) parent: Pid, pub(in process) name: String, kstack: Stack, pub(in process) memory_set: Option, @@ -17,10 +18,14 @@ pub struct Process { } pub type Pid = usize; +pub type ErrorCode = usize; #[derive(Debug, Eq, PartialEq)] pub enum Status { - Ready, Running, Sleeping(usize), Exited + Ready, + Running, + Sleeping(Pid), + Exited(ErrorCode), } impl Process { @@ -32,6 +37,7 @@ impl Process { Process { pid: 0, + parent: 0, name: String::from(name), kstack, memory_set: None, @@ -48,6 +54,7 @@ impl Process { assert_has_not_been_called!(); Process { pid: 0, + parent: 0, name: String::from("init"), kstack: mc.kernel_stack.take().unwrap(), memory_set: None, @@ -72,7 +79,7 @@ impl Process { // User stack use consts::{USER_STACK_OFFSET, USER_STACK_SIZE, USER_TCB_OFFSET}; - let (user_stack_buttom, mut user_stack_top) = match is32 { + let (user_stack_buttom, user_stack_top) = match is32 { true => (USER_TCB_OFFSET, USER_TCB_OFFSET + USER_STACK_SIZE), false => (USER_STACK_OFFSET, USER_STACK_OFFSET + USER_STACK_SIZE), }; @@ -104,7 +111,6 @@ impl Process { // TODO: full argc & argv *(user_stack_top as *mut u32).offset(-1) = 0; // argv *(user_stack_top as *mut u32).offset(-2) = 0; // argc - user_stack_top -= 8; } } }); @@ -112,12 +118,13 @@ impl Process { // Allocate kernel stack and push trap frame let kstack = mc.alloc_stack(7).unwrap(); - let tf = TrapFrame::new_user_thread(entry_addr, user_stack_top, is32); + let tf = TrapFrame::new_user_thread(entry_addr, user_stack_top - 8, is32); let rsp = kstack.push_at_top(tf); // debug!("rsp = {:#x}", rsp); Process { pid: 0, + parent: 0, name: String::new(), kstack, memory_set: Some(memory_set), @@ -152,11 +159,12 @@ impl Process { // Allocate kernel stack and push trap frame let kstack = mc.alloc_stack(7).unwrap(); let mut tf = tf.clone(); - tf.scratch.rax = 1; + tf.scratch.rax = 0; // sys_fork return 0 for child let rsp = kstack.push_at_top(tf); Process { pid: 0, + parent: self.pid, name: self.name.clone() + "_fork", kstack, memory_set: Some(memory_set), @@ -166,6 +174,17 @@ impl Process { is_user: true, } } + + pub fn set_return_value(&self, value: usize) { + let tf = unsafe { &mut *(self.rsp as *mut TrapFrame) }; + tf.scratch.rax = value; + } + pub fn exit_code(&self) -> Option { + match self.status { + Status::Exited(code) => Some(code), + _ => None, + } + } } use memory::{MemorySet, MemoryArea, PhysAddr, FromToVirtualAddress, EntryFlags}; diff --git a/src/process/processor.rs b/src/process/processor.rs index 93218fa..b476bf1 100644 --- a/src/process/processor.rs +++ b/src/process/processor.rs @@ -29,10 +29,11 @@ impl Processor { return next; } - pub fn add(&mut self, mut process: Process) { + pub fn add(&mut self, mut process: Process) -> Pid { let pid = self.alloc_pid(); process.pid = pid; self.procs.insert(pid, process); + pid } pub fn schedule(&mut self, rsp: &mut usize) { @@ -43,10 +44,13 @@ impl Processor { fn find_next(&self) -> Pid { *self.procs.keys() .find(|&&i| i > self.current_pid - && self.get(i).status != Status::Exited) - .unwrap_or(self.procs.keys().nth(0).unwrap()) + && self.get(i).exit_code().is_none()) + .unwrap_or(self.procs.keys().next().unwrap()) } + /// Switch process to `pid`, switch page table if necessary. + /// Store `rsp` and point it to target kernel stack. + /// The current status will be set to `Ready` if it is `Running` now. fn switch_to(&mut self, pid: Pid, rsp: &mut usize) { // for debug print let pid0 = self.current_pid; @@ -55,44 +59,102 @@ impl Processor { if pid == self.current_pid { return; } - { - let current = self.procs.get_mut(&self.current_pid).unwrap(); - current.status = Status::Ready; - current.rsp = *rsp; + self.current_pid = pid; + + let (from, to) = self.get_mut2(pid0, pid); + + // set `from` + if from.status == Status::Running { + from.status = Status::Ready; } - { - let process = self.procs.get_mut(&pid).unwrap(); - process.status = Status::Running; - *rsp = process.rsp; - - // switch page table - if let Some(page_table) = process.page_table.take() { - let mut active_table = unsafe { ActivePageTable::new() }; - let old_table = active_table.switch(page_table); - process.page_table = Some(old_table); - } + from.rsp = *rsp; + + // set `to` + assert_eq!(to.status, Status::Ready); + to.status = Status::Running; + *rsp = to.rsp; + + // switch page table + if let Some(page_table) = to.page_table.take() { + let mut active_table = unsafe { ActivePageTable::new() }; + let old_table = active_table.switch(page_table); + from.page_table = Some(old_table); } - self.current_pid = pid; + debug!("Processor: switch from {} to {}\n rsp: {:#x} -> {:#x}", pid0, pid, rsp0, rsp); } fn get(&self, pid: Pid) -> &Process { self.procs.get(&pid).unwrap() } - + fn get_mut(&mut self, pid: Pid) -> &mut Process { + self.procs.get_mut(&pid).unwrap() + } + fn get_mut2(&mut self, pid1: Pid, pid2: Pid) -> (&mut Process, &mut Process) { + assert_ne!(pid1, pid2); + let procs1 = &mut self.procs as *mut BTreeMap<_, _>; + let procs2 = procs1; + let p1 = unsafe { &mut *procs1 }.get_mut(&pid1).unwrap(); + let p2 = unsafe { &mut *procs2 }.get_mut(&pid2).unwrap(); + (p1, p2) + } pub fn current(&self) -> &Process { self.get(self.current_pid) } pub fn kill(&mut self, pid: Pid) { - let process = self.procs.get_mut(&pid).unwrap(); - process.status = Status::Exited; - // TODO: Remove process from set + self.exit(pid, 0x1000); // TODO: error code for killed } - pub fn exit(&mut self, pid: Pid, error_code: usize) { + pub fn exit(&mut self, pid: Pid, error_code: ErrorCode) { + assert_ne!(pid, self.current_pid); debug!("Processor: {} exit, code: {}", pid, error_code); - self.kill(pid); + self.get_mut(pid).status = Status::Exited(error_code); + if let Some(waiter) = self.find_waiter(pid) { + { + let p = self.get_mut(waiter); + p.status = Status::Ready; + p.set_return_value(error_code); + } + debug!("Processor: remove {}", pid); + self.procs.remove(&pid); + } + } + + /// Let current process wait for another + pub fn current_wait_for(&mut self, target: WaitTarget) -> WaitResult { + // Find one target process and it's exit code + let (pid, exit_code) = match target { + WaitTarget::AnyChild => { + let childs = self.procs.values() + .filter(|&p| p.parent == self.current_pid); + if childs.clone().next().is_none() { + return WaitResult::NotExist; + } + childs.clone() + .find(|&p| p.exit_code().is_some()) + .map(|p| (p.pid, p.exit_code())) + .unwrap_or((0, None)) + } + WaitTarget::Proc(pid) => (pid, self.get(pid).exit_code()), + }; + if let Some(exit_code) = exit_code { + debug!("Processor: remove {}", pid); + self.procs.remove(&pid); + WaitResult::Ok(exit_code) + } else { + debug!("Processor: {} wait for {}", self.current_pid, pid); + let current_pid = self.current_pid; + self.get_mut(current_pid).status = Status::Sleeping(pid); + WaitResult::Blocked + } + } + + fn find_waiter(&self, pid: Pid) -> Option { + self.procs.values().find(|&p| { + p.status == Status::Sleeping(pid) || + (p.status == Status::Sleeping(0) && self.get(pid).parent == p.pid) + }).map(|ref p| p.pid) } } @@ -102,4 +164,19 @@ impl Debug for Processor { .entries(self.procs.iter().map(|(pid, proc0)| { (pid, &proc0.name) })) .finish() } +} + +pub enum WaitTarget { + AnyChild, + Proc(Pid), +} + +pub enum WaitResult { + /// The target process is still running. + /// The waiter's status will be set to `Sleeping`. + Blocked, + /// The target process is exited with `ErrorCode`. + Ok(ErrorCode), + /// The target process is not exist. + NotExist, } \ No newline at end of file diff --git a/src/syscall.rs b/src/syscall.rs index 8dc3354..a6546fc 100644 --- a/src/syscall.rs +++ b/src/syscall.rs @@ -21,6 +21,8 @@ pub unsafe fn syscall(tf: &TrapFrame, rsp: &mut usize, is32: bool) -> i32 { io::open(args[0] as *const u8, args[1]), Syscall::Xv6(SYS_CLOSE) | Syscall::Ucore(UCORE_SYS_CLOSE) => io::close(args[0]), + Syscall::Xv6(SYS_WAIT) | Syscall::Ucore(UCORE_SYS_WAIT) => + process::sys_wait(rsp, args[0]), Syscall::Xv6(SYS_FORK) | Syscall::Ucore(UCORE_SYS_FORK) => process::sys_fork(tf), Syscall::Xv6(SYS_KILL) | Syscall::Ucore(UCORE_SYS_KILL) =>