mirror of
https://github.com/LearningOS/rust-based-os-comp2022.git
synced 2026-05-09 23:31:29 +08:00
add os[1-8]-ref for os refereces, add guide, add README
This commit is contained in:
32
os5-ref/src/task/context.rs
Normal file
32
os5-ref/src/task/context.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
//! Implementation of [`TaskContext`]
|
||||
|
||||
use crate::trap::trap_return;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
/// task context structure containing some registers
|
||||
pub struct TaskContext {
|
||||
/// Ret position after task switching
|
||||
ra: usize,
|
||||
/// Stack pointer
|
||||
sp: usize,
|
||||
/// s0-11 register, callee saved
|
||||
s: [usize; 12],
|
||||
}
|
||||
|
||||
impl TaskContext {
|
||||
pub fn zero_init() -> Self {
|
||||
Self {
|
||||
ra: 0,
|
||||
sp: 0,
|
||||
s: [0; 12],
|
||||
}
|
||||
}
|
||||
pub fn goto_trap_return(kstack_ptr: usize) -> Self {
|
||||
Self {
|
||||
ra: trap_return as usize,
|
||||
sp: kstack_ptr,
|
||||
s: [0; 12],
|
||||
}
|
||||
}
|
||||
}
|
||||
47
os5-ref/src/task/manager.rs
Normal file
47
os5-ref/src/task/manager.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
//! Implementation of [`TaskManager`]
|
||||
//!
|
||||
//! It is only used to manage processes and schedule process based on ready queue.
|
||||
//! Other CPU process monitoring functions are in Processor.
|
||||
|
||||
|
||||
use super::TaskControlBlock;
|
||||
use crate::sync::UPSafeCell;
|
||||
use alloc::collections::VecDeque;
|
||||
use alloc::sync::Arc;
|
||||
use lazy_static::*;
|
||||
|
||||
pub struct TaskManager {
|
||||
ready_queue: VecDeque<Arc<TaskControlBlock>>,
|
||||
}
|
||||
|
||||
// YOUR JOB: FIFO->Stride
|
||||
/// A simple FIFO scheduler.
|
||||
impl TaskManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
ready_queue: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
/// Add process back to ready queue
|
||||
pub fn add(&mut self, task: Arc<TaskControlBlock>) {
|
||||
self.ready_queue.push_back(task);
|
||||
}
|
||||
/// Take a process out of the ready queue
|
||||
pub fn fetch(&mut self) -> Option<Arc<TaskControlBlock>> {
|
||||
self.ready_queue.pop_front()
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// TASK_MANAGER instance through lazy_static!
|
||||
pub static ref TASK_MANAGER: UPSafeCell<TaskManager> =
|
||||
unsafe { UPSafeCell::new(TaskManager::new()) };
|
||||
}
|
||||
|
||||
pub fn add_task(task: Arc<TaskControlBlock>) {
|
||||
TASK_MANAGER.exclusive_access().add(task);
|
||||
}
|
||||
|
||||
pub fn fetch_task() -> Option<Arc<TaskControlBlock>> {
|
||||
TASK_MANAGER.exclusive_access().fetch()
|
||||
}
|
||||
99
os5-ref/src/task/mod.rs
Normal file
99
os5-ref/src/task/mod.rs
Normal file
@@ -0,0 +1,99 @@
|
||||
//! Implementation of process management mechanism
|
||||
//!
|
||||
//! Here is the entry for process scheduling required by other modules
|
||||
//! (such as syscall or clock interrupt).
|
||||
//! By suspending or exiting the current process, you can
|
||||
//! modify the process state, manage the process queue through TASK_MANAGER,
|
||||
//! and switch the control flow through PROCESSOR.
|
||||
//!
|
||||
//! Be careful when you see [`__switch`]. Control flow around this function
|
||||
//! might not be what you expect.
|
||||
|
||||
mod context;
|
||||
mod manager;
|
||||
mod pid;
|
||||
mod processor;
|
||||
mod switch;
|
||||
#[allow(clippy::module_inception)]
|
||||
mod task;
|
||||
|
||||
use crate::loader::get_app_data_by_name;
|
||||
use alloc::sync::Arc;
|
||||
use lazy_static::*;
|
||||
use manager::fetch_task;
|
||||
use switch::__switch;
|
||||
pub use task::{TaskControlBlock, TaskStatus};
|
||||
|
||||
pub use context::TaskContext;
|
||||
pub use manager::add_task;
|
||||
pub use pid::{pid_alloc, KernelStack, PidHandle};
|
||||
pub use processor::{
|
||||
current_task, current_trap_cx, current_user_token, run_tasks, schedule, take_current_task,
|
||||
};
|
||||
|
||||
/// Make current task suspended and switch to the next task
|
||||
pub fn suspend_current_and_run_next() {
|
||||
// There must be an application running.
|
||||
let task = take_current_task().unwrap();
|
||||
|
||||
// ---- access current TCB exclusively
|
||||
let mut task_inner = task.inner_exclusive_access();
|
||||
let task_cx_ptr = &mut task_inner.task_cx as *mut TaskContext;
|
||||
// Change status to Ready
|
||||
task_inner.task_status = TaskStatus::Ready;
|
||||
drop(task_inner);
|
||||
// ---- release current PCB
|
||||
|
||||
// push back to ready queue.
|
||||
add_task(task);
|
||||
// jump to scheduling cycle
|
||||
schedule(task_cx_ptr);
|
||||
}
|
||||
|
||||
/// Exit current task, recycle process resources and switch to the next task
|
||||
pub fn exit_current_and_run_next(exit_code: i32) {
|
||||
// take from Processor
|
||||
let task = take_current_task().unwrap();
|
||||
// **** access current TCB exclusively
|
||||
let mut inner = task.inner_exclusive_access();
|
||||
// Change status to Zombie
|
||||
inner.task_status = TaskStatus::Zombie;
|
||||
// Record exit code
|
||||
inner.exit_code = exit_code;
|
||||
// do not move to its parent but under initproc
|
||||
|
||||
// ++++++ access initproc TCB exclusively
|
||||
{
|
||||
let mut initproc_inner = INITPROC.inner_exclusive_access();
|
||||
for child in inner.children.iter() {
|
||||
child.inner_exclusive_access().parent = Some(Arc::downgrade(&INITPROC));
|
||||
initproc_inner.children.push(child.clone());
|
||||
}
|
||||
}
|
||||
// ++++++ release parent PCB
|
||||
|
||||
inner.children.clear();
|
||||
// deallocate user space
|
||||
inner.memory_set.recycle_data_pages();
|
||||
drop(inner);
|
||||
// **** release current PCB
|
||||
// drop task manually to maintain rc correctly
|
||||
drop(task);
|
||||
// we do not have to save task context
|
||||
let mut _unused = TaskContext::zero_init();
|
||||
schedule(&mut _unused as *mut _);
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Creation of initial process
|
||||
///
|
||||
/// the name "initproc" may be changed to any other app name like "usertests",
|
||||
/// but we have user_shell, so we don't need to change it.
|
||||
pub static ref INITPROC: Arc<TaskControlBlock> = Arc::new(TaskControlBlock::new(
|
||||
get_app_data_by_name("ch5b_initproc").unwrap()
|
||||
));
|
||||
}
|
||||
|
||||
pub fn add_initproc() {
|
||||
add_task(INITPROC.clone());
|
||||
}
|
||||
116
os5-ref/src/task/pid.rs
Normal file
116
os5-ref/src/task/pid.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
//! Task pid implementation.
|
||||
//!
|
||||
//! Assign PID to the process here. At the same time, the position of the application KernelStack
|
||||
//! is determined according to the PID.
|
||||
|
||||
use crate::config::{KERNEL_STACK_SIZE, PAGE_SIZE, TRAMPOLINE};
|
||||
use crate::mm::{MapPermission, VirtAddr, KERNEL_SPACE};
|
||||
use crate::sync::UPSafeCell;
|
||||
use alloc::vec::Vec;
|
||||
use lazy_static::*;
|
||||
|
||||
/// Process identifier allocator using stack allocation
|
||||
struct PidAllocator {
|
||||
/// A new PID to be assigned
|
||||
current: usize,
|
||||
/// Recycled PID sequence
|
||||
recycled: Vec<usize>,
|
||||
}
|
||||
|
||||
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().any(|ppid| *ppid == pid),
|
||||
"pid {} has been deallocated!",
|
||||
pid
|
||||
);
|
||||
self.recycled.push(pid);
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Pid allocator instance through lazy_static!
|
||||
static ref PID_ALLOCATOR: UPSafeCell<PidAllocator> =
|
||||
unsafe { UPSafeCell::new(PidAllocator::new()) };
|
||||
}
|
||||
|
||||
/// Abstract structure of PID
|
||||
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)
|
||||
}
|
||||
|
||||
/// KernelStack corresponding to PID
|
||||
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)]
|
||||
/// Push a variable of type T into the top of the KernelStack and return its raw pointer
|
||||
pub fn push_on_top<T>(&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::<T>()) 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());
|
||||
}
|
||||
}
|
||||
105
os5-ref/src/task/processor.rs
Normal file
105
os5-ref/src/task/processor.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
//! Implementation of [`Processor`] and Intersection of control flow
|
||||
//!
|
||||
//! Here, the continuous operation of user apps in CPU is maintained,
|
||||
//! the current running state of CPU is recorded,
|
||||
//! and the replacement and transfer of control flow of different applications are executed.
|
||||
|
||||
|
||||
use super::__switch;
|
||||
use super::{fetch_task, TaskStatus};
|
||||
use super::{TaskContext, TaskControlBlock};
|
||||
use crate::sync::UPSafeCell;
|
||||
use crate::trap::TrapContext;
|
||||
use alloc::sync::Arc;
|
||||
use lazy_static::*;
|
||||
|
||||
/// Processor management structure
|
||||
pub struct Processor {
|
||||
/// The task currently executing on the current processor
|
||||
current: Option<Arc<TaskControlBlock>>,
|
||||
/// The basic control flow of each core, helping to select and switch process
|
||||
idle_task_cx: TaskContext,
|
||||
}
|
||||
|
||||
impl Processor {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current: None,
|
||||
idle_task_cx: TaskContext::zero_init(),
|
||||
}
|
||||
}
|
||||
fn get_idle_task_cx_ptr(&mut self) -> *mut TaskContext {
|
||||
&mut self.idle_task_cx as *mut _
|
||||
}
|
||||
pub fn take_current(&mut self) -> Option<Arc<TaskControlBlock>> {
|
||||
self.current.take()
|
||||
}
|
||||
pub fn current(&self) -> Option<Arc<TaskControlBlock>> {
|
||||
self.current.as_ref().map(|task| Arc::clone(task))
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// PROCESSOR instance through lazy_static!
|
||||
pub static ref PROCESSOR: UPSafeCell<Processor> = unsafe { UPSafeCell::new(Processor::new()) };
|
||||
}
|
||||
|
||||
/// The main part of process execution and scheduling
|
||||
///
|
||||
/// Loop fetch_task to get the process that needs to run,
|
||||
/// and switch the process through __switch
|
||||
pub fn run_tasks() {
|
||||
loop {
|
||||
let mut processor = PROCESSOR.exclusive_access();
|
||||
if let Some(task) = fetch_task() {
|
||||
let idle_task_cx_ptr = processor.get_idle_task_cx_ptr();
|
||||
// access coming task TCB exclusively
|
||||
let mut task_inner = task.inner_exclusive_access();
|
||||
let next_task_cx_ptr = &task_inner.task_cx as *const TaskContext;
|
||||
task_inner.task_status = TaskStatus::Running;
|
||||
drop(task_inner);
|
||||
// release coming task TCB manually
|
||||
processor.current = Some(task);
|
||||
// release processor manually
|
||||
drop(processor);
|
||||
unsafe {
|
||||
__switch(idle_task_cx_ptr, next_task_cx_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get current task through take, leaving a None in its place
|
||||
pub fn take_current_task() -> Option<Arc<TaskControlBlock>> {
|
||||
PROCESSOR.exclusive_access().take_current()
|
||||
}
|
||||
|
||||
/// Get a copy of the current task
|
||||
pub fn current_task() -> Option<Arc<TaskControlBlock>> {
|
||||
PROCESSOR.exclusive_access().current()
|
||||
}
|
||||
|
||||
/// Get token of the address space of current task
|
||||
pub fn current_user_token() -> usize {
|
||||
let task = current_task().unwrap();
|
||||
let token = task.inner_exclusive_access().get_user_token();
|
||||
token
|
||||
}
|
||||
|
||||
/// Get the mutable reference to trap context of current task
|
||||
pub fn current_trap_cx() -> &'static mut TrapContext {
|
||||
current_task()
|
||||
.unwrap()
|
||||
.inner_exclusive_access()
|
||||
.get_trap_cx()
|
||||
}
|
||||
|
||||
/// Return to idle control flow for new scheduling
|
||||
pub fn schedule(switched_task_cx_ptr: *mut TaskContext) {
|
||||
let mut processor = PROCESSOR.exclusive_access();
|
||||
let idle_task_cx_ptr = processor.get_idle_task_cx_ptr();
|
||||
drop(processor);
|
||||
unsafe {
|
||||
__switch(switched_task_cx_ptr, idle_task_cx_ptr);
|
||||
}
|
||||
}
|
||||
34
os5-ref/src/task/switch.S
Normal file
34
os5-ref/src/task/switch.S
Normal file
@@ -0,0 +1,34 @@
|
||||
.altmacro
|
||||
.macro SAVE_SN n
|
||||
sd s\n, (\n+2)*8(a0)
|
||||
.endm
|
||||
.macro LOAD_SN n
|
||||
ld s\n, (\n+2)*8(a1)
|
||||
.endm
|
||||
.section .text
|
||||
.globl __switch
|
||||
__switch:
|
||||
# __switch(
|
||||
# current_task_cx_ptr: *mut TaskContext,
|
||||
# next_task_cx_ptr: *const TaskContext
|
||||
# )
|
||||
# save kernel stack of current task
|
||||
sd sp, 8(a0)
|
||||
# save ra & s0~s11 of current execution
|
||||
sd ra, 0(a0)
|
||||
.set n, 0
|
||||
.rept 12
|
||||
SAVE_SN %n
|
||||
.set n, n + 1
|
||||
.endr
|
||||
# restore ra & s0~s11 of next execution
|
||||
ld ra, 0(a1)
|
||||
.set n, 0
|
||||
.rept 12
|
||||
LOAD_SN %n
|
||||
.set n, n + 1
|
||||
.endr
|
||||
# restore kernel stack of next task
|
||||
ld sp, 8(a1)
|
||||
ret
|
||||
|
||||
16
os5-ref/src/task/switch.rs
Normal file
16
os5-ref/src/task/switch.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
//! Rust wrapper around `__switch`.
|
||||
//!
|
||||
//! Switching to a different task's context happens here. The actual
|
||||
//! implementation must not be in Rust and (essentially) has to be in assembly
|
||||
//! language (Do you know why?), so this module really is just a wrapper around
|
||||
//! `switch.S`.
|
||||
|
||||
core::arch::global_asm!(include_str!("switch.S"));
|
||||
|
||||
use super::TaskContext;
|
||||
|
||||
extern "C" {
|
||||
/// Switch to the context of `next_task_cx_ptr`, saving the current context
|
||||
/// in `current_task_cx_ptr`.
|
||||
pub fn __switch(current_task_cx_ptr: *mut TaskContext, next_task_cx_ptr: *const TaskContext);
|
||||
}
|
||||
199
os5-ref/src/task/task.rs
Normal file
199
os5-ref/src/task/task.rs
Normal file
@@ -0,0 +1,199 @@
|
||||
//! Types related to task management & Functions for completely changing TCB
|
||||
|
||||
use super::TaskContext;
|
||||
use super::{pid_alloc, KernelStack, PidHandle};
|
||||
use crate::config::TRAP_CONTEXT;
|
||||
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::Vec;
|
||||
use core::cell::RefMut;
|
||||
|
||||
/// Task control block structure
|
||||
///
|
||||
/// Directly save the contents that will not change during running
|
||||
pub struct TaskControlBlock {
|
||||
// immutable
|
||||
/// Process identifier
|
||||
pub pid: PidHandle,
|
||||
/// Kernel stack corresponding to PID
|
||||
pub kernel_stack: KernelStack,
|
||||
// mutable
|
||||
inner: UPSafeCell<TaskControlBlockInner>,
|
||||
}
|
||||
|
||||
/// Structure containing more process content
|
||||
///
|
||||
/// Store the contents that will change during operation
|
||||
/// and are wrapped by UPSafeCell to provide mutual exclusion
|
||||
pub struct TaskControlBlockInner {
|
||||
/// The physical page number of the frame where the trap context is placed
|
||||
pub trap_cx_ppn: PhysPageNum,
|
||||
/// Application data can only appear in areas
|
||||
/// where the application address space is lower than base_size
|
||||
pub base_size: usize,
|
||||
/// Save task context
|
||||
pub task_cx: TaskContext,
|
||||
/// Maintain the execution status of the current process
|
||||
pub task_status: TaskStatus,
|
||||
/// Application address space
|
||||
pub memory_set: MemorySet,
|
||||
/// Parent process of the current process.
|
||||
/// Weak will not affect the reference count of the parent
|
||||
pub parent: Option<Weak<TaskControlBlock>>,
|
||||
/// A vector containing TCBs of all child processes of the current process
|
||||
pub children: Vec<Arc<TaskControlBlock>>,
|
||||
/// It is set when active exit or execution error occurs
|
||||
pub exit_code: i32,
|
||||
}
|
||||
|
||||
/// Simple access to its internal fields
|
||||
impl TaskControlBlockInner {
|
||||
/*
|
||||
pub fn get_task_cx_ptr2(&self) -> *const usize {
|
||||
&self.task_cx_ptr as *const usize
|
||||
}
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl TaskControlBlock {
|
||||
/// Get the mutex to get the RefMut TaskControlBlockInner
|
||||
pub fn inner_exclusive_access(&self) -> RefMut<'_, TaskControlBlockInner> {
|
||||
self.inner.exclusive_access()
|
||||
}
|
||||
|
||||
/// Create a new process
|
||||
///
|
||||
/// At present, it is only used for the creation of initproc
|
||||
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();
|
||||
// push a task context which goes to trap_return to the top of kernel stack
|
||||
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,
|
||||
})
|
||||
},
|
||||
};
|
||||
// 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
|
||||
}
|
||||
/// Load a new elf to replace the original application address space and start execution
|
||||
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 inner 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 = inner.get_trap_cx();
|
||||
*trap_cx = TrapContext::app_init_context(
|
||||
entry_point,
|
||||
user_sp,
|
||||
KERNEL_SPACE.exclusive_access().token(),
|
||||
self.kernel_stack.get_top(),
|
||||
trap_handler as usize,
|
||||
);
|
||||
// **** release inner automatically
|
||||
}
|
||||
/// Fork from parent to child
|
||||
pub fn fork(self: &Arc<TaskControlBlock>) -> Arc<TaskControlBlock> {
|
||||
// ---- access parent PCB exclusively
|
||||
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();
|
||||
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,
|
||||
})
|
||||
},
|
||||
});
|
||||
// add child
|
||||
parent_inner.children.push(task_control_block.clone());
|
||||
// modify kernel_sp in trap_cx
|
||||
// **** access children 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 parent PCB automatically
|
||||
// **** release children PCB automatically
|
||||
}
|
||||
pub fn getpid(&self) -> usize {
|
||||
self.pid.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
/// task status: UnInit, Ready, Running, Exited
|
||||
pub enum TaskStatus {
|
||||
UnInit,
|
||||
Ready,
|
||||
Running,
|
||||
Zombie,
|
||||
}
|
||||
Reference in New Issue
Block a user