//!Implementation of [`TaskControlBlock`] use super::TaskContext; use super::{pid_alloc, KernelStack, PidHandle}; use crate::config::TRAP_CONTEXT; use crate::fs::{File, Stdin, Stdout}; use crate::mm::{MemorySet, PhysPageNum, VirtAddr, KERNEL_SPACE}; use crate::sync::UPSafeCell; use crate::trap::{trap_handler, TrapContext}; use alloc::sync::{Arc, Weak}; use alloc::vec; use alloc::vec::Vec; use core::cell::RefMut; pub struct TaskControlBlock { // immutable pub pid: PidHandle, pub kernel_stack: KernelStack, // 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]) { // 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(); // **** 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 trap_cx = TrapContext::app_init_context( entry_point, user_sp, KERNEL_SPACE.exclusive_access().token(), self.kernel_stack.get_top(), trap_handler as usize, ); *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, }