diff --git a/document/Context.md b/document/Context.md
new file mode 100644
index 00000000..5b9f0771
--- /dev/null
+++ b/document/Context.md
@@ -0,0 +1,55 @@
+# 执行上下文与切换机制
+
+本文档定义了程序执行上下文(Execution Context)的构成及其在执行流切换(Context Switch)时的操作流程。该机制广泛应用于多任务操作系统中,用于任务调度和资源管理。
+
+## 1. 执行上下文(Execution Context)的构成
+
+执行上下文是 CPU 恢复一个任务(进程或线程)执行所需的所有状态信息的集合。它主要由以下几个部分组成:
+
+| 要素 | 核心内容 | 存储位置/管理机制 | 切换需求 |
+|----------------|---------------------------------------------|---------------------------------|----------------|
+| **CPU 状态** | 包括通用寄存器、程序计数器、标志寄存器等 | 当前任务的内存区域中 | 每次切换时必需 |
+| **地址空间** | 虚拟地址与物理地址的映射关系 | 特定的地址映射表 | 进程切换时必需 |
+| **系统资源** | 文件描述符、网络连接等资源的引用 | 任务控制块(task control block) | 自动传递 |
+
+### 解释
+- **CPU 状态**:包括 CPU 寄存器的内容(如程序计数器、栈指针等),这些内容描述了任务在 CPU 执行期间的当前状态。
+- **地址空间**:指任务的内存布局,包括虚拟地址到物理地址的映射。每个任务的地址空间独立,任务切换时需要切换到新任务的地址空间。
+- **系统资源**:包括任务持有的资源,如文件、网络连接等。通常由任务控制块管理,切换时资源状态会自动继承。
+
+## 2. 核心数据结构
+
+在任务管理过程中,以下数据结构用于存储任务的状态和相关信息:
+
+| 结构体/寄存器 | 作用 | 存储内容 | 所属模块 |
+|------------------|--------------------------------|----------------------------------|-----------------|
+| **任务控制块 (task control block)** | 管理任务状态、资源、调度信息 | 存储任务的所有信息,包括系统资源、CPU 状态等 | 任务管理 |
+| **内存栈** | 任务的独立执行栈 | 存储上下文信息、局部变量等 | 内存管理 |
+| **当前任务指针** | 标识当前执行的任务 | 指向当前任务的任务控制块 | 调度管理 |
+| **地址映射信息** | 管理任务的内存映射信息 | 存储任务地址空间的映射信息 | 内存管理 |
+
+### 解释
+- **任务控制块 (task control block)**:每个任务都有一个独立的控制块,存储任务的所有状态信息和资源管理信息。
+- **内存栈**:任务的执行栈,保存该任务的局部变量和中间计算结果。
+- **当前任务指针**:全局指针,始终指向当前正在执行的任务,确保操作系统知道当前哪个任务在 CPU 上执行。
+
+## 3. 执行流切换(Context Switch)的关键操作
+
+当操作系统决定切换当前任务(任务 A)到另一个任务(任务 B)时,执行流切换涉及以下几个关键操作:
+
+| 步骤 | 操作内容 | 触发条件 | 模块职责 |
+|------------------|----------------------------------------------------------------------------|--------------------|--------------------|
+| **步骤 1**: 保存与恢复状态 | 1. 将任务 A 的 CPU 寄存器和状态保存到任务 A 的内存区域中。
2. 从任务 B 的内存区域恢复 CPU 寄存器和状态。 | 每次任务切换时 | 调度管理/CPU核心 |
+| **步骤 2**: 切换地址空间 | 更新任务的内存映射信息,确保任务 B 能访问到其专有的内存空间。 | 进程切换时 | 内存管理 |
+| **步骤 3**: 更新当前任务 | 更新当前任务指针,指向任务 B 的控制块,确保调度器知道当前正在执行的是任务 B。 | 每次任务切换时 | 调度管理 |
+
+### 解释
+- **步骤 1:保存与恢复状态**:保存当前任务的状态(如寄存器的值),然后恢复新任务的状态。这确保了每个任务在执行流切换后能从它离开时的状态继续运行。
+- **步骤 2:切换地址空间**:任务的地址空间必须被切换,以便确保每个任务访问的是它自己的内存区域。通常通过更新地址映射信息来完成这一操作。
+- **步骤 3:更新当前任务**:调度器需要更新当前任务指针,指向新任务的控制块,确保下一次调度时可以恢复正确的任务。
+
+---
+
+### 总结
+
+执行上下文切换是多任务操作系统中的关键机制,它确保操作系统能够在多个任务之间切换,保持各任务的独立性和执行状态。通过管理任务的 CPU 状态、地址空间和系统资源,操作系统能够高效地实现任务调度、资源分配与切换。本流程涉及的关键操作包括保存当前任务的状态、切换内存空间和更新任务指针,确保每个任务的状态能够正确恢复。
diff --git a/os/src/arch/riscv/mm/page_table.rs b/os/src/arch/riscv/mm/page_table.rs
index 447d3a48..f77cbf60 100644
--- a/os/src/arch/riscv/mm/page_table.rs
+++ b/os/src/arch/riscv/mm/page_table.rs
@@ -1,8 +1,11 @@
use super::PageTableEntry;
-use crate::mm::address::{AlignOps, PageNum, Ppn, PpnRange, UsizeConvert, Vaddr, Vpn, VpnRange, Paddr, ConvertablePaddr};
+use crate::mm::address::{
+ AlignOps, ConvertablePaddr, Paddr, PageNum, Ppn, PpnRange, UsizeConvert, Vaddr, Vpn, VpnRange,
+};
use crate::mm::frame_allocator::FrameTracker;
use crate::mm::page_table::{
- PageSize, PageTableInner as PageTableInnerTrait, PagingError, PagingResult, UniversalPTEFlag, PageTableEntry as PageTableEntryTrait
+ PageSize, PageTableEntry as PageTableEntryTrait, PageTableInner as PageTableInnerTrait,
+ PagingError, PagingResult, UniversalPTEFlag,
};
use alloc::vec::Vec;
@@ -158,9 +161,7 @@ impl PageTableInnerTrait for PageTableInner {
) -> PagingResult<()> {
// Validate flags: leaf pages must have at least one of R/W/X set
if !flags.intersects(
- UniversalPTEFlag::Readable
- | UniversalPTEFlag::Writeable
- | UniversalPTEFlag::Executable,
+ UniversalPTEFlag::Readable | UniversalPTEFlag::Writeable | UniversalPTEFlag::Executable,
) {
return Err(PagingError::InvalidFlags);
}
diff --git a/os/src/arch/riscv/mm/page_table_entry.rs b/os/src/arch/riscv/mm/page_table_entry.rs
index 892e2699..e8876ad0 100644
--- a/os/src/arch/riscv/mm/page_table_entry.rs
+++ b/os/src/arch/riscv/mm/page_table_entry.rs
@@ -16,8 +16,6 @@ bitflags::bitflags! {
}
}
-
-
/*
* SV39 Page Table Entry (PTE) format:
* ------------------------------------------------
@@ -35,7 +33,7 @@ bitflags::bitflags! {
*/
const SV39_PTE_FLAG_MASK: usize = 0xff; // Lower 8 bits for SV39 PTE flags
-const SV39_PTE_PPN_OFFSET: usize = 10; // PPN starts from bit 10
+const SV39_PTE_PPN_OFFSET: usize = 10; // PPN starts from bit 10
const SV39_PTE_PPN_MASK: u64 = 0x000f_ffff_ffff_c00; // Bits 10-53 for PPN
impl UniversalConvertableFlag for SV39PTEFlags {
@@ -84,8 +82,12 @@ impl PageTableEntryTrait for PageTableEntry {
fn is_huge(&self) -> bool {
// In SV39, we can't directly determine huge pages from the PTE alone.
- let sv39_flags = SV39PTEFlags::from_bits((self.0 & SV39_PTE_FLAG_MASK as u64) as usize).unwrap();
- sv39_flags.intersects(SV39PTEFlags::union(SV39PTEFlags::READ, SV39PTEFlags::EXECUTE).union(SV39PTEFlags::WRITE))
+ let sv39_flags =
+ SV39PTEFlags::from_bits((self.0 & SV39_PTE_FLAG_MASK as u64) as usize).unwrap();
+ sv39_flags.intersects(
+ SV39PTEFlags::union(SV39PTEFlags::READ, SV39PTEFlags::EXECUTE)
+ .union(SV39PTEFlags::WRITE),
+ )
}
fn is_empty(&self) -> bool {
@@ -98,7 +100,8 @@ impl PageTableEntryTrait for PageTableEntry {
}
fn flags(&self) -> UniversalPTEFlag {
- let sv39_flags = SV39PTEFlags::from_bits((self.0 & SV39_PTE_FLAG_MASK as u64) as usize).unwrap();
+ let sv39_flags =
+ SV39PTEFlags::from_bits((self.0 & SV39_PTE_FLAG_MASK as u64) as usize).unwrap();
sv39_flags.to_universal()
}
diff --git a/os/src/kernel/cpu.rs b/os/src/kernel/cpu.rs
index aa1fbba9..825a8b73 100644
--- a/os/src/kernel/cpu.rs
+++ b/os/src/kernel/cpu.rs
@@ -1,4 +1,6 @@
-use crate::{arch::kernel::context::Context, kernel::task::Task};
+use alloc::sync::Arc;
+
+use crate::{arch::kernel::context::Context, kernel::task::Task, mm::memory_space::MemorySpace};
/// CPU 结构体
pub struct Cpu {
@@ -6,7 +8,9 @@ pub struct Cpu {
/// 用于在调度器中保存和恢复 CPU 寄存器状态
pub context: Context,
/// 当前运行的任务
- pub current_task: Option,
+ pub current_task: Option>,
+ /// 当前内存空间
+ pub cur_memory_space: Option>,
}
impl Cpu {
@@ -14,6 +18,7 @@ impl Cpu {
Cpu {
context: Context::zero_init(),
current_task: None,
+ cur_memory_space: None,
}
}
}
diff --git a/os/src/kernel/task.rs b/os/src/kernel/task.rs
index fe943daa..a3252d34 100644
--- a/os/src/kernel/task.rs
+++ b/os/src/kernel/task.rs
@@ -2,7 +2,12 @@
use core::sync::atomic::AtomicPtr;
-use crate::arch::{kernel::context::Context, trap::kerneltrap::TrapFrame};
+use alloc::sync::Arc;
+
+use crate::{
+ arch::{kernel::context::Context, trap::kerneltrap::TrapFrame},
+ mm::{frame_allocator::FrameTracker, memory_space::MemorySpace},
+};
/// 任务
/// 存放任务的核心信息
@@ -26,14 +31,20 @@ pub struct Task {
/// 任务的所属进程id
/// NOTE: 由于采用了统一的任务模型,一个任务组内任务的 pid 是相同的,等于父任务的 pid 而父任务的 pid 等于自己的 tid
pub pid: usize,
+ /// 父任务的id
+ pub ptid: usize,
/// 内核栈基址
- kstack_base: usize,
+ pub kstack_base: usize,
+ /// 内核栈跟踪器
+ pub kstack_tracker: FrameTracker,
/// 中断上下文。指向当前任务内核栈上的 TrapFrame,仅在任务被中断时有效。
- trap_frame_ptr: AtomicPtr,
- /// 父任务的id
- parient_tid: usize,
+ /// XXX: AtomicPtr or *mut?
+ pub trap_frame_ptr: AtomicPtr,
+ /// 任务的内存空间
+ /// 对于内核任务,该字段为 None
+ pub memory_space: Option>,
/// 退出码
- exit_code: isize,
+ pub exit_code: isize,
// TODO: 由于部分相关子系统尚未实现,暂时留空
}
@@ -50,6 +61,37 @@ pub enum TaskState {
Uninterruptable,
}
+/// 创建一个新的内核线程并返回其 Arc 包装
+///
+/// 该函数负责:
+/// 1. 分配 Task 结构体本身,并用 Arc 包装
+/// 2. 分配内核栈物理页帧 (FrameTracker)
+/// 3. 将内核栈映射到虚拟地址空间 (VMM 逻辑)
+/// 4. 初始化 Task Context,设置栈指针和入口点
+/// 5. 将新的 Task 加入调度器队列
+///
+/// # 参数
+/// * `entry_point`: 线程开始执行的函数地址
+///
+/// # 返回值
+/// 包含新创建任务的 Arc
+pub fn kthread_spawn(_entry_point: fn()) -> Arc {
+ // 1. 分配内核栈 (假设 FrameTracker::alloc_one() 存在)
+ // let kstack_tracker = FrameTracker::alloc_one().expect("Failed to allocate kernel stack");
+ // let kstack_paddr = kstack_tracker.get_paddr();
+
+ // 2. 将物理页映射到连续的虚拟地址 (kstack_base)
+ // NOTE: 内核线程共享内核地址空间,映射逻辑相对简单
+
+ // 3. 构建 Task 实例
+ // let task = Task { /* ... 初始化字段 ... */ };
+
+ // 4. 将任务加入全局任务队列
+ // SCHEDULER.add_task(task.clone());
+
+ unimplemented!("kthread_spawn 核心逻辑尚未实现")
+}
+
// /// 关于任务的管理信息
// /// 存放与调度器、任务状态、队列相关的、需要高频访问和修改的数据。
// /// 主要由调度器子系统使用。
diff --git a/os/src/mm/frame_allocator/frame_allocator.rs b/os/src/mm/frame_allocator/frame_allocator.rs
index 947d6352..4d875a39 100644
--- a/os/src/mm/frame_allocator/frame_allocator.rs
+++ b/os/src/mm/frame_allocator/frame_allocator.rs
@@ -5,6 +5,7 @@ use crate::config::PAGE_SIZE;
use crate::mm::address::{ConvertablePaddr, Paddr, PageNum, Ppn, PpnRange, UsizeConvert};
use alloc::vec::Vec;
+#[derive(Debug)]
pub struct FrameTracker(Ppn);
impl FrameTracker {
diff --git a/os/src/mm/frame_allocator/mod.rs b/os/src/mm/frame_allocator/mod.rs
index 8c3737ac..bc609158 100644
--- a/os/src/mm/frame_allocator/mod.rs
+++ b/os/src/mm/frame_allocator/mod.rs
@@ -25,3 +25,5 @@ mod frame_allocator;
pub fn init_frame_allocator(start_addr: usize, end_addr: usize) {
frame_allocator::init_frame_allocator(start_addr, end_addr);
}
+
+pub use frame_allocator::FrameTracker;
diff --git a/os/src/mm/heap.rs b/os/src/mm/heap.rs
new file mode 100644
index 00000000..ccd74c6a
--- /dev/null
+++ b/os/src/mm/heap.rs
@@ -0,0 +1,81 @@
+// 内核堆(Kernel Heap)的公共接口,提供了 C 风格的 kmalloc 家族函数。
+
+// 引入在 kalloc.rs 中实现的全局分配器
+// #[global_allocator]
+// static ALLOCATOR: ...;
+//
+// 假设全局分配器 ALLOCATOR 已经被正确设置和初始化。
+
+// -------------------------------------------------------------------
+// C 风格的 kmalloc 家族接口 (Wrapper Functions)
+// -------------------------------------------------------------------
+
+/// kmalloc: 内核动态内存分配函数。
+///
+/// 功能: 分配至少 `size` 字节的内存块。返回的内存是**未初始化**的。
+///
+/// # Arguments
+/// * `size`: 需要分配的字节数。
+///
+/// # Safety
+/// 这是一个不安全函数,因为分配失败可能返回空指针,且使用者必须负责调用 kfree 释放。
+///
+/// # Returns
+/// 分配内存块的指针 (*mut u8);失败则返回 ptr::null_mut()。
+#[inline]
+pub unsafe fn kmalloc(_size: usize) -> *mut u8 {
+ todo!()
+}
+
+/// kcalloc: 内核动态内存分配并清零函数。
+///
+/// 功能: 分配 `count * size` 字节的内存块,并将分配的内存**清零**。
+///
+/// # Arguments
+/// * `count`: 元素数量。
+/// * `size`: 每个元素的字节数。
+///
+/// # Safety
+/// 必须负责释放。如果乘法溢出或分配失败,返回空指针。
+///
+/// # Returns
+/// 分配内存块的指针 (*mut u8);失败则返回 ptr::null_mut()。
+#[inline]
+pub unsafe fn kcalloc(_count: usize, _size: usize) -> *mut u8 {
+ todo!()
+}
+
+/// kfree: 释放之前由 kmalloc/kcalloc/krealloc 分配的内存块。
+///
+/// # Arguments
+/// * `ptr`: 待释放的内存块指针。
+/// * `size`: 释放的内存块大小。
+///
+/// # Safety
+/// * `ptr` 必须是由本分配器分配的。
+/// * `ptr` 必须是非空且尚未被释放。
+/// * `size` 必须与分配时的 `size` 相同。
+#[inline]
+pub unsafe fn kfree(_ptr: *mut u8, _size: usize) {
+ todo!()
+}
+
+/// krealloc: 重新调整内存块的大小。
+///
+/// 功能: 调整 `old_ptr` 指向的内存块大小为 `new_size`。
+///
+/// # Arguments
+/// * `old_ptr`: 原始内存块的指针。
+/// * `old_size`: 原始内存块的大小。
+/// * `new_size`: 新需要的大小。
+///
+/// # Safety
+/// * `old_ptr` 必须是本分配器分配的,且未释放。
+/// * `old_size` 必须与分配时的大小相同。
+///
+/// # Returns
+/// 新的内存块指针 (*mut u8);失败则返回 ptr::null_mut()。
+#[inline]
+pub unsafe fn krealloc(_old_ptr: *mut u8, _old_size: usize, _new_size: usize) -> *mut u8 {
+ todo!()
+}
diff --git a/os/src/mm/memory_space/mod.rs b/os/src/mm/memory_space/mod.rs
new file mode 100644
index 00000000..a6eaef90
--- /dev/null
+++ b/os/src/mm/memory_space/mod.rs
@@ -0,0 +1,5 @@
+/// MemorySpace:进程虚拟地址空间管理器。
+///
+/// 封装了进程的页表、所有虚拟内存区域 (VMA) 列表以及相关的同步和统计信息。
+#[derive(Debug)]
+pub struct MemorySpace {}
diff --git a/os/src/mm/mod.rs b/os/src/mm/mod.rs
index d8aba2b2..0146e34d 100644
--- a/os/src/mm/mod.rs
+++ b/os/src/mm/mod.rs
@@ -13,6 +13,8 @@
pub mod address;
pub mod frame_allocator;
pub mod global_allocator;
+pub mod heap;
+pub mod memory_space;
pub mod page_table;
pub use frame_allocator::init_frame_allocator;
diff --git a/os/src/mm/page_table/mod.rs b/os/src/mm/page_table/mod.rs
index b8dcc5b4..f7f448c9 100644
--- a/os/src/mm/page_table/mod.rs
+++ b/os/src/mm/page_table/mod.rs
@@ -31,4 +31,3 @@ pub enum PagingError {
}
pub type PagingResult = Result;
-