From c239525dc488673033ed1a3d8b3bfb1f51a4ad9f Mon Sep 17 00:00:00 2001 From: zhangxinyu <840317537@qq.com> Date: Wed, 31 May 2023 13:53:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=A8=E5=B1=80=E7=9A=84=E5=86=85=E5=AD=98?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=99=A8=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ch4/os/src/config.rs | 3 ++ ch4/os/src/mm/address.rs | 73 ++++++++++++++++++++++++++++++++ ch4/os/src/mm/frame_allocator.rs | 61 ++++++++++++++++++++++++++ ch4/os/src/mm/mod.rs | 4 +- 4 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 ch4/os/src/mm/address.rs create mode 100644 ch4/os/src/mm/frame_allocator.rs diff --git a/ch4/os/src/config.rs b/ch4/os/src/config.rs index 0afa146..4d60d5d 100644 --- a/ch4/os/src/config.rs +++ b/ch4/os/src/config.rs @@ -2,6 +2,9 @@ pub const APP_BASE_ADDRESS: usize = 0x80400000; // 载入的app的起始的地 pub const APP_SIZE_LIMIT: usize = 0x20000; // app的最大的二进制文件能够使用的大小 pub const MAX_APP_NUM: usize = 10; // 支持最大的用户应用数量 pub const KERNEL_HEAP_SIZE: usize = 0x30_0000; // 内核的堆大小 3M +pub const PAGE_SIZE_BITS: usize = 0xc; // 页内偏移的位宽(也就是每页的大小) +pub const PAGE_SIZE: usize = 0x1000; // 每个页 的字节大小为 4kb +pub const MEMORY_END: usize = 0x80800000; // 设置我们当前操作系统最大只能用到 0x80800000-0x80000000大小的内存也就是8M pub const USER_STACK_SIZE: usize = 4096 * 2; // 每个应用用户态的栈大小为8kb pub const KERNEL_STACK_SIZE: usize = 4096 * 2; // 每个应用的内核栈 diff --git a/ch4/os/src/mm/address.rs b/ch4/os/src/mm/address.rs new file mode 100644 index 0000000..842bf0f --- /dev/null +++ b/ch4/os/src/mm/address.rs @@ -0,0 +1,73 @@ +use crate::config::{PAGE_SIZE, PAGE_SIZE_BITS}; + +const PA_WIDTH_SV39: usize = 56; // 真实物理地址 的字节宽度 44(物理页宽) + 12(4kb的宽度) +const PPN_WIDTH_SV39: usize = PA_WIDTH_SV39 - PAGE_SIZE_BITS; // 物理页 的宽度, 也就是 物理地址宽度-每页宽度=物理页宽度 + + +// 物理地址 +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub struct PhysAddr(pub usize); + +// 虚拟地址 +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub struct VirtAddr(pub usize); + +// 物理页 +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub struct PhysPageNum(pub usize); + +// 虚拟页 +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub struct VirtPageNum(pub usize); + + +// 把usize 转为真实地址(只保留 PA_WIDTH_SV39 位字节) +impl From for PhysAddr { + fn from(v: usize) -> Self { Self(v & ( (1 << PA_WIDTH_SV39) - 1 )) } +} + +// 把usize类型的物理页号 转为物理页结构体 (只保留物理页的宽度) +impl From for PhysPageNum { + fn from(v: usize) -> Self { Self(v & ( (1 << PPN_WIDTH_SV39) - 1 )) } +} + +// 把物理地址转为 usize, 直接返回即可 +impl From for usize { + fn from(v: PhysAddr) -> Self { v.0 } +} + +// 把物理页结构体转为 usize类型的物理页 直接返回即可 +impl From for usize { + fn from(v: PhysPageNum) -> Self { v.0 } +} + + +impl PhysAddr { + // 从当前物理地址得到页内偏移(只保留12个bit即可) + pub fn page_offset(&self) -> usize { + self.0 & (PAGE_SIZE - 1) + } + + // 舍去小数位, 把物理地址, 转为 物理页号 + pub fn floor(&self) -> PhysPageNum { + PhysPageNum(self.0 / PAGE_SIZE) + } + + // 有小数统一 +1, 物理地址转为物理页号 + pub fn ceil(&self) -> PhysPageNum { + PhysPageNum((self.0 + PAGE_SIZE - 1) / PAGE_SIZE) + } +} + +// 把PhysAddr 转为 PhysPageNum +impl From for PhysPageNum { + fn from(v: PhysAddr) -> Self { + assert_eq!(v.page_offset(), 0); + v.floor() + } +} + +// 把物理页转为物理地址(左移页宽即可) +impl From for PhysAddr { + fn from(v: PhysPageNum) -> Self { Self(v.0 << PAGE_SIZE_BITS) } +} \ No newline at end of file diff --git a/ch4/os/src/mm/frame_allocator.rs b/ch4/os/src/mm/frame_allocator.rs new file mode 100644 index 0000000..e90b454 --- /dev/null +++ b/ch4/os/src/mm/frame_allocator.rs @@ -0,0 +1,61 @@ +use alloc::vec::Vec; +use lazy_static::lazy_static; +use crate::mm::address::{PhysPageNum, PhysAddr}; +use crate::config::MEMORY_END; +use crate::sync::UPSafeCell; + +extern "C" { + fn ekernel(); +} + +// 物理页帧管理器, 它管理了 ekernel(操作系统使用的内存结束位置) 到 MEMORY_END(最大的可用的物理内存地址)所有的内存, 这部分内存是给用户用的 +// current, end 这个区间表示此前从未被分配过的页 +// recycled 表示之前被分配过, 但是已经被回收可以再利用的页 +pub struct StackFrameAllocator { + current: usize, + end: usize, + recycled: Vec, +} + +impl StackFrameAllocator { + fn from(l: PhysPageNum, r: PhysPageNum) -> Self { + Self { + current: l.0, + end: r.0, + recycled: Vec::new(), + } + } +} + +impl StackFrameAllocator { + // 从分配器中, 找到一个空闲页(它可能之前从未分配过, 或者之前被分配但是已经被回收的完好页) + fn alloc(&mut self) -> Option { + if let Some(ppn) = self.recycled.pop() { + Some(ppn.into()) + } else if self.current == self.end { + None + } else { + self.current += 1; + Some((self.current - 1).into()) + } + } + + // 回收一个页, 为了防止二次回收, 我们这里直接panic + fn dealloc(&mut self, ppn: PhysPageNum) { + let ppn = ppn.0; + // 防止被二次回收 + if ppn >= self.current || self.recycled.iter().any(|&v| v == ppn) { + panic!("Frame ppn={:#x} has not been allocated!", ppn); + } + // 校验完毕, 把这个页, 还给管理器 + self.recycled.push(ppn); + } +} + +lazy_static! { + pub static ref FRAME_ALLOCATOR: UPSafeCell = unsafe { + // 这里我们只要完整的页, 舍去开头和结尾一些碎片 + UPSafeCell::new(StackFrameAllocator::from(PhysAddr::from(ekernel as usize).ceil(), PhysAddr::from(MEMORY_END).floor())) + }; +} + diff --git a/ch4/os/src/mm/mod.rs b/ch4/os/src/mm/mod.rs index e7b4c87..6794603 100644 --- a/ch4/os/src/mm/mod.rs +++ b/ch4/os/src/mm/mod.rs @@ -1 +1,3 @@ -pub mod heap_allocator; \ No newline at end of file +pub mod heap_allocator; +pub mod address; +pub mod frame_allocator; \ No newline at end of file