全局的内存管理器的分配和释放物理页帧完成

ch4
zhangxinyu 2 years ago
parent c239525dc4
commit d4069b8cde

@ -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"
buddy_system_allocator = "0.6"
bitflags = "1.2.1"

@ -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

@ -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();

@ -41,6 +41,18 @@ impl From<PhysPageNum> for usize {
fn from(v: PhysPageNum) -> Self { v.0 }
}
// 把PhysAddr 转为 PhysPageNum
impl From<PhysAddr> for PhysPageNum {
fn from(v: PhysAddr) -> Self {
assert_eq!(v.page_offset(), 0);
v.floor()
}
}
// 把物理页转为物理地址(左移页宽即可)
impl From<PhysPageNum> 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<PhysAddr> 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<PhysPageNum> for PhysAddr {
fn from(v: PhysPageNum) -> Self { Self(v.0 << PAGE_SIZE_BITS) }
// 把物理页转为物理地址之后, 再转为T, 强转
pub fn get_mut<T>(&self) -> &'static mut T {
let pa: PhysAddr = (*self).into();
unsafe { (pa.0 as *mut T).as_mut().unwrap() }
}
}

@ -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<usize>,
current: PhysPageNum,
end: PhysPageNum,
recycled: Vec<PhysPageNum>,
}
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<PhysPageNum> {
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<StackFrameAllocator> = unsafe {
static ref FRAME_ALLOCATOR: UPSafeCell<StackFrameAllocator> = unsafe {
// 这里我们只要完整的页, 舍去开头和结尾一些碎片
UPSafeCell::new(StackFrameAllocator::from(PhysAddr::from(ekernel as usize).ceil(), PhysAddr::from(MEMORY_END).floor()))
};
}
/// 分配一个页
pub fn frame_alloc() -> Option<FrameTracker> {
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<FrameTracker> = 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!");
}

@ -1,3 +1,14 @@
pub mod heap_allocator;
pub mod address;
pub mod frame_allocator;
pub mod frame_allocator;
pub fn init(){
// 开启 内核的内存分配器
heap_allocator::init_heap();
// 初始化bin测试 物理页帧管理器
frame_allocator::frame_allocator_test();
}

@ -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<FrameTracker>,
}
Loading…
Cancel
Save