diff --git a/ch4/os/Cargo.toml b/ch4/os/Cargo.toml index 7980275..f6a6e0b 100644 --- a/ch4/os/Cargo.toml +++ b/ch4/os/Cargo.toml @@ -13,4 +13,5 @@ debug=true riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } lazy_static = { version = "1.4.0", features = ["spin_no_std"] } sbi-rt = { version = "0.0.2", features = ["legacy"] } -buddy_system_allocator = "0.6" \ No newline at end of file +buddy_system_allocator = "0.6" +bitflags = "1.2.1" \ No newline at end of file diff --git a/ch4/os/Makefile b/ch4/os/Makefile index f2c534f..1a7acf0 100644 --- a/ch4/os/Makefile +++ b/ch4/os/Makefile @@ -43,7 +43,7 @@ run:build_elf $(KERNEL_BIN) $(SYMBOL_MAP) kill -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY) clean: - rm -rf ./target* && rm -rf ./src/link_app.S + #rm -rf ./target* && rm -rf ./src/link_app.S kill: -pkill -9 qemu diff --git a/ch4/os/src/main.rs b/ch4/os/src/main.rs index eb00600..95bcf95 100644 --- a/ch4/os/src/main.rs +++ b/ch4/os/src/main.rs @@ -49,7 +49,7 @@ pub fn rust_main(){ println!("boot_stack_top_bound: {:#x}, boot_stack_lower_bound: {:#x}", boot_stack_top_bound as usize, boot_stack_lower_bound as usize); // 初始化动态内存分配器, 使我们能在内核中使用动态大小数据类型 - mm::heap_allocator::init_heap(); + mm::init(); trap::init(); loader::load_app(); diff --git a/ch4/os/src/mm/address.rs b/ch4/os/src/mm/address.rs index 842bf0f..49d4dca 100644 --- a/ch4/os/src/mm/address.rs +++ b/ch4/os/src/mm/address.rs @@ -41,6 +41,18 @@ impl From for usize { fn from(v: PhysPageNum) -> Self { v.0 } } +// 把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) } +} impl PhysAddr { // 从当前物理地址得到页内偏移(只保留12个bit即可) @@ -59,15 +71,25 @@ impl PhysAddr { } } -// 把PhysAddr 转为 PhysPageNum -impl From for PhysPageNum { - fn from(v: PhysAddr) -> Self { - assert_eq!(v.page_offset(), 0); - v.floor() + +impl PhysPageNum { + // // 从当前物理页保存的数据中得到所有的 PTE + // pub fn get_pte_array(&self) -> &'static mut [PageTableEntry] { + // // 物理页号 转为物理地址 + // let pa: PhysAddr = (*self).into(); + // // 从物理页地址读出一页 512个8字节(4k)的数据 + // unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut PageTableEntry, 512) } + // } + + // 从当前物理页得到 引用类型的 byte array + pub fn get_bytes_array(&self) -> &'static mut [u8] { + let pa: PhysAddr = (*self).into(); + unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut u8, 4096) } } -} -// 把物理页转为物理地址(左移页宽即可) -impl From for PhysAddr { - fn from(v: PhysPageNum) -> Self { Self(v.0 << PAGE_SIZE_BITS) } + // 把物理页转为物理地址之后, 再转为T, 强转 + pub fn get_mut(&self) -> &'static mut T { + let pa: PhysAddr = (*self).into(); + unsafe { (pa.0 as *mut T).as_mut().unwrap() } + } } \ No newline at end of file diff --git a/ch4/os/src/mm/frame_allocator.rs b/ch4/os/src/mm/frame_allocator.rs index e90b454..3e3108c 100644 --- a/ch4/os/src/mm/frame_allocator.rs +++ b/ch4/os/src/mm/frame_allocator.rs @@ -2,26 +2,52 @@ use alloc::vec::Vec; use lazy_static::lazy_static; use crate::mm::address::{PhysPageNum, PhysAddr}; use crate::config::MEMORY_END; +use crate::println; use crate::sync::UPSafeCell; extern "C" { fn ekernel(); } +// 物理页帧的结构体, 他和物理页号不同, 它是管理整个物理页内的数据, 而物理页号被页表管理 +// 我们使用这个结构体, 来利用rust变量生命周期, 自动管理该页, 数据清零或者还回物理页帧管理器 +pub struct FrameTracker { + pub ppn: PhysPageNum, +} + +impl FrameTracker { + pub fn new(ppn: PhysPageNum) -> Self { + // 页数据初始化 + let bytes_array = ppn.get_bytes_array(); + for i in bytes_array { + *i = 0; + } + Self { ppn } + } +} + +impl Drop for FrameTracker { + // 生命周期结束, 自动把页还给 物理页帧管理器 + fn drop(&mut self) { + frame_dealloc(self.ppn); + } +} + + // 物理页帧管理器, 它管理了 ekernel(操作系统使用的内存结束位置) 到 MEMORY_END(最大的可用的物理内存地址)所有的内存, 这部分内存是给用户用的 // current, end 这个区间表示此前从未被分配过的页 // recycled 表示之前被分配过, 但是已经被回收可以再利用的页 pub struct StackFrameAllocator { - current: usize, - end: usize, - recycled: Vec, + current: PhysPageNum, + end: PhysPageNum, + recycled: Vec, } impl StackFrameAllocator { fn from(l: PhysPageNum, r: PhysPageNum) -> Self { Self { - current: l.0, - end: r.0, + current: l, + end: r, recycled: Vec::new(), } } @@ -31,21 +57,22 @@ impl StackFrameAllocator { // 从分配器中, 找到一个空闲页(它可能之前从未分配过, 或者之前被分配但是已经被回收的完好页) fn alloc(&mut self) -> Option { if let Some(ppn) = self.recycled.pop() { - Some(ppn.into()) + Some(ppn) } else if self.current == self.end { None } else { - self.current += 1; - Some((self.current - 1).into()) + let current_ppn = self.current; + let next_ppn = (self.current.0 + 1).into(); + self.current = next_ppn; + Some(current_ppn) } } // 回收一个页, 为了防止二次回收, 我们这里直接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); + panic!("Frame ppn={:#x} has not been allocated!", ppn.0); } // 校验完毕, 把这个页, 还给管理器 self.recycled.push(ppn); @@ -53,9 +80,39 @@ impl StackFrameAllocator { } lazy_static! { - pub static ref FRAME_ALLOCATOR: UPSafeCell = unsafe { + static ref FRAME_ALLOCATOR: UPSafeCell = unsafe { // 这里我们只要完整的页, 舍去开头和结尾一些碎片 UPSafeCell::new(StackFrameAllocator::from(PhysAddr::from(ekernel as usize).ceil(), PhysAddr::from(MEMORY_END).floor())) }; } +/// 分配一个页 +pub fn frame_alloc() -> Option { + FRAME_ALLOCATOR + .exclusive_access() + .alloc() + .map(FrameTracker::new) +} + +/// 回收指定的页 +fn frame_dealloc(ppn: PhysPageNum) { + FRAME_ALLOCATOR.exclusive_access().dealloc(ppn); +} + + +pub fn frame_allocator_test() { + let mut v: Vec = Vec::new(); + for i in 0..5 { + let frame = frame_alloc().unwrap(); + println!("{:?}", frame.ppn.0); + v.push(frame); + } + v.clear(); + for i in 0..5 { + let frame = frame_alloc().unwrap(); + println!("{:?}", frame.ppn.0); + v.push(frame); + } + drop(v); + println!("frame_allocator_test passed!"); +} diff --git a/ch4/os/src/mm/mod.rs b/ch4/os/src/mm/mod.rs index 6794603..9d15053 100644 --- a/ch4/os/src/mm/mod.rs +++ b/ch4/os/src/mm/mod.rs @@ -1,3 +1,14 @@ pub mod heap_allocator; pub mod address; -pub mod frame_allocator; \ No newline at end of file +pub mod frame_allocator; + + + +pub fn init(){ + // 开启 内核的内存分配器 + heap_allocator::init_heap(); + // 初始化bin测试 物理页帧管理器 + frame_allocator::frame_allocator_test(); + + +} \ No newline at end of file diff --git a/ch4/os/src/mm/page_table.rs b/ch4/os/src/mm/page_table.rs new file mode 100644 index 0000000..210bbd8 --- /dev/null +++ b/ch4/os/src/mm/page_table.rs @@ -0,0 +1,77 @@ +use alloc::vec::Vec; +use bitflags::*; +use crate::mm::address::PhysPageNum; + + +bitflags! { + pub struct PTEFlags: u8 { + const V = 1 << 0; // 仅当位 V 为 1 时,页表项才是合法的 + const R = 1 << 1; // 可读标志 + const W = 1 << 2; // 可写标志 + const X = 1 << 3; // 可执行标志 + const U = 1 << 4; // 是否允许U级访问 + const G = 1 << 5; // + const A = 1 << 6; // 从上次清零之后, 该页是否被访问过 + const D = 1 << 7; // 从上次清零之后, 该页是否被修改过 + } +} + +#[derive(Copy, Clone)] +#[repr(C)] +/// pte结构体 +/// +/// 对应的64个字节的布局为 +/// +/// 10bit(保留) | 44bit(物理页号) | 10bit(标志位) +pub struct PageTableEntry { + pub bits: usize, +} + + +impl PageTableEntry { + // 从一个物理页号和一个flag 创建出来一个 PTE + // 物理也好 左移10位 加上 flag 即可 + pub fn from(ppn: PhysPageNum, flags: PTEFlags) -> Self { + PageTableEntry { + bits: ppn.0 << 10 | flags.bits as usize, + } + } + + // 生成一个全0的PTE, V也是0, 它是不合法的 + pub fn new() -> Self { + PageTableEntry { bits: 0 } + } + + // 从PTE中的到 物理页的地址, 右移bit flag的宽度 再只保留44位即可得到 usize类型的物理页号, 随后调用into进行转换 + pub fn ppn(&self) -> PhysPageNum { + (self.bits >> 10 & ((1usize << 44) - 1)).into() + } + // 从PTE中得到bit flag + pub fn flags(&self) -> PTEFlags { + PTEFlags::from_bits(self.bits as u8).unwrap() + } + + // 判断是否合法, 只保留 V标志的位置, 再判断是否为0 + pub fn is_valid(&self) -> bool { + (self.flags() & PTEFlags::V) != PTEFlags::empty() + } + // 判断是否可读 + pub fn readable(&self) -> bool { + (self.flags() & PTEFlags::R) != PTEFlags::empty() + } + // 判断是否可写 + pub fn writable(&self) -> bool { + (self.flags() & PTEFlags::W) != PTEFlags::empty() + } + // 判断是否可执行 + pub fn executable(&self) -> bool { + (self.flags() & PTEFlags::X) != PTEFlags::empty() + } +} + +// 页表 +pub struct PageTable { + root_ppn: PhysPageNum, + // 这里保存的是页表每一页所占的物理页帧的页号 + frames: Vec, +} \ No newline at end of file