内存分配器
本节介绍堆内存分配器和物理页帧分配器
物理页帧分配
/// 使用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)
}
}
为其实现了GlobalAlloc
trait后,内核中即可使用动态内存结构,而具体内存分配和释放是由Rust编译器实现的。