diff --git a/os/src/entry.asm b/os/src/entry.asm index 9d2ff713..a28dc8ff 100644 --- a/os/src/entry.asm +++ b/os/src/entry.asm @@ -9,4 +9,4 @@ _start: boot_stack: .space 4096 * 16 .globl boot_stack_top -boot_stack_top: \ No newline at end of file +boot_stack_top: diff --git a/os/src/mm/memory_set.rs b/os/src/mm/memory_set.rs index c33008d9..2f95625e 100644 --- a/os/src/mm/memory_set.rs +++ b/os/src/mm/memory_set.rs @@ -181,23 +181,7 @@ impl MemorySet { // map user stack with U flags let max_end_va: VirtAddr = max_end_vpn.into(); let mut user_stack_bottom: usize = max_end_va.into(); - // guard page - user_stack_bottom += PAGE_SIZE; - let user_stack_top = user_stack_bottom + USER_STACK_SIZE; - memory_set.push(MapArea::new( - user_stack_bottom.into(), - user_stack_top.into(), - MapType::Framed, - MapPermission::R | MapPermission::W | MapPermission::U, - ), None); - // map TrapContext - memory_set.push(MapArea::new( - TRAP_CONTEXT.into(), - TRAMPOLINE.into(), - MapType::Framed, - MapPermission::R | MapPermission::W, - ), None); - (memory_set, user_stack_top, elf.header.pt2.entry_point() as usize) + (memory_set, user_stack_bottom, elf.header.pt2.entry_point() as usize) } pub fn from_existed_user(user_space: &MemorySet) -> MemorySet { let mut memory_set = Self::new_bare(); @@ -355,4 +339,4 @@ pub fn remap_test() { false, ); println!("remap_test passed!"); -} \ No newline at end of file +} diff --git a/os/src/task/id.rs b/os/src/task/id.rs new file mode 100644 index 00000000..598351d4 --- /dev/null +++ b/os/src/task/id.rs @@ -0,0 +1,126 @@ +use alloc::{vec::Vec, sync::Arc}; +use lazy_static::*; +use crate::sync::UPSafeCell; +use crate::mm::{KERNEL_SPACE, MapPermission, VirtAddr}; +use crate::config::{ + PAGE_SIZE, + TRAMPOLINE, + KERNEL_STACK_SIZE, +}; +use super::ProcessControlBlock; + +pub struct RecycleAllocator { + current: usize, + recycled: Vec, +} + +impl RecycleAllocator { + pub fn new() -> Self { + RecycleAllocator { + current: 0, + recycled: Vec::new(), + } + } + pub fn alloc(&mut self) -> usize { + if let Some(id) = self.recycled.pop() { + id + } else { + self.current += 1; + self.current - 1 + } + } + pub fn dealloc(&mut self, id: usize) { + assert!(id < self.current); + assert!( + self.recycled.iter().find(|i| **i == id).is_none(), + "id {} has been deallocated!", id + ); + self.recycled.push(id); + } +} + +lazy_static! { + static ref PID_ALLOCATOR: UPSafeCell = unsafe { + UPSafeCell::new(RecycleAllocator::new()) + }; + + static ref KSTACK_ALLOCATOR: UPSafeCell = unsafe { + UPSafeCell::new(RecycleAllocator::new()) + }; +} + +pub struct PidHandle(pub usize); + +pub fn pid_alloc() -> PidHandle { + PidHandle(PID_ALLOCATOR.exclusive_access().alloc()) +} + +impl Drop for PidHandle { + fn drop(&mut self) { + PID_ALLOCATOR.exclusive_access().dealloc(self.0); + } +} + +/// Return (bottom, top) of a kernel stack in kernel space. +pub fn kernel_stack_position(kstack_id: usize) -> (usize, usize) { + let top = TRAMPOLINE - kstack_id * (KERNEL_STACK_SIZE + PAGE_SIZE); + let bottom = top - KERNEL_STACK_SIZE; + (bottom, top) +} + +pub struct KernelStack(pub usize); + +pub fn kstack_alloc() -> KernelStack { + let kstack_id = KSTACK_ALLOCATOR.exclusive_access().alloc(); + let (kstack_bottom, kstack_top) = kernel_stack_position(kstack_id); + KERNEL_SPACE + .exclusive_access() + .insert_framed_area( + kstack_bottom.into(), + kstack_top.into(), + MapPermission::R | MapPermission::W, + ); + KernelStack(kstack_id) +} + +impl Drop for KernelStack { + fn drop(&mut self) { + let (kernel_stack_bottom, _) = kernel_stack_position(self.0); + let kernel_stack_bottom_va: VirtAddr = kernel_stack_bottom.into(); + KERNEL_SPACE + .exclusive_access() + .remove_area_with_start_vpn(kernel_stack_bottom_va.into()); + } +} + +impl KernelStack { + #[allow(unused)] + pub fn push_on_top(&self, value: T) -> *mut T where + T: Sized, { + 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 + } + pub fn get_top(&self) -> usize { + let (_, kernel_stack_top) = kernel_stack_position(self.0); + kernel_stack_top + } +} + +pub struct TaskUserRes { + pub tid: usize, + pub kstack: KernelStack, + pub process: Arc, +} + +impl Drop for TaskUserRes { + fn drop(&mut self) { + // dealloc tid + let process = self.process.inner_exclusive_access(); + process.task_res_allocator.dealloc(self.tid); + // dealloc trap_cx + process.dealloc_trap_cx(self.tid); + // kstack can be deallocated automatically + } +} diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs index 81d6a09b..8294d619 100644 --- a/os/src/task/mod.rs +++ b/os/src/task/mod.rs @@ -3,7 +3,8 @@ mod switch; mod task; mod manager; mod processor; -mod pid; +mod id; +mod process; use crate::fs::{open_file, OpenFlags}; use switch::__switch; @@ -11,8 +12,10 @@ use task::{TaskControlBlock, TaskStatus}; use alloc::sync::Arc; use manager::fetch_task; use lazy_static::*; -pub use context::TaskContext; +use process::ProcessControlBlock; +use id::RecycleAllocator; +pub use context::TaskContext; pub use processor::{ run_tasks, current_task, @@ -22,7 +25,12 @@ pub use processor::{ schedule, }; pub use manager::add_task; -pub use pid::{PidHandle, pid_alloc, KernelStack}; +pub use id::{ + PidHandle, + pid_alloc, + KernelStack, + kstack_alloc, +}; pub fn suspend_current_and_run_next() { // There must be an application running. diff --git a/os/src/task/pid.rs b/os/src/task/pid.rs deleted file mode 100644 index 517cd1d4..00000000 --- a/os/src/task/pid.rs +++ /dev/null @@ -1,108 +0,0 @@ -use alloc::vec::Vec; -use lazy_static::*; -use crate::sync::UPSafeCell; -use crate::mm::{KERNEL_SPACE, MapPermission, VirtAddr}; -use crate::config::{ - PAGE_SIZE, - TRAMPOLINE, - KERNEL_STACK_SIZE, -}; - -struct PidAllocator { - current: usize, - recycled: Vec, -} - -impl PidAllocator { - pub fn new() -> Self { - PidAllocator { - current: 0, - recycled: Vec::new(), - } - } - pub fn alloc(&mut self) -> PidHandle { - if let Some(pid) = self.recycled.pop() { - PidHandle(pid) - } else { - self.current += 1; - PidHandle(self.current - 1) - } - } - pub fn dealloc(&mut self, pid: usize) { - assert!(pid < self.current); - assert!( - self.recycled.iter().find(|ppid| **ppid == pid).is_none(), - "pid {} has been deallocated!", pid - ); - self.recycled.push(pid); - } -} - -lazy_static! { - static ref PID_ALLOCATOR : UPSafeCell = unsafe { - UPSafeCell::new(PidAllocator::new()) - }; -} - -pub struct PidHandle(pub usize); - -impl Drop for PidHandle { - fn drop(&mut self) { - //println!("drop pid {}", self.0); - PID_ALLOCATOR.exclusive_access().dealloc(self.0); - } -} - -pub fn pid_alloc() -> PidHandle { - PID_ALLOCATOR.exclusive_access().alloc() -} - -/// 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) -} - -pub struct KernelStack { - pid: usize, -} - -impl KernelStack { - pub fn new(pid_handle: &PidHandle) -> Self { - let pid = pid_handle.0; - let (kernel_stack_bottom, kernel_stack_top) = kernel_stack_position(pid); - KERNEL_SPACE - .exclusive_access() - .insert_framed_area( - kernel_stack_bottom.into(), - kernel_stack_top.into(), - MapPermission::R | MapPermission::W, - ); - KernelStack { - pid: pid_handle.0, - } - } - #[allow(unused)] - pub fn push_on_top(&self, value: T) -> *mut T where - T: Sized, { - 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 - } - pub fn get_top(&self) -> usize { - let (_, kernel_stack_top) = kernel_stack_position(self.pid); - kernel_stack_top - } -} - -impl Drop for KernelStack { - fn drop(&mut self) { - let (kernel_stack_bottom, _) = kernel_stack_position(self.pid); - let kernel_stack_bottom_va: VirtAddr = kernel_stack_bottom.into(); - KERNEL_SPACE - .exclusive_access() - .remove_area_with_start_vpn(kernel_stack_bottom_va.into()); - } -} \ No newline at end of file diff --git a/os/src/task/process.rs b/os/src/task/process.rs new file mode 100644 index 00000000..3a2a61a9 --- /dev/null +++ b/os/src/task/process.rs @@ -0,0 +1,213 @@ +use crate::mm::{ + MemorySet, + KERNEL_SPACE, + VirtAddr, + translated_refmut, +}; +use crate::trap::{TrapContext, trap_handler}; +use crate::config::TRAP_CONTEXT; +use crate::sync::UPSafeCell; +use core::cell::RefMut; +use super::id::RecycleAllocator; +use super::{TaskContext, TaskControlBlock}; +use super::{PidHandle, pid_alloc, KernelStack, kstack_alloc}; +use alloc::sync::{Weak, Arc}; +use alloc::vec; +use alloc::vec::Vec; +use alloc::string::String; +use crate::fs::{File, Stdin, Stdout}; + +pub struct ProcessControlBlock { + // immutable + pub pid: PidHandle, + // mutable + inner: UPSafeCell, +} + +pub struct ProcessControlBlockInner { + pub base_size: usize, + pub is_zombie: bool, + pub memory_set: MemorySet, + pub parent: Option>, + pub children: Vec>, + pub exit_code: i32, + pub fd_table: Vec>>, + pub tasks: Vec>>, + pub task_res_allocator: RecycleAllocator, +} + +impl ProcessControlBlockInner { + pub fn get_user_token(&self) -> usize { + self.memory_set.token() + } + pub fn alloc_fd(&mut self) -> usize { + if let Some(fd) = (0..self.fd_table.len()) + .find(|fd| self.fd_table[*fd].is_none()) { + fd + } else { + self.fd_table.push(None); + self.fd_table.len() - 1 + } + } + pub fn dealloc_trap_cx(&mut self, tid: usize) { + unimplemented!(); + //self.memory_set.remove_area_with_start_vpn() + } +} + +impl ProcessControlBlock { + pub fn inner_exclusive_access(&self) -> RefMut<'_, ProcessControlBlockInner> { + self.inner.exclusive_access() + } + + 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 + .translate(VirtAddr::from(TRAP_CONTEXT).into()) + .unwrap() + .ppn(); + // alloc a pid and a kernel stack in kernel space + let pid_handle = pid_alloc(); + let kstack = kstack_alloc(); + let kernel_stack_top = kstack.get_top(); + let task_control_block = Self { + pid: pid_handle, + kernel_stack, + inner: unsafe { UPSafeCell::new(TaskControlBlockInner { + trap_cx_ppn, + base_size: user_sp, + task_cx: TaskContext::goto_trap_return(kernel_stack_top), + task_status: TaskStatus::Ready, + memory_set, + parent: None, + children: Vec::new(), + exit_code: 0, + fd_table: vec![ + // 0 -> stdin + Some(Arc::new(Stdin)), + // 1 -> stdout + Some(Arc::new(Stdout)), + // 2 -> stderr + Some(Arc::new(Stdout)), + ], + })}, + }; + // prepare TrapContext in user space + let trap_cx = task_control_block.inner_exclusive_access().get_trap_cx(); + *trap_cx = TrapContext::app_init_context( + entry_point, + user_sp, + KERNEL_SPACE.exclusive_access().token(), + kernel_stack_top, + trap_handler as usize, + ); + task_control_block + } + pub fn exec(&self, elf_data: &[u8], args: Vec) { + // memory_set with elf program headers/trampoline/trap context/user stack + let (memory_set, mut user_sp, entry_point) = MemorySet::from_elf(elf_data); + let trap_cx_ppn = memory_set + .translate(VirtAddr::from(TRAP_CONTEXT).into()) + .unwrap() + .ppn(); + // push arguments on user stack + user_sp -= (args.len() + 1) * core::mem::size_of::(); + let argv_base = user_sp; + let mut argv: Vec<_> = (0..=args.len()) + .map(|arg| { + translated_refmut( + memory_set.token(), + (argv_base + arg * core::mem::size_of::()) as *mut usize + ) + }) + .collect(); + *argv[args.len()] = 0; + for i in 0..args.len() { + user_sp -= args[i].len() + 1; + *argv[i] = user_sp; + let mut p = user_sp; + for c in args[i].as_bytes() { + *translated_refmut(memory_set.token(), p as *mut u8) = *c; + p += 1; + } + *translated_refmut(memory_set.token(), p as *mut u8) = 0; + } + // make the user_sp aligned to 8B for k210 platform + user_sp -= user_sp % core::mem::size_of::(); + + // **** access current TCB exclusively + let mut inner = self.inner_exclusive_access(); + // substitute memory_set + inner.memory_set = memory_set; + // update trap_cx ppn + inner.trap_cx_ppn = trap_cx_ppn; + // initialize trap_cx + let mut trap_cx = TrapContext::app_init_context( + entry_point, + user_sp, + KERNEL_SPACE.exclusive_access().token(), + self.kernel_stack.get_top(), + trap_handler as usize, + ); + trap_cx.x[10] = args.len(); + trap_cx.x[11] = argv_base; + *inner.get_trap_cx() = trap_cx; + // **** release current PCB + } + pub fn fork(self: &Arc) -> Arc { + // ---- hold parent PCB lock + let mut parent_inner = self.inner_exclusive_access(); + // 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(); + // 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(); + // copy fd table + let mut new_fd_table: Vec>> = 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 { + pid: pid_handle, + kernel_stack, + inner: unsafe { UPSafeCell::new(TaskControlBlockInner { + trap_cx_ppn, + base_size: parent_inner.base_size, + task_cx: TaskContext::goto_trap_return(kernel_stack_top), + task_status: TaskStatus::Ready, + memory_set, + parent: Some(Arc::downgrade(self)), + children: Vec::new(), + exit_code: 0, + fd_table: new_fd_table, + })}, + }); + // add child + parent_inner.children.push(task_control_block.clone()); + // modify kernel_sp in trap_cx + // **** access child PCB exclusively + let trap_cx = task_control_block.inner_exclusive_access().get_trap_cx(); + trap_cx.kernel_sp = kernel_stack_top; + // return + task_control_block + // **** release child PCB + // ---- release parent PCB + } + pub fn getpid(&self) -> usize { + self.pid.0 + } + +} + diff --git a/os/src/task/task.rs b/os/src/task/task.rs index 25b2226d..b81c16ee 100644 --- a/os/src/task/task.rs +++ b/os/src/task/task.rs @@ -1,224 +1,39 @@ -use crate::mm::{ - MemorySet, - PhysPageNum, - KERNEL_SPACE, - VirtAddr, - translated_refmut, +use alloc::sync::Arc; +use crate::{mm::PhysPageNum, sync::UPSafeCell}; +use crate::trap::TrapContext; +use super::{ + KernelStack, + ProcessControlBlock, + TaskContext }; -use crate::trap::{TrapContext, trap_handler}; -use crate::config::TRAP_CONTEXT; -use crate::sync::UPSafeCell; -use core::cell::RefMut; -use super::TaskContext; -use super::{PidHandle, pid_alloc, KernelStack}; -use alloc::sync::{Weak, Arc}; -use alloc::vec; -use alloc::vec::Vec; -use alloc::string::String; -use crate::fs::{File, Stdin, Stdout}; pub struct TaskControlBlock { // immutable - pub pid: PidHandle, - pub kernel_stack: KernelStack, + pub tid: usize, + pub kstack: KernelStack, + pub process: Arc, // mutable inner: UPSafeCell, } pub struct TaskControlBlockInner { pub trap_cx_ppn: PhysPageNum, - pub base_size: usize, pub task_cx: TaskContext, pub task_status: TaskStatus, - pub memory_set: MemorySet, - pub parent: Option>, - pub children: Vec>, pub exit_code: i32, - pub fd_table: Vec>>, } impl TaskControlBlockInner { pub fn get_trap_cx(&self) -> &'static mut TrapContext { self.trap_cx_ppn.get_mut() } - pub fn get_user_token(&self) -> usize { - self.memory_set.token() - } fn get_status(&self) -> TaskStatus { self.task_status } - pub fn is_zombie(&self) -> bool { - self.get_status() == TaskStatus::Zombie - } - pub fn alloc_fd(&mut self) -> usize { - if let Some(fd) = (0..self.fd_table.len()) - .find(|fd| self.fd_table[*fd].is_none()) { - fd - } else { - self.fd_table.push(None); - self.fd_table.len() - 1 - } - } -} - -impl TaskControlBlock { - pub fn inner_exclusive_access(&self) -> RefMut<'_, TaskControlBlockInner> { - self.inner.exclusive_access() - } - 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 - .translate(VirtAddr::from(TRAP_CONTEXT).into()) - .unwrap() - .ppn(); - // 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(); - let task_control_block = Self { - pid: pid_handle, - kernel_stack, - inner: unsafe { UPSafeCell::new(TaskControlBlockInner { - trap_cx_ppn, - base_size: user_sp, - task_cx: TaskContext::goto_trap_return(kernel_stack_top), - task_status: TaskStatus::Ready, - memory_set, - parent: None, - children: Vec::new(), - exit_code: 0, - fd_table: vec![ - // 0 -> stdin - Some(Arc::new(Stdin)), - // 1 -> stdout - Some(Arc::new(Stdout)), - // 2 -> stderr - Some(Arc::new(Stdout)), - ], - })}, - }; - // prepare TrapContext in user space - let trap_cx = task_control_block.inner_exclusive_access().get_trap_cx(); - *trap_cx = TrapContext::app_init_context( - entry_point, - user_sp, - KERNEL_SPACE.exclusive_access().token(), - kernel_stack_top, - trap_handler as usize, - ); - task_control_block - } - pub fn exec(&self, elf_data: &[u8], args: Vec) { - // memory_set with elf program headers/trampoline/trap context/user stack - let (memory_set, mut user_sp, entry_point) = MemorySet::from_elf(elf_data); - let trap_cx_ppn = memory_set - .translate(VirtAddr::from(TRAP_CONTEXT).into()) - .unwrap() - .ppn(); - // push arguments on user stack - user_sp -= (args.len() + 1) * core::mem::size_of::(); - let argv_base = user_sp; - let mut argv: Vec<_> = (0..=args.len()) - .map(|arg| { - translated_refmut( - memory_set.token(), - (argv_base + arg * core::mem::size_of::()) as *mut usize - ) - }) - .collect(); - *argv[args.len()] = 0; - for i in 0..args.len() { - user_sp -= args[i].len() + 1; - *argv[i] = user_sp; - let mut p = user_sp; - for c in args[i].as_bytes() { - *translated_refmut(memory_set.token(), p as *mut u8) = *c; - p += 1; - } - *translated_refmut(memory_set.token(), p as *mut u8) = 0; - } - // make the user_sp aligned to 8B for k210 platform - user_sp -= user_sp % core::mem::size_of::(); - - // **** access current TCB exclusively - let mut inner = self.inner_exclusive_access(); - // substitute memory_set - inner.memory_set = memory_set; - // update trap_cx ppn - inner.trap_cx_ppn = trap_cx_ppn; - // initialize trap_cx - let mut trap_cx = TrapContext::app_init_context( - entry_point, - user_sp, - KERNEL_SPACE.exclusive_access().token(), - self.kernel_stack.get_top(), - trap_handler as usize, - ); - trap_cx.x[10] = args.len(); - trap_cx.x[11] = argv_base; - *inner.get_trap_cx() = trap_cx; - // **** release current PCB - } - pub fn fork(self: &Arc) -> Arc { - // ---- hold parent PCB lock - let mut parent_inner = self.inner_exclusive_access(); - // 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(); - // 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(); - // copy fd table - let mut new_fd_table: Vec>> = 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 { - pid: pid_handle, - kernel_stack, - inner: unsafe { UPSafeCell::new(TaskControlBlockInner { - trap_cx_ppn, - base_size: parent_inner.base_size, - task_cx: TaskContext::goto_trap_return(kernel_stack_top), - task_status: TaskStatus::Ready, - memory_set, - parent: Some(Arc::downgrade(self)), - children: Vec::new(), - exit_code: 0, - fd_table: new_fd_table, - })}, - }); - // add child - parent_inner.children.push(task_control_block.clone()); - // modify kernel_sp in trap_cx - // **** access child PCB exclusively - let trap_cx = task_control_block.inner_exclusive_access().get_trap_cx(); - trap_cx.kernel_sp = kernel_stack_top; - // return - task_control_block - // **** release child PCB - // ---- release parent PCB - } - pub fn getpid(&self) -> usize { - self.pid.0 - } - } #[derive(Copy, Clone, PartialEq)] pub enum TaskStatus { Ready, Running, - Zombie, -} \ No newline at end of file +} diff --git a/rust-toolchain b/rust-toolchain index 1c5af266..bc17a162 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2021-08-25 +nightly-2021-09-15