use super::{kstack_alloc, pid_alloc, KernelStack, PidHandle, SignalActions, SignalFlags, TaskContext};
use crate::{
config::TRAP_CONTEXT_BASE,
fs::{File, Stdin, Stdout},
mm::{translated_refmut, MemorySet, PhysPageNum, VirtAddr, KERNEL_SPACE},
sync::UPSafeCell,
trap::{trap_handler, TrapContext},
};
use alloc::{
string::String,
sync::{Arc, Weak},
vec,
vec::Vec,
};
use core::cell::RefMut;
pub struct TaskControlBlock {
pub pid: PidHandle,
pub kernel_stack: KernelStack,
inner: UPSafeCell<TaskControlBlockInner>,
}
impl TaskControlBlock {
pub fn inner_exclusive_access(&self) -> RefMut<'_, TaskControlBlockInner> {
self.inner.exclusive_access()
}
pub fn get_user_token(&self) -> usize {
let inner = self.inner_exclusive_access();
inner.memory_set.token()
}
}
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<Weak<TaskControlBlock>>,
pub children: Vec<Arc<TaskControlBlock>>,
pub exit_code: i32,
pub fd_table: Vec<Option<Arc<dyn File + Send + Sync>>>,
pub signals: SignalFlags,
pub signal_mask: SignalFlags,
pub handling_sig: isize,
pub signal_actions: SignalActions,
pub killed: bool,
pub frozen: bool,
pub trap_ctx_backup: Option<TrapContext>,
pub heap_bottom: usize,
pub program_brk: usize,
}
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 new(elf_data: &[u8]) -> Self {
let (memory_set, user_sp, entry_point) = MemorySet::from_elf(elf_data);
let trap_cx_ppn = memory_set
.translate(VirtAddr::from(TRAP_CONTEXT_BASE).into())
.unwrap()
.ppn();
let pid_handle = pid_alloc();
let kernel_stack = kstack_alloc();
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![
Some(Arc::new(Stdin)),
Some(Arc::new(Stdout)),
Some(Arc::new(Stdout)),
],
signals: SignalFlags::empty(),
signal_mask: SignalFlags::empty(),
handling_sig: -1,
signal_actions: SignalActions::default(),
killed: false,
frozen: false,
trap_ctx_backup: None,
heap_bottom: user_sp,
program_brk: user_sp,
})
},
};
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<String>) {
let (memory_set, mut user_sp, entry_point) = MemorySet::from_elf(elf_data);
let trap_cx_ppn = memory_set
.translate(VirtAddr::from(TRAP_CONTEXT_BASE).into())
.unwrap()
.ppn();
user_sp -= (args.len() + 1) * core::mem::size_of::<usize>();
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::<usize>()) 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;
}
user_sp -= user_sp % core::mem::size_of::<usize>();
let mut inner = self.inner_exclusive_access();
inner.memory_set = memory_set;
inner.trap_cx_ppn = trap_cx_ppn;
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;
}
pub fn fork(self: &Arc<TaskControlBlock>) -> Arc<TaskControlBlock> {
let mut parent_inner = self.inner_exclusive_access();
let memory_set = MemorySet::from_existed_user(&parent_inner.memory_set);
let trap_cx_ppn = memory_set
.translate(VirtAddr::from(TRAP_CONTEXT_BASE).into())
.unwrap()
.ppn();
let pid_handle = pid_alloc();
let kernel_stack = kstack_alloc();
let kernel_stack_top = kernel_stack.get_top();
let mut new_fd_table: Vec<Option<Arc<dyn File + Send + Sync>>> = 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,
signals: SignalFlags::empty(),
signal_mask: parent_inner.signal_mask,
handling_sig: -1,
signal_actions: parent_inner.signal_actions.clone(),
killed: false,
frozen: false,
trap_ctx_backup: None,
heap_bottom: parent_inner.heap_bottom,
program_brk: parent_inner.program_brk,
})
},
});
parent_inner.children.push(task_control_block.clone());
let trap_cx = task_control_block.inner_exclusive_access().get_trap_cx();
trap_cx.kernel_sp = kernel_stack_top;
task_control_block
}
pub fn getpid(&self) -> usize {
self.pid.0
}
pub fn change_program_brk(&self, size: i32) -> Option<usize> {
let mut inner = self.inner_exclusive_access();
let heap_bottom = inner.heap_bottom;
let old_break = inner.program_brk;
let new_brk = inner.program_brk as isize + size as isize;
if new_brk < heap_bottom as isize {
return None;
}
let result = if size < 0 {
inner
.memory_set
.shrink_to(VirtAddr(heap_bottom), VirtAddr(new_brk as usize))
} else {
inner
.memory_set
.append_to(VirtAddr(heap_bottom), VirtAddr(new_brk as usize))
};
if result {
inner.program_brk = new_brk as usize;
Some(old_break)
} else {
None
}
}
}
#[derive(Copy, Clone, PartialEq)]
pub enum TaskStatus {
UnInit,
Ready,
Running,
Zombie,
}