进程管理的核心结构以及0号进程的创建

ch5
zhangxinyu 2 years ago
parent 2f40dbce77
commit d1dc7056a5

@ -50,13 +50,14 @@ pub fn rust_main(){
// 初始化动态内存分配器, 使我们能在内核中使用动态大小数据类型
mm::init();
use loader;
println!("{:?}", loader::get_app_data_by_name("00power_3"));
// trap::init();
// trap::enable_timer_interrupt(); // 允许定时器中断
// timer::set_next_trigger(); // 在进入用户态之前, 设置一个时钟中断, 防止第一个用户任务死循环
// task::run_first_task();
trap::init();
trap::enable_timer_interrupt(); // 允许定时器中断
timer::set_next_trigger(); // 在进入用户态之前, 设置一个时钟中断, 防止第一个用户任务死循环
loader::list_apps();
task::add_initproc();
// task::run_tasks();
panic!("Disable run here")
}

@ -2,7 +2,8 @@
use crate::mm::page_table::translated_byte_buffer;
use crate::print;
use crate::task::TASK_MANAGER;
use crate::task::manager::TASK_MANAGER;
use crate::task::processor::current_user_token;
const FD_STDOUT: usize = 1;
@ -12,7 +13,7 @@ pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize {
FD_STDOUT => {
// 根据当前页表 找到物理内存基地址
// 这里需要从应用的地址空间里面寻找数据
let buffers = translated_byte_buffer(TASK_MANAGER.get_current_token(), buf, len);
let buffers = translated_byte_buffer(current_user_token(), buf, len);
for buffer in buffers {
print!("{}", core::str::from_utf8(buffer).unwrap());
}

@ -1,13 +1,15 @@
//! App management syscalls
// use crate::batch::run_next_app;
use crate::println;
use crate::task::{change_program_brk, exit_current_and_run_next, suspend_current_and_run_next};
use crate::task::{exit_current_and_run_next, suspend_current_and_run_next};
use crate::task::processor::change_program_brk;
// use crate::task::{change_program_brk, exit_current_and_run_next, suspend_current_and_run_next};
use crate::timer::get_time_ms;
/// 任务退出, 并立即切换任务
pub fn sys_exit(exit_code: i32) -> ! {
println!("[kernel] Application exited with code {}", exit_code);
exit_current_and_run_next();
exit_current_and_run_next(exit_code);
panic!("Unreachable in sys_exit!");
}

@ -0,0 +1,46 @@
use alloc::collections::VecDeque;
use alloc::sync::Arc;
use lazy_static::lazy_static;
use crate::println;
use crate::task::task::TaskControlBlock;
use crate::task::UPSafeCell;
/// 任务管理器 仅负责管理所有任务, 由ch4 TaskManager 拆分的,
/// 先入先出的双端队列
pub struct TaskManager {
ready_queue: VecDeque<Arc<TaskControlBlock>>,
}
impl TaskManager {
pub fn new() -> Self {
Self {
ready_queue: VecDeque::new(),
}
}
}
impl TaskManager {
// 添加一个任务到 队列中
pub fn add(&mut self, task: Arc<TaskControlBlock>) {
self.ready_queue.push_back(task);
}
// 如果有任务, 弹出第一个任务
pub fn fetch(&mut self) -> Option<Arc<TaskControlBlock>> {
self.ready_queue.pop_front()
}
}
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 pop_task() -> Option<Arc<TaskControlBlock>> {
TASK_MANAGER.exclusive_access().fetch()
}

@ -5,232 +5,111 @@ use crate::sync::UPSafeCell;
use crate::task::task::{TaskControlBlock, TaskStatus};
use crate::loader::{get_num_app, get_app_data};
use crate::println;
use crate::sbi::shutdown;
use crate::task::context::TaskContext;
use crate::task::manager::{add_task, TASK_MANAGER};
use crate::task::processor::{schedule, take_current_task};
use crate::task::switch::__switch;
use crate::timer::{get_time_ms, get_time_us};
use crate::trap::TrapContext;
use alloc::sync::Arc;
use crate::loader::get_app_data_by_name;
mod context;
mod switch;
mod task;
pub mod task;
mod pid;
pub mod manager;
pub mod processor;
pub use processor::{run_tasks};
// 公开到外部的一个全局任务管理器的结构体
pub struct TaskManager {
num_app: usize, // app的数量 这个在os运行之后就不会有变化
inner: UPSafeCell<TaskManagerInner>, // 这个内部TaskControlBlock 会随着系统运行发生变化
}
impl TaskManager {
// 即将进入用户态, 把之前使用的内核时间统计
pub fn user_time_start(&self){
let mut inner = self.inner.exclusive_access();
let current_task_id = inner.current_task_id;
let kernel_use_time = inner.refresh_stop_clock();
inner.tasks[current_task_id].kernel_time += kernel_use_time;
}
pub fn kernel_time_start(&self){
let mut inner = self.inner.exclusive_access();
let current_task_id = inner.current_task_id;
let user_use_time = inner.refresh_stop_clock();
inner.tasks[current_task_id].user_time += user_use_time;
}
fn run_first_task(&self){
// 得到下一个需要执行的任务,的上下文 这里我们由于第一次执行, 下一个任务我们指定为0下标的任务即可
let next_task_context_ptr = {
let mut inner = self.inner.exclusive_access();
// 第一次掐表, 开始记录时间
inner.refresh_stop_clock();
let task_0 = &mut inner.tasks[0];
// 修改任务0的状态
task_0.task_status = TaskStatus::Running;
&(task_0.task_cx) as *const TaskContext
};
// 由于第一次切换, 我们current task context 不存在, 所以我们手动创建一个, 在调用switch的时候
// 这里调用switch的状态会被保存在unused 这里, 但是一旦程序开始运行, 就开始在内核栈或者用户栈之间切换, 永远
// 不会走到这里了
let mut unused = TaskContext::new();
println!("start run app");
unsafe {
__switch(&mut unused as *mut TaskContext, next_task_context_ptr)
}
//
panic!("unreachable in run_first_task!");
}
// 停止当前正在运行的任务
fn mark_current_exit(&self){
let mut inner = self.inner.exclusive_access();
let current_task_id = inner.current_task_id;
let kernel_use_time = inner.refresh_stop_clock();
let current_task = &mut inner.tasks[current_task_id];
current_task.task_status = TaskStatus::Exited;
// 统计内核时间, 并输出这个任务 花费了多少内核时间
current_task.kernel_time += kernel_use_time;
println!("task {:?}, exit, kernel_time: {:?}ms, user_time: {:?}ms", current_task_id, current_task.kernel_time/1000, current_task.user_time/1000);
}
// 挂起当前任务
fn mark_current_suspend(&self){
let mut inner = self.inner.exclusive_access();
let current_task_id = inner.current_task_id;
let kernel_use_time = inner.refresh_stop_clock();
let current_task = &mut inner.tasks[current_task_id];
current_task.task_status = TaskStatus::Ready;
// 统计内核时间(因为内核进入到这里之前肯定是掐表了)
// 挂起任务A之后,这里也掐表了, 切到新的任务B, B开始执行, B在挂起的时候 也会走入到这里掐表, 顺便并得到任务A掐表到现在任务B掐表准备切换任务出去中间的时间
current_task.kernel_time += kernel_use_time;
}
pub const IDLE_PID: usize = 0;
// 运行下一个任务
fn run_next_task(&self){
if let Some(next_task_id) = self.find_next_task() {
// 得到当前任务context ptr 以及 下一个任务context的ptr
let (current_task_ptr,next_task_ptr) = {
let mut inner = self.inner.exclusive_access();
// 当前任务id
let current_task_id = inner.current_task_id;
// 修改current_task_id 为 下一个任务, 因为一旦到switch就是在运行下一个任务了
inner.current_task_id = next_task_id;
// 修改下一个任务的状态
inner.tasks[next_task_id].task_status = TaskStatus::Running;
lazy_static! {
/// 初始化用户shell的全局进程, 他的pid是0
pub static ref INITPROC: Arc<TaskControlBlock> = Arc::new(TaskControlBlock::from(get_app_data_by_name("initproc").unwrap()));
}
// 得到current的ptr
let current_task_ptr = &mut inner.tasks[current_task_id].task_cx as *mut TaskContext;
/// 添加初始化 Process 到 全局任务管理器
pub fn add_initproc() {
add_task(INITPROC.clone());
}
// 得到需要被切换的下一个任务ptr
let next_task_ptr = &inner.tasks[next_task_id].task_cx as *const TaskContext;
(current_task_ptr, next_task_ptr)
};
// 挂起当前运行下一个, 需要把当前任务 从Process中 take 取出来, 然后再 push回 全局的任务管理器
pub fn suspend_current_and_run_next() {
// 必须有一个程序在运行,
let task = take_current_task().unwrap();
// ---- access current TCB exclusively
// 当前正在执行的Process
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回全局的任务管理器
add_task(task);
// jump to scheduling cycle
schedule(task_cx_ptr);
}
// 开始伟大的切换!
unsafe {
__switch(current_task_ptr, next_task_ptr);
}
pub fn exit_current_and_run_next(exit_code: i32) {
// 从 Process 管理器中 转移所有权的取出当前cpu执行的任务
let task = take_current_task().unwrap();
// 当前pid
let pid = task.get_pid();
// 如果是idle进程
if pid == IDLE_PID {
println!(
"[kernel] Idle process exit with exit_code {} ...",
exit_code
);
if exit_code != 0 {
//crate::sbi::shutdown(255); //255 == -1 for err hint
shutdown()
} else {
panic!("All applications completed!");
//crate::sbi::shutdown(0); //0 for success hint
shutdown()
}
}
// 找到一个除当前任务之外的 是Ready状态的任务的id
fn find_next_task(&self) -> Option<usize>{
let mut inner = self.inner.exclusive_access();
let current = inner.current_task_id;
for task_id in current + 1..current + self.num_app + 1 {
let _tmp_id = task_id % self.num_app;
if inner.tasks[_tmp_id].task_status == TaskStatus::Ready {
return Some(_tmp_id)
}
}
None
}
// 改变pcb 的状态, 从当前task获取inner, 也就是当前当前pcb需要变化的值
let mut inner = task.inner_exclusive_access();
// 将状态更改为 僵尸进程, 供后续父进程查找回收资源
inner.task_status = TaskStatus::Zombie;
// 写入退出码
inner.exit_code = exit_code;
// 不需要修改父进程, 因为所有进程的父进程这里都为 initproc
// 得到当前正在执行用户应用的 trap context
pub fn get_current_trap_cx(&self) -> &'static mut TrapContext {
let inner = self.inner.exclusive_access();
inner.tasks[inner.current_task_id].get_trap_cx()
// 把当前进程, 所有的子进程, 都挂载到 initproc进程
{
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()); // 挂载到initproc进程的childre中, 引用计数 +1, 现在是2
}
// 得到当前正在只用的用户应用的 页表
pub fn get_current_token(&self) -> usize {
let inner = self.inner.exclusive_access();
inner.tasks[inner.current_task_id].get_user_token()
}
// 扩张/缩减, 当前应用的地址空间中的堆段, 指定字节的数据
pub fn change_current_program_brk(&self, size: i32) -> Option<usize> {
let mut inner = self.inner.exclusive_access();
let current_task_id = inner.current_task_id;
inner.tasks[current_task_id].change_program_brk(size)
}
}
inner.children.clear(); // 子进程引用计数-1 现在又变成1了
// 不公开的结构体, 有一个MAX_APP_NUM大小的数组, 用来保存TCB, 和当前任务的的TCB的下标
struct TaskManagerInner {
tasks: Vec<TaskControlBlock>,
current_task_id: usize,
stop_clock_time: usize, // 记录最近一次停表时间
}
// 释放当前地址空间中, 逻辑段清空, areas->MapArea->data_frames->FrameTracker 执行析构, 过程是把areas中的物理页帧 根据 RAII还给操作系统
// 注意 页表本身占用的 三级页表的物理帧(page_table->frames), 还没有被释放, 因为我们还需要使用, 这需要父进程管理进行释放
inner.memory_set.recycle_data_pages();
drop(inner);
impl TaskManagerInner {
fn refresh_stop_clock(&mut self) -> usize {
// 上次停表的时间
let start_time = self.stop_clock_time;
// 当前时间
self.stop_clock_time = get_time_us();
// 返回 当前时间 - 上次停表时间 = 时间间距
self.stop_clock_time - start_time
}
}
// 手动删除tcb, 后面这里永远不会执行了, 我们智能指针需要手动释放(这里需要给引用计数-1)
drop(task);
lazy_static! {
pub static ref TASK_MANAGER: TaskManager = {
println!("init TASK_MANAGER");
let num_app = get_num_app();
println!("num_app = {}", num_app);
let mut tasks: Vec<TaskControlBlock> = Vec::new();
for i in 0..num_app {
// 得到elf 的二进制数据
let app_elf_data = get_app_data(i);
// 根据二进制数据创建 TCB
let tcb = TaskControlBlock::from(app_elf_data, i);
tasks.push(tcb);
}
TaskManager {
num_app,
inner: unsafe {
UPSafeCell::new(TaskManagerInner {
tasks,
stop_clock_time: 0,
current_task_id: 0, // 从第0个启动
})
},
}
};
}
// 由内核调用, 在main中, 用来 作为执行用户应用的开端
pub fn run_first_task() {
TASK_MANAGER.run_first_task();
}
// 挂起当前任务, 并运行下一个
pub fn suspend_current_and_run_next(){
TASK_MANAGER.mark_current_suspend();
TASK_MANAGER.run_next_task();
}
// 退出当前任务, 并运行下一个
pub fn exit_current_and_run_next(){
TASK_MANAGER.mark_current_exit();
TASK_MANAGER.run_next_task();
}
// 扩张/缩减, 当前应用的地址空间中的堆段, 指定字节的数据
pub fn change_program_brk(size: i32) -> Option<usize> {
TASK_MANAGER.change_current_program_brk(size)
}
// 当前task 函数 为什么在 recycle_data_pages 之后还能执行? 函数栈帧为什么还能使用? 是因为我们虽然把 应用程序锁使用的逻辑页帧都取消映射了, 但是关于 task中的 pid 和kernel stack 还没有被释放
// task 他在被fork的时候 parent_inner.children.push(task_control_block.clone()); 也clone了自己一份到父进程中, 所以这里删除drop只是删除了当前 Process中管理的引用计数(后续我们不会执行到这里了, 需要手动删除)
// 得到当前运行任务的 trap context 的地址
pub fn current_trap_cx() -> &'static mut TrapContext {
TASK_MANAGER.get_current_trap_cx()
// 这个进程不会被执行了, 我们当前栈帧随便创建一个上下文就好了
let mut _unused = TaskContext::new();
schedule(&mut _unused as *mut _);
}

@ -1,6 +1,7 @@
use alloc::vec::Vec;
use lazy_static::lazy_static;
use crate::config::{KERNEL_STACK_SIZE, PAGE_SIZE, TRAMPOLINE};
use crate::mm::address::VirtAddr;
use crate::mm::memory_set::{KERNEL_SPACE, MapPermission};
use crate::sync::UPSafeCell;
@ -16,6 +17,11 @@ lazy_static! {
unsafe { UPSafeCell::new(PidAllocator::new()) };
}
pub fn pid_alloc() -> PidHandle {
PID_ALLOCATOR.exclusive_access().alloc()
}
impl PidAllocator {
pub fn new() -> Self {
Self {
@ -105,7 +111,15 @@ impl KernelStack {
pub fn from(pid_handle: &PidHandle) -> Self {
let pid = pid_handle.0;
// 根据 pid 创建受保护的用户的任务内核栈, 并得到栈的起始地址和结束地址, 这个在次高页的下面的 某个位置
// kernel_stack_bottom = kernel_stack_top - PAGESIZE * 2
// kernel_stack_top 是当前栈的栈顶, 同时也是现在没有push操作也是栈底
// 这里都是虚拟地址
let (kernel_stack_bottom, kernel_stack_top) = kernel_stack_position(pid);
// 把用户的内核栈逻辑段, 映射并创建出来实际的物理页帧, 到内核地址空间, 供内核态可以进行寻址找到用户应用的内核栈,
// 这里使用虚拟映射 而不是恒等映射, 是因为需要动态调整?
// 这个只有在内核会用到
KERNEL_SPACE
.exclusive_access()
.insert_framed_area(
@ -118,5 +132,18 @@ impl KernelStack {
}
// 当kernel stack 生命周期结束, 对应 new 出来的一个 应用内核栈 逻辑段也随之删除
// 注意! 一般这个是在 wait_pid 内用的, 用于父进程回收子进程资源时, 子进程内的字段根据RAII执行
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());
}
}

@ -0,0 +1,170 @@
use alloc::sync::Arc;
use lazy_static::lazy_static;
use crate::task::context::TaskContext;
use crate::task::switch::__switch;
use crate::task::task::{TaskControlBlock, TaskStatus};
use crate::trap::TrapContext;
use crate::sync::UPSafeCell;
use crate::task::manager::{pop_task, TASK_MANAGER};
use crate::timer::get_time_us;
///维护在一个处理器上正在执行的任务, 这在ch4 中是 TaskManager做的, 现在拆分
pub struct Processor {
/// 当前处理器上正在执行的任务, 他是一个被option 包裹的 智能指针, 保证 TCB 要不在这个结构体中, 要不就是在全局的任务管理器上
current: Option<Arc<TaskControlBlock>>,
/// 当前处理器上的 idle 控制流的任务上下文, 当任务切换的时候, 先切换到这个
/// 这样做的主要目的是使得换入/换出进程和调度执行流在内核层各自执行在不同的内核栈上,
/// 分别是进程自身的内核栈和内核初始化时使用的启动栈
/// 这样的话, 调度相关的数据不会出现在进程内核栈上,
/// 也使得调度机制对于换出进程的Trap执行流是不可见的
/// 它在决定换出的时候只需调用schedule而无需操心调度的事情
/// 从而各执行流的分工更加明确了, 虽然带来了更大的开销
/// 虽然 永远不会执行这个 task context 的任务, 他只保存 内核的启动栈
idle_task_cx: TaskContext,
pub clock_time: usize, // 处理器时间
}
impl Processor {
///Create an empty Processor
/// 一旦程序开始运行, 就开始在内核栈或者用户栈之间切换, 永远 不会运行到这个, 这个就是保存 内核的启动栈的
pub fn new() -> Self {
Self {
current: None,
idle_task_cx: TaskContext::new(),
clock_time: 0
}
}
}
impl Processor {
// 得到当前 task context 的指针
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()
}
///当前正在执行的任务的 copy
pub fn current(&self) -> Option<Arc<TaskControlBlock>> {
self.current.as_ref().map(Arc::clone)
}
// 掐表函数
fn refresh_stop_clock(&mut self) -> usize {
// 上次停表的时间
let start_time = self.clock_time;
// 当前时间
self.clock_time = get_time_us();
// 返回 当前时间 - 上次停表时间 = 时间间距
self.clock_time - start_time
}
// 即将进入用户态, 把之前使用的内核时间统计
pub fn user_time_start(&mut self) {
let use_clock_time = self.refresh_stop_clock();
self.current.as_ref().unwrap().inner_exclusive_access().kernel_time += use_clock_time;
}
pub fn kernel_time_start(&mut self) {
let use_clock_time = self.refresh_stop_clock();
self.current.as_ref().unwrap().inner_exclusive_access().user_time += use_clock_time;
}
}
// 全局 描述cpu状态的管理器
lazy_static! {
pub static ref PROCESSOR: UPSafeCell<Processor> = unsafe { UPSafeCell::new(Processor::new()) };
}
// 循环 全局的任务管理器'fetch_task'获取需要运行的下一个进程, 并通过'__switch' 和当前process切换进程,
// 这和之前的 run_first_task 差不多
pub fn run_tasks() {
loop {
// 获得当前 进程的管理对象
let mut processor = PROCESSOR.exclusive_access();
// 弹出一个任务
if let Some(task) = pop_task() {
// 当前运行的进程(第一次运行时,这里是空的 zero, 但是不影响, 因为永远不会执行到第一次运行的任务)
let idle_task_cx_ptr = processor.get_idle_task_cx_ptr();
// 获得下一个需要运行的进程 的task context的指针
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);
// 修改process的状态, 把下一个需要运行的进程 move到 Process中被管理, 保证引用计数为1
processor.current = Some(task);
// 这里需要手动释放, 同上, 因为switch跳出作用域了, 不会通过作用域自动析构 refcell
drop(processor);
unsafe {
__switch(idle_task_cx_ptr, next_task_cx_ptr);
// 调用 schedule 之后 内部又调用__switch, next task 也就是idle task会走入到这里, 开启新的循环获取新的任务
// 其实 idle task 并不会执行, 他只是保存 内核的启动栈 也就是当前函数 的寄存器状态
}
}
}
}
// 取出当前正在执行的任务
pub fn take_current_task() -> Option<Arc<TaskControlBlock>> {
PROCESSOR.exclusive_access().take_current()
}
// 返回当前正在执行的任务的 copy版本
pub fn current_task() -> Option<Arc<TaskControlBlock>> {
PROCESSOR.exclusive_access().current()
}
//得到当前正在执行任务的token
pub fn current_user_token() -> usize {
// 得到当前正在执行的任务
let task = current_task().unwrap();
// 得到当前正在执行任务的token
let token = task.inner_exclusive_access().get_user_token();
token
}
// 得到当前 task context 的指针
pub fn current_trap_cx() -> &'static mut TrapContext {
current_task()
.unwrap()
.inner_exclusive_access()
.get_trap_cx()
}
// 扩张/缩减, 当前运行进程的地址空间中的堆段, 指定字节的数据
pub fn change_program_brk(size: i32) -> Option<usize> {
current_task().as_ref().unwrap().inner_exclusive_access().change_program_brk(size)
}
// 传入当前运行的任务指针, 切换到 idle 任务
// 用尽时间片, 或者调用 yield之后会调用这个, 调用这个之后, 会回到 run_task __switch返回的位置, 开启下一轮循环 中执行
pub fn schedule(switched_task_cx_ptr: *mut TaskContext) {
// 进程管理对象
let mut processor = PROCESSOR.exclusive_access();
// 得到当前进程管理中 task context 指针
let idle_task_cx_ptr = processor.get_idle_task_cx_ptr();
drop(processor);
// 切换 汇编会保存当前的寄存器状态到 switched_task_cx_ptr 中, 然后根据 idle_task_cx_ptr 继续执行 __switch 的下一行, 结束当前循环, 开启新的循环
unsafe {
__switch(switched_task_cx_ptr, idle_task_cx_ptr);
}
}

@ -1,29 +1,44 @@
use alloc::sync::{Arc, Weak};
use alloc::vec::Vec;
use core::cell::RefMut;
use crate::config::{TRAP_CONTEXT};
use crate::mm::address::{PhysPageNum, VirtAddr};
use crate::mm::memory_set::{KERNEL_SPACE, MapPermission, MemorySet};
use crate::println;
use crate::sync::UPSafeCell;
use crate::task::context::{TaskContext};
use crate::task::pid::kernel_stack_position;
use crate::task::pid::{kernel_stack_position, KernelStack, pid_alloc, PidHandle};
use crate::trap::{trap_handler, TrapContext};
// TCB的字段, 用来保存任务的状态
#[derive(Copy, Clone, PartialEq)]
pub enum TaskStatus {
UnInit, // 未初始化
Ready, // 准备运行
Running, // 正在运行
Exited, // 已退出
Ready,
Running,
Zombie,
}
// 一个任务的主体, 用来保存或者控制一个任务所有需要的东西
// 把 ch4 的TaskManagerInner 和 TaskControlBlock 整合为 TaskControlBlock, 现在被 TaskManager 关联和管理
// 在内核中, 它等价与一个进程
pub struct TaskControlBlock {
// 初始化后不再变化的元数据
pub pid: PidHandle,
pub kernel_stack: KernelStack,
// 初始化后 需要变化的元数据
inner: UPSafeCell<TaskControlBlockInner>,
}
pub struct TaskControlBlockInner {
pub user_time: usize, // 用户态程序用的时间
pub kernel_time: usize, // 内核态程序所用的时间
pub task_status: TaskStatus,
pub trap_cx_ppn: PhysPageNum, // tcb访问 trap context所在的真实的物理页, 它对应逻辑页的次高页
pub task_cx: TaskContext,
pub task_status: TaskStatus,
pub memory_set: MemorySet, // tcb他自己的地址空间
pub trap_cx_ppn: PhysPageNum, // tcb访问 trap context所在的真实的物理页, 它对应逻辑页的次高页
pub parent: Option<Weak<TaskControlBlock>>, // 不包含父进程引用计数的弱指针
pub children: Vec<Arc<TaskControlBlock>>, // 所有的子进程 arc智能指针, 只有引用计数为0的时候, 绑定的各类资源才能回收
pub exit_code: i32, // 该进程退出时的 退出码
pub base_size: usize, // 应用地址空间中从0x0开始到用户栈结束一共包含多少字节, 就是用户数据有多大
pub heap_bottom: usize, // 堆的起始地址 这个一开始和base_size 是一样的
pub program_brk: usize, // 这个一开始和base_size 是一样的, 表示进程当前的堆边界, 即堆的顶部地址, 它指向堆中最后一个已分配内存块的末尾, 下一个内存分配将从该地址开始
@ -31,11 +46,21 @@ pub struct TaskControlBlock {
}
impl TaskControlBlock {
pub fn from(elf_data: &[u8], app_id: usize) -> Self {
// 得到具有内部可变性的字段
pub fn inner_exclusive_access(&self) -> RefMut<'_, TaskControlBlockInner> {
self.inner.exclusive_access()
}
pub fn get_pid(&self) -> usize {
self.pid.0
}
}
impl TaskControlBlock {
// 根据 elf data 新建一个 TCB, 这个只在初始化 INITPROC 时候用
pub fn from(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);
// 在当前的地址空间中, 找到 trap_context 逻辑页 所在的真实物理页, 他逻辑地址在次高页, 真实物理地址是我们在 from_elf 申请映射的未知地址
// 这个真实的物理地址, 我们后续再陷入的时候会用到, 进行强写数据
let trap_cx_ppn = memory_set
@ -44,38 +69,38 @@ impl TaskControlBlock {
.unwrap()
.ppn(); // pte 内的ppn, 得到实际 虚拟内存次高页 所在的物理页号
// 初始化任务状态
let task_status = TaskStatus::Ready;
// map a kernel-stack in kernel space
// 根据 app_id 创建受保护的用户的任务内核栈, 并得到栈的起始地址和结束地址, 这个在次高页的下面的 某个位置
// kernel_stack_bottom = kernel_stack_top - PAGESIZE * 2
// kernel_stack_top 是当前栈的栈顶, 同时也是现在没有push操作也是栈底
// 这里都是虚拟地址
let (kernel_stack_bottom, kernel_stack_top) = kernel_stack_position(app_id);
// 从全局pid分配器, 得到一个pid
let pid_handle = pid_alloc();
// 根据pid, 得到内核栈并得到高地址(栈底)
let kernel_stack = KernelStack::from(&pid_handle);
let kernel_stack_top = kernel_stack.get_top();
// 把用户的内核栈逻辑段, 映射并创建出来实际的物理页帧, 到内核地址空间, 供内核态可以进行寻址找到用户应用的内核栈,
// 这里使用虚拟映射 而不是恒等映射, 是因为需要动态调整?
// 这个只有在内核会用到
KERNEL_SPACE.exclusive_access().insert_framed_area(
kernel_stack_bottom.into(),
kernel_stack_top.into(),
MapPermission::R | MapPermission::W,
);
// 构建 任务控制块
let task_control_block = Self {
pid: pid_handle,
kernel_stack: kernel_stack,
inner: unsafe {
UPSafeCell::new(TaskControlBlockInner{
user_time: 0,
kernel_time: 0,
task_status,
task_status:TaskStatus::Ready,
task_cx: TaskContext::goto_trap_return(kernel_stack_top), // 根据内核栈构造任务切换上下文, 并把 switch任务切换的 ra 设置为 trap_return的函数地址
memory_set, // 新增, 当前任务的地址空间
parent: None,
children: Vec::new(),
trap_cx_ppn, // 新增 逻辑次高页的trap context, 对应的这个是真实的物理页, 我们这里保存一份, 省的在memory_set 里面查找了
base_size: user_sp, // 用户栈顶以下 是代码中的各种逻辑段+栈段, 应用地址空间中从0x0开始到用户栈结束一共包含多少字节, 所以大小就是截止到user_sp
heap_bottom: user_sp, //
program_brk: user_sp, //
exit_code: 0,
})
}
};
// prepare TrapContext in user space
// 根据 trap_cx_ppn 构建 陷入 trap context 的结构体
let trap_cx = task_control_block.get_trap_cx(); // 根据trap_cx_ppn得到真实的物理页的 trap context的地址
let trap_cx = task_control_block.inner_exclusive_access().get_trap_cx(); // 根据trap_cx_ppn得到真实的物理页的 trap context的地址
*trap_cx = TrapContext::from( // 对 tcb 的 TrapContext 的物理页进行 修改,
entry_point, // 在陷入完成后, 准备返回用户态执行的, 用户代码入口地址
user_sp, // 用户栈, 这个栈的地址是虚拟地址
@ -97,6 +122,17 @@ impl TaskControlBlock {
// base_size 用户栈顶距离0x0的距离, 即用户应用已知的大小
//
}
}
impl TaskControlBlockInner{
// 根据 trap context 的实际的物理地址, 强转为 TrapContext 结构体
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()
}
// 扩张/缩减, 当前应用的地址空间中的堆段, 指定字节的数据
pub fn change_program_brk(&mut self, size: i32) -> Option<usize> {
@ -129,14 +165,3 @@ impl TaskControlBlock {
}
}
}
impl TaskControlBlock {
// 根据 trap context 的实际的物理地址, 强转为 TrapContext 结构体
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()
}
}

@ -8,8 +8,10 @@ pub use context::TrapContext;
use crate::config::{TRAMPOLINE, TRAP_CONTEXT};
use crate::println;
use crate::syscall::syscall;
use crate::task::{exit_current_and_run_next, suspend_current_and_run_next, TASK_MANAGER};
use crate::task::{exit_current_and_run_next, suspend_current_and_run_next};
use crate::timer::set_next_trigger;
use crate::task::manager::TASK_MANAGER;
use crate::task::processor::{current_trap_cx, current_user_token};
@ -35,10 +37,10 @@ pub fn trap_handler() -> ! {
set_kernel_trap_entry();
// 进入到了内核态, 需要把之前的用户消耗时间统计在用户时间上
TASK_MANAGER.kernel_time_start();
// TASK_MANAGER.kernel_time_start();
// 得到当前用户应用 的 trap context, 需要调用一个函数, 因为用户应用的trap context 不再内核空间, 他是在用户空间的次高地址
let cx = TASK_MANAGER.get_current_trap_cx();
let cx = current_trap_cx();
let scause = scause::read(); // trap 发生的原因
let stval = stval::read(); // trap的附加信息
@ -55,12 +57,12 @@ pub fn trap_handler() -> ! {
| Trap::Exception(Exception::LoadFault)
| Trap::Exception(Exception::LoadPageFault) => {
println!("[kernel] PageFault in application, kernel killed it.");
exit_current_and_run_next();
exit_current_and_run_next(-2);
}
// 非法指令
Trap::Exception(Exception::IllegalInstruction) => {
println!("[kernel] IllegalInstruction in application, kernel killed it.");
exit_current_and_run_next();
exit_current_and_run_next(-3);
}
Trap::Interrupt(Interrupt::SupervisorTimer) => {
// 发生时钟中断之后, 继续设置下一个时钟中断的发起时间
@ -78,7 +80,7 @@ pub fn trap_handler() -> ! {
}
}
// 即将进入用户态, 把内核使用的时间统计在内核时间上
TASK_MANAGER.user_time_start();
// TASK_MANAGER.user_time_start();
trap_return()
}
@ -99,7 +101,7 @@ pub fn trap_return() -> ! {
set_user_trap_entry();
let trap_cx_ptr = TRAP_CONTEXT; // 用户空间虚拟地址的次高页, 保存了trap context, 在返回用户空间的时候, 需要恢复trap context中保存的寄存器信息
let user_satp = TASK_MANAGER.get_current_token(); // 当前用户应用的 页表所在的地址 需要在trap.S中, 等下恢复完寄存器之后 修改的用户应用自己的页表
let user_satp = current_user_token(); // 当前用户应用的 页表所在的地址 需要在trap.S中, 等下恢复完寄存器之后 修改的用户应用自己的页表
extern "C" {
fn __alltraps();
fn __restore();

Loading…
Cancel
Save