From f56e7fe50dd269b74d554facf7b3a1f40121065b Mon Sep 17 00:00:00 2001 From: BITcyman <815207911@qq.com> Date: Fri, 18 Aug 2023 16:58:53 +0800 Subject: [PATCH] doc: ipc and task doc --- kernel/src/ipc/futex.rs | 10 +++ kernel/src/ipc/mod.rs | 9 +++ kernel/src/ipc/pipe.rs | 64 ++++++++++++++--- kernel/src/ipc/shm.rs | 30 +++++++- kernel/src/task/context.rs | 8 +++ kernel/src/task/cpu.rs | 29 ++++++-- kernel/src/task/heap.rs | 13 ++++ kernel/src/task/mod.rs | 11 ++- kernel/src/task/schedule.rs | 11 +++ kernel/src/task/stack.rs | 8 ++- kernel/src/task/task.rs | 138 +++++++++++++++++++++++++++++++----- 11 files changed, 293 insertions(+), 38 deletions(-) diff --git a/kernel/src/ipc/futex.rs b/kernel/src/ipc/futex.rs index d939682f..b985c254 100644 --- a/kernel/src/ipc/futex.rs +++ b/kernel/src/ipc/futex.rs @@ -24,17 +24,25 @@ use crate::error::{AlienError, AlienResult}; use crate::task::{Task, TASK_MANAGER}; use crate::timer::read_timer; +/// 用于记录一个进程等待一个 futex 的相关信息 pub struct FutexWaiter { + /// 进程的控制块 task: Option>, + /// 进程等待 futex 的等待时间 wait_time: Option, + /// 超时事件的标志位,标识该进程对于 futex 等待是否超时 timeout_flag: Arc>, } +/// 用于管理 futex 等待队列的数据结构 +/// +/// 包含一个 futex id -> futexWait Vec 的 map pub struct FutexWaitManager { map: BTreeMap>, } impl FutexWaiter { + /// 创建一个新的 `FutexWaiter` 保存等待在某 futex 上的一个进程 有关等待的相关信息 pub fn new(task: Arc, wait_time: Option, timeout_flag: Arc>) -> Self { Self { task: Some(task), @@ -43,12 +51,14 @@ impl FutexWaiter { } } + /// 唤醒该进程,返回该进程的控制块 pub fn wake(&mut self) -> Arc { self.task.take().unwrap() } } impl FutexWaitManager { + /// 创建一个新的 futex 管理器,保存 futex 和在其上等待队列的映射关系 pub fn new() -> Self { Self { map: BTreeMap::new(), diff --git a/kernel/src/ipc/mod.rs b/kernel/src/ipc/mod.rs index 353b2c8b..6cb5d294 100644 --- a/kernel/src/ipc/mod.rs +++ b/kernel/src/ipc/mod.rs @@ -1,3 +1,10 @@ +//! IPC 进程间通信 +//! +//! [`futex`] 子模块指明了 Alien 中的 futex (快速用户空间互斥体)结构。 +//! [`pipe`] 子模块指明了 Alien 中管道结构。 +//! [`shm`] 子模块指明了 Alien 中的共享内存结构。 +//! [`signal`] 子模块指明了 Alien 中使用的信号机制。 + use alloc::sync::Arc; use core::sync::atomic::{AtomicI32, Ordering}; @@ -23,6 +30,7 @@ pub mod shm; pub mod signal; lazy_static! { + /// 一个全局变量,用于记录和管理 futex 的等待队列 pub static ref FUTEX_WAITER: Mutex = Mutex::new(FutexWaitManager::new()); } @@ -265,6 +273,7 @@ pub fn get_robust_list(pid: usize, head_ptr: usize, len_ptr: usize) -> isize { 0 } +/// 唤醒所有当前正在等待 futex 但因为超时或者信号而需要被唤醒的进程 pub fn solve_futex_wait() { let mut futex_waiter = FUTEX_WAITER.lock(); futex_waiter.wake_for_timeout(); diff --git a/kernel/src/ipc/pipe.rs b/kernel/src/ipc/pipe.rs index 303b0a1d..faf160aa 100644 --- a/kernel/src/ipc/pipe.rs +++ b/kernel/src/ipc/pipe.rs @@ -1,3 +1,15 @@ +//! 管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。 +//! +//! `Alien` 中对于管道的设计参考了`rCore`的相关设计。创建管道时会同时创建一个环形缓冲区, +//! 管道的两个端口抽象成文件,对两个端口直接的相关的文件操作(读操作或者写操作)都被设计 +//! 成对缓冲区进行数据处理(向缓冲区中传入数据或接收数据)。 +//! +//! 管道文件创建时,依据 Alien 所使用的 rvfs 中对文件 `File` 的规定,我们只需为管道文件规定好 +//! [`pipe_release`]、[`pipe_write`]、[`pipe_read`]、[`pipe_exec`]、[`pipe_llseek`]、 +//! [`pipe_read_is_hang_up`]、[`pipe_write_is_hang_up`]、[`pipe_ready_to_read`] +//! 、[`pipe_ready_to_write`] 几个操作函数,即可快速的创建管道文件,并将其放入进程的文件描述 +//! 符表中。 + use alloc::boxed::Box; use alloc::sync::{Arc, Weak}; use core::intrinsics::forget; @@ -12,25 +24,27 @@ use crate::config::PIPE_BUF; use crate::fs::file::KFile; use crate::task::{current_task, do_suspend}; -/// 管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。 -/// -/// `Alien` 中对于管道的设计参考了`rCore`的相关设计。创建管道时会同时创建一个环形缓冲区, -/// 管道的两个端口抽象成文件,对两个端口直接的相关的文件操作(读操作或者写操作)都被设计成对缓冲区进行数据处理(向缓冲区中传入数据或接收数据)。 +/// 管道结构 pub struct Pipe; /// 环形缓冲区,用于在内存中维护管道的相关信息。 pub struct RingBuffer { + /// 缓冲区的数据部分 pub buf: [u8; PIPE_BUF], + /// 缓冲区头部,用于指明当前的读位置 pub head: usize, + /// 缓冲区尾部,用于指明当前的写位置 pub tail: usize, + /// 记录 在 读端 进行等待的进程 pub read_wait: Option>, - // record whether there is a process waiting for reading + /// 记录 在 写端 进行等待的进程 pub write_wait: Option>, - // record whether there is a process waiting for writing + /// 引用计数 pub ref_count: usize, } impl RingBuffer { + /// 创建一片新的管道缓冲区,在 `Pipe::new` 中被调用 pub fn new() -> RingBuffer { RingBuffer { buf: [0; PIPE_BUF], @@ -41,13 +55,18 @@ impl RingBuffer { ref_count: 2, } } + + /// 用于返回当前的缓冲区是否为空 pub fn is_empty(&self) -> bool { self.head == self.tail } + + /// 用于返回当前的缓冲区是否为满 pub fn is_full(&self) -> bool { (self.tail + 1) % PIPE_BUF == self.head } - /// return the number of bytes that can be read + + /// 返回当前缓冲区中能够被读的字节数 pub fn available_read(&self) -> usize { if self.head <= self.tail { self.tail - self.head @@ -55,7 +74,8 @@ impl RingBuffer { PIPE_BUF - self.head + self.tail } } - /// return the number of bytes that can be written + + /// 返回当前缓冲区中还能够写入的字节数 pub fn available_write(&self) -> usize { if self.head <= self.tail { PIPE_BUF - self.tail + self.head - 1 @@ -63,6 +83,8 @@ impl RingBuffer { self.head - self.tail - 1 } } + + /// 向缓冲区中写入数据,返回写入的字节数 pub fn write(&mut self, buf: &[u8]) -> usize { let mut count = 0; while !self.is_full() && count < buf.len() { @@ -72,6 +94,8 @@ impl RingBuffer { } count } + + /// 从缓冲区中读取数据,返回读取的字节数 pub fn read(&mut self, buf: &mut [u8]) -> usize { let mut count = 0; while !self.is_empty() && count < buf.len() { @@ -81,14 +105,19 @@ impl RingBuffer { } count } + + /// 清除缓冲区中的内容,当前实现为将 head 和 tail 重置为 0 pub fn clear(&mut self) { self.head = 0; self.tail = 0; } + + /// 返回是否有进程在 写端等待 pub fn is_write_wait(&self) -> bool { self.write_wait.is_some() && self.write_wait.as_ref().unwrap().upgrade().is_some() } + /// 返回是否有进程在 读端等待 pub fn is_read_wait(&self) -> bool { self.read_wait.is_some() && self.read_wait.as_ref().unwrap().upgrade().is_some() } @@ -96,8 +125,9 @@ impl RingBuffer { impl Pipe { /// 创建一个管道,初始化环形缓冲区,返回一对文件,分别对应读端文件和写端文件。 - /// 过程包括创建一个环形缓冲区、创建并初始化写端文件和读端文件, - /// 将两个文件与环形缓冲区相连,使得通过两个端文件快速对缓冲区进行读写。 + /// + /// 过程包括创建一个环形缓冲区、创建并初始化写端文件和读端文件、 + /// 将两个文件与环形缓冲区相连、使得通过两个端文件快速对缓冲区进行读写等。 pub fn new() -> (Arc, Arc) { let mut buf = Box::new(RingBuffer::new()); let mut tx_file = File::new( @@ -168,6 +198,7 @@ impl Pipe { } } +/// 管道文件的写操作,效果等同于 RingBuffer::write fn pipe_write(file: Arc, user_buf: &[u8], _offset: u64) -> StrResult { warn!("pipe_write: {:?}, mode:{:?}", user_buf.len(), file.f_mode); let inode = file.f_dentry.access_inner().d_inode.clone(); @@ -215,6 +246,7 @@ fn pipe_write(file: Arc, user_buf: &[u8], _offset: u64) -> StrResult, user_buf: &mut [u8], _offset: u64) -> StrResult { debug!("pipe_read: {:?}", user_buf.len()); let inode = file.f_dentry.access_inner().d_inode.clone(); @@ -261,6 +293,7 @@ fn pipe_read(file: Arc, user_buf: &mut [u8], _offset: u64) -> StrResult) -> StrResult<()> { warn!("pipe_release: file"); assert_eq!(Arc::strong_count(&file), 1); @@ -286,13 +319,19 @@ fn pipe_release(file: Arc) -> StrResult<()> { Ok(()) } +/// 管道文件 [`pipe_exec`] 操作中 `func` 参数的类型 pub enum PipeFunc { + /// 读就绪操作 AvailableRead, + /// 写就绪操作 AvailableWrite, + /// 某端是否被悬挂操作,当其中的布尔值为 true 时,表示检查的是读端;否则为写端。具体可见 [`pipe_read_is_hang_up`] 和 [`pipe_write_is_hang_up`] Hangup(bool), + /// 未知操作 Unknown, } +/// 管道文件的 exec 操作,用于被其他文件操作函数调用 fn pipe_exec(file: Arc, func: PipeFunc) -> bool { let inode = file.f_dentry.access_inner().d_inode.clone(); let inode_inner = inode.access_inner(); @@ -323,23 +362,28 @@ fn pipe_exec(file: Arc, func: PipeFunc) -> bool { res } +/// 管道文件的读就绪操作,用于检查管道文件是否准备好读,效果等同于 RingBuffer::available_read fn pipe_ready_to_read(file: Arc) -> bool { pipe_exec(file, PipeFunc::AvailableRead) } +/// 管道文件的写就绪操作,用于检查管道文件是否准备好写,效果等同于 RingBuffer::available_write fn pipe_ready_to_write(file: Arc) -> bool { pipe_exec(file, PipeFunc::AvailableWrite) } +/// 管道文件的 "读端是否处于悬挂状态" 操作,用于检查管道文件的写端是否已经被关闭,效果等同于 !RingBuffer::is_write_wait fn pipe_read_is_hang_up(file: Arc) -> bool { let pipe_hang_up = pipe_exec(file, PipeFunc::Hangup(true)); pipe_hang_up } +/// 管道文件的 "写端是否处于悬挂状态" 操作,用于检查管道文件的读端是否已经被关闭,效果等同于 !RingBuffer::is_read_wait fn pipe_write_is_hang_up(file: Arc) -> bool { pipe_exec(file, PipeFunc::Hangup(false)) } +/// (待实现)用于移动管道文件的文件指针,目前仅返回错误 fn pipe_llseek(_file: Arc, _whence: SeekFrom) -> StrResult { Err("ESPIPE") } diff --git a/kernel/src/ipc/shm.rs b/kernel/src/ipc/shm.rs index 069355f9..e8a1815a 100644 --- a/kernel/src/ipc/shm.rs +++ b/kernel/src/ipc/shm.rs @@ -2,10 +2,9 @@ //! 为了在多个进程间交换信息,内核专门留出了一块内存区。这段内存区可以由需要访问的进程将其映射到自己的私有地址空间。 //! 因此,进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高了效率。 //! -//! 共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。 -//! 所以我们通常需要用其他的机制来同步对共享内存的访问,如互斥锁和信号量等。 +//! Alien 中的共享内存同时提供了同步机制,也就是说,所有的 [`ShmMemoryInner`] 结构被包裹在一个 Mutex 中, +//! 最后封装成 [`ShmMemory`] 结构。 //! - use alloc::collections::btree_map::BTreeMap; use alloc::vec::Vec; @@ -21,31 +20,42 @@ use crate::config::FRAME_SIZE; use crate::memory::{frames_alloc, FrameTracker}; use crate::task::current_task; +/// 共享内存被 Mutex 封装后的结构 #[derive(Debug)] pub struct ShmMemory { + /// 封装后的共享内存 inner: Mutex, } +/// 未加入同步机制前的 共享内存 #[derive(Debug)] pub struct ShmMemoryInner { + /// 引用计数器 ref_count: usize, + /// 共享内存数据部分 pub frames: Vec, + /// 共享内存的状态 state: ShmMemoryState, } +/// 共享内存的信息,在创建一块共享内存时,需要将对应的信息加入到进程控制块中的 `shm` 字段下 #[derive(Debug, Clone)] pub struct ShmInfo { + /// 共享内存的虚拟地址首地址 pub start_va: usize, + /// 共享内存的虚拟地址尾地址 pub end_va: usize, } impl ShmInfo { + /// 创建新的共享内存信息 pub fn new(start_va: usize, end_va: usize) -> Self { Self { start_va, end_va } } } impl ShmMemory { + /// 创建新的共享内存 pub fn new(frames: Vec) -> Self { Self { inner: Mutex::new(ShmMemoryInner { @@ -55,31 +65,44 @@ impl ShmMemory { }), } } + + /// 同步获取内部的共享内存信息 pub fn access_inner(&self) -> MutexGuard { self.inner.lock() } + + /// 返回共享内存数据区的长度(字节数) pub fn len(&self) -> usize { self.access_inner().frames.len() * FRAME_SIZE } + /// 引用计数器加一 pub fn add_ref(&self) { self.access_inner().ref_count += 1; } + + /// 获取当前共享内存的引用数 pub fn get_ref(&self) -> usize { self.access_inner().ref_count } + + /// 删除当前的共享内存 pub fn delete(&self) { self.access_inner().state = ShmMemoryState::Deleted; } + + /// 查询当前的共享内存是否被删除 pub fn is_deleted(&self) -> bool { self.access_inner().state == ShmMemoryState::Deleted } + /// 引用计数器减一 pub fn dec_ref(&self) { self.access_inner().ref_count -= 1; } } +/// 记录共享内存当前状态的结构 #[derive(Debug, Eq, PartialEq, Copy, Clone)] pub enum ShmMemoryState { Init, @@ -87,6 +110,7 @@ pub enum ShmMemoryState { Deleted, } +/// 用于记录共享内存分配情况的全局变量,可使用其获取已经被创建的一块共享内存 pub static SHM_MEMORY: Mutex> = Mutex::new(BTreeMap::new()); /// 一个系统调用,用于创建一块共享内存,方便进程间通信。 diff --git a/kernel/src/task/context.rs b/kernel/src/task/context.rs index 56df9f39..812c912f 100644 --- a/kernel/src/task/context.rs +++ b/kernel/src/task/context.rs @@ -1,3 +1,4 @@ +//! 线程切换的上下文结构 use core::arch::global_asm; /// 线程切换需要保存的上下文 @@ -6,15 +7,21 @@ use core::arch::global_asm; #[derive(Debug, Clone)] #[repr(C)] pub struct Context { + /// ra 寄存器 ra: usize, + /// sp 寄存器值 sp: usize, + /// s0 ~ s11 s: [usize; 12], } impl Context { + /// 创建一个新的上下文,默认 s0 ~ s11 的值为 0 pub fn new(ra: usize, sp: usize) -> Self { Self { ra, sp, s: [0; 12] } } + + /// 创建一个全为 0 的上下文 pub const fn empty() -> Self { Self { ra: 0, @@ -30,6 +37,7 @@ extern "C" { pub fn __switch(current_task_cx_ptr: *mut Context, next_task_cx_ptr: *const Context); } +/// 交换前后两个线程的上下文,调用 `switch.asm` 中的 `__switch` pub fn switch(current_task_cx_ptr: *mut Context, next_task_cx_ptr: *const Context) { unsafe { __switch(current_task_cx_ptr, next_task_cx_ptr); diff --git a/kernel/src/task/cpu.rs b/kernel/src/task/cpu.rs index 3774cd5a..8e9ba9dc 100644 --- a/kernel/src/task/cpu.rs +++ b/kernel/src/task/cpu.rs @@ -1,3 +1,4 @@ +//! Alien 中有关进程的系统调用 和 多核的相关支持。 use alloc::collections::VecDeque; use alloc::string::{String, ToString}; use alloc::sync::Arc; @@ -26,17 +27,22 @@ use crate::task::task::{Task, TaskState}; use crate::task::INIT_PROCESS; use crate::trap::{check_task_timer_expired, TrapFrame}; +/// 记录当前 CPU 上正在执行的线程 和 线程上下文 #[derive(Debug, Clone)] pub struct CPU { + /// 正在该 CPU 上运行的线程的控制块 pub task: Option>, + /// 当前线程的上下文 pub context: Context, } +/// 记录一组 CPU 的相关信息 pub struct CpuManager { cpus: Vec, } impl CpuManager { + /// 创建一个 `CpuManager` 结构 pub fn new() -> Self { Self { cpus: vec![CPU::empty(); CPUS], @@ -47,44 +53,55 @@ impl CpuManager { impl Index for CpuManager { type Output = CPU; + /// 用于快捷取出 hartid 为 index 的 CPU 的一个不可变引用 fn index(&self, index: usize) -> &Self::Output { &self.cpus[index] } } impl IndexMut for CpuManager { + /// 用于快捷取出 hartid 为 index 的 CPU 的一个可变引用 fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut self.cpus[index] } } impl CPU { + /// 获取一个空的 CPU const fn empty() -> Self { Self { task: None, context: Context::empty(), } } + + /// 获取 cpu 上的线程任务控制块(会直接获取该任务控制块的所有权) pub fn take_process(&mut self) -> Option> { self.task.take() } + + /// 获取线程上下文的一个 不可变引用 的指针 pub fn get_context_raw_ptr(&self) -> *const Context { &self.context as *const Context } + + /// 获取线程上下文的一个 可变引用 的指针 pub fn get_context_mut_raw_ptr(&mut self) -> *mut Context { &mut self.context as *mut Context } } -/// save info for each cpu +/// 保存每个核的信息 static mut CPU_MANAGER: Once> = Once::new(); -/// the global process pool +/// 全局的线程池 type ProcessPool = VecDeque>; lazy_static! { + /// 管理所有线程的全局变量 pub static ref TASK_MANAGER: Mutex = Mutex::new(ProcessPool::new()); } +/// 初始化 CPU 的相关信息 pub fn init_per_cpu() { unsafe { CPU_MANAGER.call_once(|| CpuManager::new()); @@ -92,7 +109,7 @@ pub fn init_per_cpu() { println!("{} cpus in total", CPU_NUM); } -/// get the current cpu info +/// 获取当前 cpu 的信息 pub fn current_cpu() -> &'static mut CPU { let hart_id = arch::hart_id(); unsafe { @@ -102,19 +119,19 @@ pub fn current_cpu() -> &'static mut CPU { // unsafe { &mut CPU_MANAGER[hart_id] } } -/// get the current_process +/// 获取当前 CPU 上的线程 pub fn current_task() -> Option<&'static Arc> { let cpu = current_cpu(); cpu.task.as_ref() } -/// get the current process's token (root ppn) +/// 获取当前进程的虚拟页表的 token (root ppn) pub fn current_user_token() -> usize { let task = current_task().unwrap(); task.token() } -/// get the current process's trap frame +/// 获取当前进程的 trap 帧(上下文) pub fn current_trap_frame() -> &'static mut TrapFrame { let task = current_task().unwrap(); task.trap_frame() diff --git a/kernel/src/task/heap.rs b/kernel/src/task/heap.rs index 23cf9b04..9243bc09 100644 --- a/kernel/src/task/heap.rs +++ b/kernel/src/task/heap.rs @@ -1,11 +1,18 @@ +//! 记录进程的堆空间的相关信息 + +/// 记录进程的堆空间的相关信息 #[derive(Debug, Clone)] pub struct HeapInfo { + /// 堆使用到的位置 pub current: usize, + /// 堆空间的起始位置 pub start: usize, + /// 堆空间的末尾位置 pub end: usize, } impl HeapInfo { + /// 新建一个 HeapInfo pub fn new(start: usize, end: usize) -> Self { HeapInfo { current: start, @@ -14,29 +21,35 @@ impl HeapInfo { } } + /// 返回堆的大小 #[allow(unused)] pub fn size(&self) -> usize { self.end - self.start } + /// 返回堆是否包括某地址 #[allow(unused)] pub fn contains(&self, addr: usize) -> bool { addr >= self.start && addr < self.end } + /// 堆大小增加 size 个单位 pub fn increase(&mut self, size: usize) { self.end += size; } + /// 重新设置堆空间的头 #[allow(unused)] pub fn set_start(&mut self, start: usize) { self.start = start; } + /// 重新设置堆空间的尾 pub fn set_end(&mut self, end: usize) { self.end = end; } + /// 返回堆空间是否为空 #[allow(unused)] pub fn is_empty(&self) -> bool { self.start == self.end diff --git a/kernel/src/task/mod.rs b/kernel/src/task/mod.rs index d1913abb..1a0d4e1e 100644 --- a/kernel/src/task/mod.rs +++ b/kernel/src/task/mod.rs @@ -1,3 +1,11 @@ +//! Alien 中有关进程管理的相关数据结构 +//! +//! [`context`] 子模块定义了 Alien 中线程上下文的相关结构. +//! [`cpu`] 子模块中指明了 Alien 中有关进程的系统调用 和 多核的相关支持。 +//! [`heap`] 子模块定义了 Alien 记录进程堆空间的相关信息的结构。 +//! [`schedule`] 子模块指明了 Alien 中有关 CPU 调度的相关机制 +//! [`stack`] 子模块定义了 Alien 中有关内核栈的相关结构。 +//! [`task`] 子模块定义了 Alien 中有关进程控制块的定义。 use alloc::sync::Arc; use alloc::vec::Vec; @@ -17,6 +25,7 @@ pub mod schedule; mod stack; mod task; +/// 初始进程(0号进程) pub static INIT_PROCESS: Lazy> = Lazy::new(|| { let mut data = Vec::new(); vfs::read_all("/bin/init", &mut data); @@ -25,7 +34,7 @@ pub static INIT_PROCESS: Lazy> = Lazy::new(|| { Arc::new(task) }); -/// put init process into process pool +/// 将初始进程加入进程池中进行调度 pub fn init_process() { let mut task_pool = TASK_MANAGER.lock(); let task = INIT_PROCESS.clone(); diff --git a/kernel/src/task/schedule.rs b/kernel/src/task/schedule.rs index 5ab3c8f9..8c0cda5b 100644 --- a/kernel/src/task/schedule.rs +++ b/kernel/src/task/schedule.rs @@ -1,3 +1,4 @@ +//! CPU 调度 use core::arch::asm; use syscall_define::signal::SignalNumber; @@ -8,6 +9,15 @@ use crate::task::cpu::{current_cpu, TASK_MANAGER}; use crate::task::task::TaskState; use crate::trap::check_timer_interrupt_pending; +/// 在 CPU 启动并初始化完毕后初次进入用户态时,或者在一个任务将要让渡 CPU 时 将会执行该函数。 +/// +/// 如果当前 CPU 上有任务正在执行,那么将根据该任务当前的状态进行操作。 +/// - 如果该任务处于睡眠或等待状态,将会把其任务的控制块取出丢弃掉。 +/// - 如果该任务处于僵尸状态,将会向其父进程发送信号,令其回收该任务的控制块。 +/// - 如果该任务处于其他状态,我们将其放入线程池中等待下一次分配。 +/// +/// 之后如果在线程池中有任务需要调度,那么就把该任务的上下文切换到 CPU 上来运行; +/// 否则该 CPU 将进入等待状态,等待其它核的中断信号。 #[no_mangle] pub fn first_into_user() -> ! { loop { @@ -63,6 +73,7 @@ pub fn first_into_user() -> ! { } } +/// 切换线程上下文,调度当前在 CPU 上执行的线程 让渡出 CPU pub fn schedule() { check_timer_interrupt_pending(); let cpu = current_cpu(); diff --git a/kernel/src/task/stack.rs b/kernel/src/task/stack.rs index 8e312c0c..5799b222 100644 --- a/kernel/src/task/stack.rs +++ b/kernel/src/task/stack.rs @@ -1,3 +1,4 @@ +//! 进程内核栈空间 use alloc::vec::Vec; use kernel_sync::Mutex; @@ -5,12 +6,15 @@ use kernel_sync::Mutex; use crate::config::FRAME_BITS; use crate::memory::{frame_alloc_contiguous, FrameTracker}; +/// 记录进程内核栈空间 #[derive(Debug)] pub struct Stack { + /// 栈帧 frames: Mutex>, } impl Stack { + /// 通过帧的个数创建一块新的 内核栈 pub fn new(pages: usize) -> Option { let frames = frame_alloc_contiguous(pages) as usize >> FRAME_BITS; let frames = (0..pages) @@ -21,12 +25,14 @@ impl Stack { frames: Mutex::new(frames), }) } - /// get the stack top + + /// 获取帧顶指针 pub fn top(&self) -> usize { let first = self.frames.lock().last().map(|frame| frame.end()); first.unwrap() } + /// 回收内核栈空间。 pub fn release(&self) { let mut frames = self.frames.lock(); *frames = Vec::new(); diff --git a/kernel/src/task/task.rs b/kernel/src/task/task.rs index da7ebd75..933edd51 100644 --- a/kernel/src/task/task.rs +++ b/kernel/src/task/task.rs @@ -1,3 +1,8 @@ +//! Alien 中任务控制块的相关定义。 +//! +//! Alien 中对于进程\线程的相关概念的设计与 Linux 类似,进程和线程共用一个控制块结构。 +//! 使用 `clone` 创建新的进程(线程)时,会根据 flag 指明父子进程之间资源共享的程度。 +//! tid 是标识不同任务的唯一标识。 use alloc::collections::BTreeMap; use alloc::string::{String, ToString}; use alloc::sync::{Arc, Weak}; @@ -55,10 +60,13 @@ lazy_static! { /// 但tid的分配并不需要实际存储信息,因此可以插入任意的数据,这里为了节省空间,将数据定义为u8 pub static ref TID_MANAGER:Mutex> = Mutex::new(MinimalManager::new(MAX_THREAD_NUM)); } + +/// 用于存储线程的tid #[derive(Debug)] pub struct TidHandle(pub usize); impl TidHandle { + /// 获取一个新的线程 tid (来自于 `TID_MANAGER` 分配) pub fn new() -> Option { let tid = TID_MANAGER.lock().insert(0); if tid.is_err() { @@ -76,32 +84,51 @@ impl Drop for TidHandle { #[derive(Debug)] pub struct Task { + /// 任务的唯一标识 pub tid: TidHandle, + /// 作为进程时,pid == tid;作为线程时,pid 为其线程组 leader (父进程)的 tid 号。 pub pid: usize, /// 当退出时是否向父进程发送信号 SIGCHLD。 /// 如果创建时带 CLONE_THREAD 选项,则不发送信号,除非它是线程组(即拥有相同pid的所有线程)中最后一个退出的线程; /// 否则发送信号 pub send_sigchld_when_exit: bool, + /// 内核栈 pub kernel_stack: Stack, + /// 更详细的信息 inner: Mutex, } #[derive(Debug)] pub struct TaskInner { + /// 任务名,一般为其文件路径加文件名 pub name: String, + /// 线程计数器,用于分配同一个线程组中的线程序号 pub threads: MinimalManager<()>, + /// 用于记录当前线程组中的线程个数 pub thread_number: usize, + /// 地址空间 pub address_space: Arc>>, + /// 线程状态 pub state: TaskState, + /// 父亲任务控制块 pub parent: Option>, + /// 孩子任务控制块的集合 pub children: Vec>, + /// 文件描述符表 pub fd_table: Arc>, + /// 任务上下文 pub context: Context, + /// 文件系统的信息 pub fs_info: FsContext, + /// 有关任务执行情况的统计信息 pub statistical_data: StatisticalData, + /// 任务计时器 pub timer: TaskTimer, + /// 返回值 pub exit_code: i32, + /// 堆空间 pub heap: Arc>, + /// 地址空间中的映射信息 pub mmap: MMapInfo, /// 信号量对应的一组处理函数。 /// 因为发送信号是通过 pid/tid 查找的,因此放在 inner 中一起调用时更容易导致死锁 @@ -120,12 +147,17 @@ pub struct TaskInner { /// 此时用户可能修改其中的 pc 信息(如musl-libc 的 pthread_cancel 函数)。 /// 在这种情况下,需要手动在 sigreturn 时更新已保存的上下文信息 pub signal_set_siginfo: bool, + /// robust 锁的列表 pub robust: RobustList, + /// 共享内存 pub shm: BTreeMap, + /// cpu 亲和力,用于 cpu 调度时 倾向于将该任务调度给 哪个 CPU pub cpu_affinity: usize, - /// process's file mode creation mask + /// 进程创建文件时,文件权限的默认掩码 pub unmask: usize, + /// 栈空间的信息 pub stack: Range, + /// 是否需要等待 pub need_wait: u8, } @@ -142,15 +174,18 @@ pub struct TaskTimer { /// /// 根据 timer_type 的规则不断减少,当归零时触发信号 pub timer_remained: usize, - + /// 上一次计时的开始时间 pub start: usize, + /// 该计时器是否已经超时 pub expired: bool, } impl TaskTimer { + /// 创建一个新的任务计数器 pub fn new() -> Self { Self::default() } + /// 清除当前的计数器信息,将 timer_remained 置为 0 pub fn clear(&mut self) { self.timer_type = TimerType::NONE; self.timer_interval = TimeVal::new(); @@ -160,6 +195,7 @@ impl TaskTimer { } impl Default for TaskTimer { + /// 默认的任务计数器 fn default() -> Self { Self { timer_type: TimerType::NONE, @@ -188,6 +224,7 @@ pub struct StatisticalData { } impl StatisticalData { + /// 用于创建一个新的 `StatisticalData` 结构 pub fn new() -> Self { let now = read_timer(); StatisticalData { @@ -199,6 +236,7 @@ impl StatisticalData { tms_cstime: 0, } } + /// 清除当前 `StatisticalData` 结构中储存的数据,并将 `last_utime` 和 `last_stime` 的值置为 当前的时间 pub fn clear(&mut self) { let now = read_timer(); self.tms_utime = 0; @@ -223,6 +261,7 @@ pub struct FsContext { } impl FsContext { + /// 返回一个空的 `FsContext` 结构 pub fn empty() -> Self { FsContext { cwd: Arc::new(DirEntry::empty()), @@ -232,6 +271,7 @@ impl FsContext { } } + /// 创建一个新的 `FsContext` 结构 pub fn new( root: Arc, cwd: Arc, @@ -260,19 +300,22 @@ impl Into for FsContext { #[derive(Debug, Copy, Clone, PartialOrd, PartialEq)] pub enum TaskState { + /// 就绪态 Ready, + /// 运行态 Running, - // waiting for some time + /// 等待一段时间 Sleeping, - // waiting some event + /// 等待一个事件 Waiting, - // waiting for parent to reap + /// 僵尸态,等待父进程回收资源 Zombie, - // terminated + /// 终止态 Terminated, } impl Task { + /// 终止进程,回收内核栈等资源,同时将该任务的状态修改为 `Terminated` pub fn terminate(self: Arc) { // recycle kernel stack self.kernel_stack.release(); @@ -294,64 +337,83 @@ impl Task { self.access_inner().state = TaskState::Terminated; } + /// 获取进程的 pid 号 #[inline] pub fn get_pid(&self) -> isize { self.pid as isize } + + /// 获取进程的 tid 号 #[inline] pub fn get_tid(&self) -> isize { self.tid.0 as isize } + /// 设置 `clear_child_tid` 字段的 值 pub fn set_tid_address(&self, tidptr: usize) { let mut inner = self.inner.lock(); inner.clear_child_tid = tidptr; } + + /// 获取文件的名称 pub fn get_name(&self) -> String { let inner = self.inner.lock(); inner.name.clone() } + + /// 尝试获取 TaskInner pub fn access_inner(&self) -> MutexGuard { self.inner.lock() } + + /// 获取进程页表的root ppn pub fn token(&self) -> usize { let inner = self.inner.lock(); let paddr = inner.address_space.lock().root_paddr(); (8usize << 60) | (paddr.as_usize() >> 12) } + /// 获取 trap 帧的可变引用 pub fn trap_frame(&self) -> &'static mut TrapFrame { self.inner.lock().trap_frame() } + /// 获取 trap 帧的指针 pub fn trap_frame_ptr(&self) -> *mut TrapFrame { self.inner.lock().trap_frame_ptr() } + /// 将进程的状态修改为 state pub fn update_state(&self, state: TaskState) { let mut inner = self.inner.lock(); inner.state = state; } + /// 返回进程的状态 pub fn state(&self) -> TaskState { let inner = self.inner.lock(); inner.state } + /// 更新进程的返回码 pub fn update_exit_code(&self, code: i32) { let mut inner = self.inner.lock(); inner.exit_code = code; } + /// 获取任务上下文的指针 pub fn get_context_raw_ptr(&self) -> *const Context { let inner = self.inner.lock(); &inner.context as *const Context } + + /// 获取任务上下文的可变指针 pub fn get_context_mut_raw_ptr(&self) -> *mut Context { let mut inner = self.inner.lock(); &mut inner.context as *mut Context } + /// 获取该进程的子进程的控制块列表 pub fn children(&self) -> Vec> { let inner = self.inner.lock(); inner.children.clone() @@ -372,12 +434,14 @@ impl Task { res } + /// 取走当前进程的子进程控制块列表的所有权 pub fn take_children(&self) -> Vec> { let children = self.children(); self.access_inner().children = Vec::new(); children } + /// 将任务号为 `tid` 的进程 从子进程列表中移除 pub fn remove_child_by_tid(&self, tid: isize) -> Option> { let mut inner = self.inner.lock(); let index = inner @@ -391,27 +455,32 @@ impl Task { } } + /// 将子进程列表中第 `index` 个进程 从子进程列表中移除 pub fn remove_child(&self, index: usize) -> Arc { let mut inner = self.inner.lock(); assert!(index < inner.children.len()); inner.children.remove(index) } + /// 将进程的父进程更新为 `parent` pub fn update_parent(&self, parent: Arc) { let mut inner = self.inner.lock(); inner.parent = Some(Arc::downgrade(&parent)); } + /// 向子进程列表中插入一个新的子进程 pub fn insert_child(&self, child: Arc) { let mut inner = self.inner.lock(); inner.children.push(child); } + /// 获取进程当前的返回码 pub fn exit_code(&self) -> i32 { let inner = self.inner.lock(); inner.exit_code } + /// 用于判断一个文件是否存在在该进程的文件描述符表中,如果存在返回该文件描述符 pub fn file_existed(&self, file: Arc) -> Option> { let inner = self.inner.lock(); let fd_table = inner.fd_table.lock(); @@ -429,11 +498,15 @@ impl Task { } }) } + + /// 用于获取文件描述符id号为 fd 的 文件描述符 pub fn get_file(&self, fd: usize) -> Option> { let inner = self.inner.lock(); let file = inner.fd_table.lock().get(fd); return if file.is_err() { None } else { file.unwrap() }; } + + /// 在进程的文件描述符表中加入 file 文件 pub fn add_file(&self, file: Arc) -> Result { self.access_inner() .fd_table @@ -441,12 +514,15 @@ impl Task { .insert(file) .map_err(|x| x as isize) } + + /// 指定文件描述符表中的一个id,在该处加入一个 file 文件 pub fn add_file_with_fd(&self, file: Arc, fd: usize) -> Result<(), ()> { let inner = self.access_inner(); let mut fd_table = inner.fd_table.lock(); fd_table.insert_with_index(fd, file).map_err(|_| {}) } + /// 指明文件描述符表中的一个id,删除并返回该处的 file 文件 pub fn remove_file(&self, fd: usize) -> Result, ()> { let inner = self.inner.lock(); let file = inner.fd_table.lock().get(fd); @@ -462,10 +538,12 @@ impl Task { Ok(file) } + /// 获取一个虚拟地址 `ptr` 的实际物理地址 pub fn transfer_raw(&self, ptr: usize) -> usize { self.access_inner().transfer_raw(ptr) } + /// 获取一个虚拟地址 `ptr` 对应的 T 类型数据 的 可变引用 pub fn transfer_raw_ptr(&self, ptr: *mut T) -> &'static mut T { self.access_inner().transfer_raw_ptr_mut(ptr) } @@ -531,13 +609,17 @@ impl Task { } impl TaskInner { + /// 获取进程的文件系统信息 pub fn cwd(&self) -> FsContext { self.fs_info.clone() } + /// 获取进程的计时器 pub fn get_timer(&self) -> TaskTimer { self.timer.clone() } + + /// 获取当前进程对于资源的限制 pub fn get_prlimit(&self, resource: PrLimitRes) -> PrLimit { match resource { PrLimitRes::RlimitStack => PrLimit::new(USER_STACK_SIZE as u64, USER_STACK_SIZE as u64), @@ -549,6 +631,7 @@ impl TaskInner { } } + /// 设置当前进程对于资源的限制 pub fn set_prlimit(&mut self, resource: PrLimitRes, value: PrLimit) { match resource { PrLimitRes::RlimitStack => {} @@ -560,6 +643,7 @@ impl TaskInner { } } + /// 返回 trap 上下文的一个可变指针 pub fn trap_frame_ptr(&self) -> *mut TrapFrame { let trap_context_base = if self.thread_number != 0 { let base = TRAP_CONTEXT_BASE - self.thread_number * FRAME_SIZE; @@ -570,6 +654,7 @@ impl TaskInner { trap_context_base as *mut TrapFrame } + /// 返回 trap 上下文的一个可变引用 pub fn trap_frame(&self) -> &'static mut TrapFrame { let trap_context_base = if self.thread_number != 0 { let base = TRAP_CONTEXT_BASE - self.thread_number * FRAME_SIZE; @@ -618,6 +703,8 @@ impl TaskInner { -1 } } + + /// 获取一个虚拟地址 `ptr` 的实际物理地址 pub fn transfer_raw(&mut self, ptr: usize) -> usize { let (phy, flag, _) = self .address_space @@ -637,6 +724,8 @@ impl TaskInner { } phy.as_usize() } + + /// 获取 虚拟地址空间中的以 `ptr` 为起始地址,以 '\0' 结尾的字符串 pub fn transfer_str(&self, ptr: *const u8) -> String { let mut res = String::new(); let mut start = ptr as usize; @@ -657,6 +746,7 @@ impl TaskInner { res } + /// 将虚拟地址空间中的一段缓冲区的首地址 `ptr` 和 缓冲区的长度 `len` 转换为 实地址下的一组页 pub fn transfer_raw_buffer(&self, ptr: *const u8, len: usize) -> Vec<&'static mut [u8]> { let address_space = &self.address_space.lock(); let mut start = ptr as usize; @@ -681,6 +771,7 @@ impl TaskInner { v } + /// 从物理地址的 `src` 处取一个长度为 `len` 类型为 T 的缓冲区 赋到 用户虚拟地址空间下的 `dst` 处 pub fn copy_to_user_buffer( &mut self, src: *const T, @@ -719,6 +810,7 @@ impl TaskInner { } } + /// 从用户虚拟地址空间的 `src` 处取一个长度为 `len` 类型为 T 的缓冲区 赋到 物理地址下的 `dst` 处 pub fn copy_from_user_buffer( &mut self, src: *const T, @@ -757,7 +849,7 @@ impl TaskInner { } } - /// src is in kernel memory, we don't need trans + /// 从物理空间下的 `src` 处取一个 T 类型的数据 赋给 虚拟地址空间下的 `dst` 处 pub fn copy_to_user(&mut self, src: *const T, dst: *mut T) { // self.copy_to_user_buffer(src, dst, 1); let size = core::mem::size_of::(); @@ -792,6 +884,7 @@ impl TaskInner { } } + /// 从用户虚拟地址空间的 `src` 处取一个 T 类型的数据 赋给 物理地址下的 `dst` 处 pub fn copy_from_user(&mut self, src: *const T, dst: *mut T) { // self.copy_from_user_buffer(src, dst, 1); let size = core::mem::size_of::(); @@ -826,6 +919,7 @@ impl TaskInner { } } + /// 将在进程的虚拟空间中的一段缓冲区的首地址 `ptr` 和 长度 `len` 转换为 实地址下的一组页 pub fn transfer_buffer( &mut self, ptr: *const T, @@ -859,6 +953,7 @@ impl TaskInner { v } + /// 将一个在进程的虚拟空间中的虚地址 转换为一个实地址的可变引用 pub fn transfer_raw_ptr_mut(&self, ptr: *mut T) -> &'static mut T { let (physical, flag, _) = self .address_space @@ -869,6 +964,7 @@ impl TaskInner { unsafe { &mut *(physical.as_usize() as *mut T) } } + /// 将一个在进程的虚拟空间中的虚地址 转换为一个实地址的不可变引用 pub fn transfer_raw_ptr(&self, ptr: *const T) -> &'static T { let (physical, flag, _) = self .address_space @@ -879,7 +975,7 @@ impl TaskInner { unsafe { &*(physical.as_usize() as *const T) } } - /// When process return to user mode, we need to update the user mode time + /// 当进程回到用户态时,需要更新进程在内核态下的运行时间 /// WARNING: If the cause of the process returning to the kernel is a timer interrupt, /// We should not call this function. pub fn update_kernel_mode_time(&mut self) { @@ -890,7 +986,7 @@ impl TaskInner { self.statistical_data.last_utime = now; } - /// When process return to kernel mode, we need to update the user Mode Time + /// 当进程进入内核态时,需要更新进程在用户态下的运行时间 pub fn update_user_mode_time(&mut self) { let now = read_timer(); // current cpu clocks let time = now - self.statistical_data.last_utime; @@ -899,7 +995,7 @@ impl TaskInner { self.statistical_data.last_stime = now; } - /// 设置计时器。 + /// 设置计时器 pub fn set_timer(&mut self, itimer: ITimerVal, timer_type: TimerType) { self.timer.timer_remained = itimer.it_value.to_clock(); self.timer.timer_interval = itimer.it_interval; @@ -907,7 +1003,8 @@ impl TaskInner { self.timer.start = TimeVal::now().to_clock(); } - /// 更新计时器。 + /// 更新计时器 + /// /// 如果没有计时器则直接返回;如果有计时器但时辰未到也直接返回; /// 如果有计时器且计时器到时间了,根据是否为one-shot计时器,确定重置计时器或者置`timer`的`timer_remained`为0。 pub fn update_timer(&mut self) { @@ -932,9 +1029,7 @@ impl TaskInner { self.timer.expired = true; } - /// After call `update_user_mode_time` and `update_kernel_mode_time`, we - /// need to check if the timer is expired - #[must_use] + /// 在调用 `update_user_mode_time` 和 `update_kernel_mode_time` 后,我们需要检查一下计时器是否已经超时 pub fn check_timer_expired(&mut self) -> Option { if self.timer.expired { self.timer.expired = false; @@ -944,19 +1039,22 @@ impl TaskInner { } } + /// 返回进程的统计信息 pub fn statistical_data(&self) -> &StatisticalData { &self.statistical_data } + /// 返回堆信息 pub fn heap_info(&self) -> HeapInfo { self.heap.lock().clone() } + /// (待实现)缩减堆空间 pub fn shrink_heap(_addr: usize) -> Result { todo!() } - /// extend heap + /// 拓展堆空间 pub fn extend_heap(&mut self, addr: usize) -> Result { let mut heap = self.heap.lock(); heap.current = addr; @@ -1159,6 +1257,7 @@ impl TaskInner { Ok(()) } + /// 用于处理装入页异常 pub fn do_load_page_fault( &mut self, addr: usize, @@ -1208,6 +1307,7 @@ impl TaskInner { Ok(Some((file.clone(), buf, read_offset as u64))) } + /// 用于处理无效页错误 fn invalid_page_solver( &mut self, addr: usize, @@ -1265,6 +1365,7 @@ impl TaskInner { Ok(None) } + /// 用于处理指令页异常 pub fn do_instruction_page_fault( &mut self, addr: usize, @@ -1287,6 +1388,8 @@ impl TaskInner { } panic!("instruction page fault"); } + + /// 用于处理数据页异常 pub fn do_store_page_fault( &mut self, o_addr: usize, @@ -1349,6 +1452,7 @@ impl TaskInner { } impl Task { + /// 对进程的资源进行预回收,将会回收 trap 帧、子进程控制块列表、文件描述符表等资源。 pub fn pre_recycle(&self) { // recycle trap page let trap_frame_ptr = self.trap_frame_ptr() as usize; @@ -1383,12 +1487,12 @@ impl Task { } } - /// get the clear_child_tid + /// 获取进程的 `clear_child_tid` 字段 pub fn futex_wake(&self) -> usize { self.access_inner().clear_child_tid } - /// only call once + /// 从 elf 文件中创建一个新的进程控制块,只会调用一次(即读取 init 进程的相关信息) pub fn from_elf(name: &str, elf: &[u8]) -> Option { let tid = TidHandle::new()?; let pid = tid.0;