内存分配器

本节介绍堆内存分配器和物理页帧分配器

物理页帧分配

/// 使用bitmap_allocator库定义全局BIT_ALLOCATOR
static BIT_ALLOCATOR: Mutex<BitAlloc256M> = Mutex::new(BitAlloc256M::DEFAULT);

/// 初始化页帧分配器
pub fn init(memory_regions: &'static mut MemoryRegions) {
    let mut ba = BIT_ALLOCATOR.lock();
    println!("[Kernel] Memory regions:");
    for region in memory_regions.into_iter() {
        println!("    {:x?}", region);
        if region.kind == MemoryRegionKind::Usable {
            let start = region.start as usize;
            let end = region.end;
            let start_frame = start as usize / PAGE_SIZE;
            let end_frame = end as usize / PAGE_SIZE;
            ba.insert(start_frame..end_frame);
        }
    }
}

NUDT-OS使用了一个基于位图实现的物理页帧分配器,利用bootloader启动后传入的内存区域信息初始化位图。

/// 物理页帧
#[derive(Debug)]
#[repr(transparent)]
pub struct PhysFrame(usize);

一个物理页帧大小为4096字节,#[repr(transparent)]保证PhysFrame结构体与usize具有相同的内存布局。

impl PhysFrame {
    /// 分配一个物理页帧
    pub fn alloc() -> Option<Self> {
        let mut bitmap_allocator = BIT_ALLOCATOR.lock();
        let paddr = bitmap_allocator.alloc().map(|id| id * PAGE_SIZE).unwrap();
        Some(PhysFrame(paddr))
    }

    /// 回收一个物理页帧
    pub fn dealloc(pa: usize) {
        BIT_ALLOCATOR.lock().dealloc(pa)
    }
    ...
}

impl Drop for PhysFrame {
    /// 页帧结构体声明周期结束时,由编译器调用释放其使用的位
    fn drop(&mut self) {
        BIT_ALLOCATOR.lock().dealloc(self.0);
    }
}

物理页帧由内核显示地分配,需要一个新的页帧时使用alloc()方法分配,但是是由编译器隐式地释放的,释放时调用drop()方法,释放掉页帧对应的bit。

堆内存分配

use buddy_system_allocator::Heap;


struct LockedHeap(Cell<Heap<32>>);
#[global_allocator]
static HEAP_ALLOCATOR: LockedHeap = LockedHeap(Cell::new(Heap::new()));

我们使用了一个带伙伴系统的堆内存分配器(来自第三方库),使用#[global_allocator]将其注册为全局堆内存分配器。

unsafe impl GlobalAlloc for LockedHeap {
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        self.0
            .get()
            .alloc(layout)
            .ok()
            .map_or(0 as _, |p| p.as_ptr())
    }

    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
        self.0.get().dealloc(NonNull::new_unchecked(ptr), layout)
    }
}

为其实现了GlobalAlloctrait后,内核中即可使用动态内存结构,而具体内存分配和释放是由Rust编译器实现的。