mirror of
https://github.com/LearningOS/rust-based-os-comp2022.git
synced 2026-05-08 14:51:26 +08:00
add os[1-8]-ref for os refereces, add guide, add README
This commit is contained in:
30
os3-ref/src/task/context.rs
Normal file
30
os3-ref/src/task/context.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
//! Implementation of [`TaskContext`]
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
/// task context structure containing some registers
|
||||
pub struct TaskContext {
|
||||
ra: usize,
|
||||
sp: usize,
|
||||
s: [usize; 12],
|
||||
}
|
||||
|
||||
impl TaskContext {
|
||||
pub fn zero_init() -> Self {
|
||||
Self {
|
||||
ra: 0,
|
||||
sp: 0,
|
||||
s: [0; 12],
|
||||
}
|
||||
}
|
||||
pub fn goto_restore(kstack_ptr: usize) -> Self {
|
||||
extern "C" {
|
||||
fn __restore();
|
||||
}
|
||||
Self {
|
||||
ra: __restore as usize,
|
||||
sp: kstack_ptr,
|
||||
s: [0; 12],
|
||||
}
|
||||
}
|
||||
}
|
||||
176
os3-ref/src/task/mod.rs
Normal file
176
os3-ref/src/task/mod.rs
Normal file
@@ -0,0 +1,176 @@
|
||||
//! Task management implementation
|
||||
//!
|
||||
//! Everything about task management, like starting and switching tasks is
|
||||
//! implemented here.
|
||||
//!
|
||||
//! A single global instance of [`TaskManager`] called `TASK_MANAGER` controls
|
||||
//! all the tasks in the operating system.
|
||||
//!
|
||||
//! Be careful when you see [`__switch`]. Control flow around this function
|
||||
//! might not be what you expect.
|
||||
|
||||
mod context;
|
||||
mod switch;
|
||||
#[allow(clippy::module_inception)]
|
||||
mod task;
|
||||
|
||||
use crate::config::{MAX_APP_NUM, MAX_SYSCALL_NUM};
|
||||
use crate::loader::{get_num_app, init_app_cx};
|
||||
use crate::sync::UPSafeCell;
|
||||
use lazy_static::*;
|
||||
pub use switch::__switch;
|
||||
pub use task::{TaskControlBlock, TaskStatus};
|
||||
|
||||
pub use context::TaskContext;
|
||||
|
||||
/// The task manager, where all the tasks are managed.
|
||||
///
|
||||
/// Functions implemented on `TaskManager` deals with all task state transitions
|
||||
/// and task context switching. For convenience, you can find wrappers around it
|
||||
/// in the module level.
|
||||
///
|
||||
/// Most of `TaskManager` are hidden behind the field `inner`, to defer
|
||||
/// borrowing checks to runtime. You can see examples on how to use `inner` in
|
||||
/// existing functions on `TaskManager`.
|
||||
pub struct TaskManager {
|
||||
/// total number of tasks
|
||||
num_app: usize,
|
||||
/// use inner value to get mutable access
|
||||
inner: UPSafeCell<TaskManagerInner>,
|
||||
}
|
||||
|
||||
/// The task manager inner in 'UPSafeCell'
|
||||
struct TaskManagerInner {
|
||||
/// task list
|
||||
tasks: [TaskControlBlock; MAX_APP_NUM],
|
||||
/// id of current `Running` task
|
||||
current_task: usize,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// a `TaskManager` instance through lazy_static!
|
||||
pub static ref TASK_MANAGER: TaskManager = {
|
||||
let num_app = get_num_app();
|
||||
let mut tasks = [TaskControlBlock {
|
||||
task_cx: TaskContext::zero_init(),
|
||||
task_status: TaskStatus::UnInit,
|
||||
}; MAX_APP_NUM];
|
||||
for (i, t) in tasks.iter_mut().enumerate().take(num_app) {
|
||||
t.task_cx = TaskContext::goto_restore(init_app_cx(i));
|
||||
t.task_status = TaskStatus::Ready;
|
||||
}
|
||||
TaskManager {
|
||||
num_app,
|
||||
inner: unsafe {
|
||||
UPSafeCell::new(TaskManagerInner {
|
||||
tasks,
|
||||
current_task: 0,
|
||||
})
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl TaskManager {
|
||||
/// Run the first task in task list.
|
||||
///
|
||||
/// Generally, the first task in task list is an idle task (we call it zero process later).
|
||||
/// But in ch3, we load apps statically, so the first task is a real app.
|
||||
fn run_first_task(&self) -> ! {
|
||||
let mut inner = self.inner.exclusive_access();
|
||||
let task0 = &mut inner.tasks[0];
|
||||
task0.task_status = TaskStatus::Running;
|
||||
let next_task_cx_ptr = &task0.task_cx as *const TaskContext;
|
||||
drop(inner);
|
||||
let mut _unused = TaskContext::zero_init();
|
||||
// before this, we should drop local variables that must be dropped manually
|
||||
unsafe {
|
||||
__switch(&mut _unused as *mut TaskContext, next_task_cx_ptr);
|
||||
}
|
||||
panic!("unreachable in run_first_task!");
|
||||
}
|
||||
|
||||
/// Change the status of current `Running` task into `Ready`.
|
||||
fn mark_current_suspended(&self) {
|
||||
let mut inner = self.inner.exclusive_access();
|
||||
let current = inner.current_task;
|
||||
inner.tasks[current].task_status = TaskStatus::Ready;
|
||||
}
|
||||
|
||||
/// Change the status of current `Running` task into `Exited`.
|
||||
fn mark_current_exited(&self) {
|
||||
let mut inner = self.inner.exclusive_access();
|
||||
let current = inner.current_task;
|
||||
inner.tasks[current].task_status = TaskStatus::Exited;
|
||||
}
|
||||
|
||||
/// Find next task to run and return task id.
|
||||
///
|
||||
/// In this case, we only return the first `Ready` task in task list.
|
||||
fn find_next_task(&self) -> Option<usize> {
|
||||
let inner = self.inner.exclusive_access();
|
||||
let current = inner.current_task;
|
||||
(current + 1..current + self.num_app + 1)
|
||||
.map(|id| id % self.num_app)
|
||||
.find(|id| inner.tasks[*id].task_status == TaskStatus::Ready)
|
||||
}
|
||||
|
||||
/// Switch current `Running` task to the task we have found,
|
||||
/// or there is no `Ready` task and we can exit with all applications completed
|
||||
fn run_next_task(&self) {
|
||||
if let Some(next) = self.find_next_task() {
|
||||
let mut inner = self.inner.exclusive_access();
|
||||
let current = inner.current_task;
|
||||
inner.tasks[next].task_status = TaskStatus::Running;
|
||||
inner.current_task = next;
|
||||
let current_task_cx_ptr = &mut inner.tasks[current].task_cx as *mut TaskContext;
|
||||
let next_task_cx_ptr = &inner.tasks[next].task_cx as *const TaskContext;
|
||||
drop(inner);
|
||||
// before this, we should drop local variables that must be dropped manually
|
||||
unsafe {
|
||||
__switch(current_task_cx_ptr, next_task_cx_ptr);
|
||||
}
|
||||
// go back to user mode
|
||||
} else {
|
||||
panic!("All applications completed!");
|
||||
}
|
||||
}
|
||||
|
||||
// LAB1: Try to implement your function to update or get task info!
|
||||
}
|
||||
|
||||
/// Run the first task in task list.
|
||||
pub fn run_first_task() {
|
||||
TASK_MANAGER.run_first_task();
|
||||
}
|
||||
|
||||
/// Switch current `Running` task to the task we have found,
|
||||
/// or there is no `Ready` task and we can exit with all applications completed
|
||||
fn run_next_task() {
|
||||
TASK_MANAGER.run_next_task();
|
||||
}
|
||||
|
||||
/// Change the status of current `Running` task into `Ready`.
|
||||
fn mark_current_suspended() {
|
||||
TASK_MANAGER.mark_current_suspended();
|
||||
}
|
||||
|
||||
/// Change the status of current `Running` task into `Exited`.
|
||||
fn mark_current_exited() {
|
||||
TASK_MANAGER.mark_current_exited();
|
||||
}
|
||||
|
||||
/// Suspend the current 'Running' task and run the next task in task list.
|
||||
pub fn suspend_current_and_run_next() {
|
||||
mark_current_suspended();
|
||||
run_next_task();
|
||||
}
|
||||
|
||||
/// Exit the current 'Running' task and run the next task in task list.
|
||||
pub fn exit_current_and_run_next() {
|
||||
mark_current_exited();
|
||||
run_next_task();
|
||||
}
|
||||
|
||||
// LAB1: Public functions implemented here provide interfaces.
|
||||
// You may use TASK_MANAGER member functions to handle requests.
|
||||
34
os3-ref/src/task/switch.S
Normal file
34
os3-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
os3-ref/src/task/switch.rs
Normal file
16
os3-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);
|
||||
}
|
||||
20
os3-ref/src/task/task.rs
Normal file
20
os3-ref/src/task/task.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
//! Types related to task management
|
||||
|
||||
use super::TaskContext;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
/// task control block structure
|
||||
pub struct TaskControlBlock {
|
||||
pub task_status: TaskStatus,
|
||||
pub task_cx: TaskContext,
|
||||
// LAB1: Add whatever you need about the Task.
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
/// task status: UnInit, Ready, Running, Exited
|
||||
pub enum TaskStatus {
|
||||
UnInit,
|
||||
Ready,
|
||||
Running,
|
||||
Exited,
|
||||
}
|
||||
Reference in New Issue
Block a user