很高兴能够进入第三阶段,本来想着好好跟进一下,但是很遗憾与考试科目以及组里的项目冲突里,仅仅完成了前三次课程的实验,后面大体了解了一下宏内核与Hypervisor
第一次课 HelloWorld

什么是组件化操作系统? 上面是课件里很形象的一张图。在裸机程序中,Bootloader阶段后,需要初始化寄存器、栈指针等。之后初始化串口驱动,并在此基础上打印HelloWorld,这是一套连贯的操作流程。在层次化中,可以分为Hal层和runtime层,Hal层对于对应一些基本的初始化,runtime层对应的是驱动的初始化,为上层的应用提供运行时环境。如果迭代为组件化结构,那么优势就很明显了,在hal层可以抽象出不同架构的初始化,runtime层分为了不同组件,在构建不同的裸机应用时,可以有选择的使用相关的组件,有利于系统的裁剪和定制化。
一个简单的helloword程序使用的组件大致如图所示

课后练习
为了实现带颜色的输出,一个最简单的实现是在输出字符串前后加入相应的转义字符。
修改axstd组件中println的实现即可
1 2 3 4 5 6 7
| #[macro_export] macro_rules! println { () => { $crate::print!("\n") }; ($($arg:tt)*) => { $crate::io::__print_impl(format_args!("\x1b[34m{}\x1b[0m\n", format_args!($($arg)*))); } }
|
、
或者修改axhal中putchar的实现,在打印每一个字符时都加入相应的转义字符
1 2 3 4 5 6 7 8 9 10 11 12 13
| pub fn putchar(c: u8) { #[allow(deprecated)] sbi_rt::legacy::console_putchar('\x1b' as usize); sbi_rt::legacy::console_putchar('[' as usize); sbi_rt::legacy::console_putchar('3' as usize); sbi_rt::legacy::console_putchar('4' as usize); sbi_rt::legacy::console_putchar('m' as usize); sbi_rt::legacy::console_putchar(c as usize); sbi_rt::legacy::console_putchar('\x1b' as usize); sbi_rt::legacy::console_putchar('[' as usize); sbi_rt::legacy::console_putchar('0' as usize); sbi_rt::legacy::console_putchar('m' as usize); }
|

第二次课 Collections
引入了新的组件axalloc,以便于实现动态的内存分配。
课后练习
支持HashMap
使用hashbrown作为HashMap实现

第三次课
课后练习

实现一个简单的bump内存分配算法,比较简单,维护相应的trait即可。Page不考虑回收,回收Bytes的时候记录剩余的Bytes计数,在count为0的时候将byte_pos归零。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| pub struct EarlyAllocator<const PAGE_SIZE: usize> { start: usize, b_pos: usize, p_pos: usize, end: usize, count: usize, }
impl<const PAGE_SIZE: usize> EarlyAllocator<PAGE_SIZE> { pub const fn new() -> Self { Self { start: 0, b_pos: 0, p_pos: 0, end: 0, count: 0, } } }
impl<const PAGE_SIZE: usize> BaseAllocator for EarlyAllocator<PAGE_SIZE> { fn init(&mut self, start: usize, size: usize) { self.start = start; self.end = start + size; self.b_pos = start; self.p_pos = self.end; self.count = 0; } fn add_memory(&mut self, _start: usize, _size: usize) -> AllocResult { Err(AllocError::NoMemory) } }
impl<const PAGE_SIZE: usize> ByteAllocator for EarlyAllocator<PAGE_SIZE> { fn alloc(&mut self, layout: Layout) -> AllocResult<NonNull<u8>> { let size = layout.size(); let align = layout.align(); let align_mask = align - 1; let new_pos = (self.b_pos + align_mask) & !align_mask; if new_pos + size > self.p_pos { return Err(AllocError::NoMemory); } self.b_pos = new_pos + size; self.count += 1; Ok(NonNull::new(new_pos as *mut u8).unwrap()) } fn dealloc(&mut self, _ptr: NonNull<u8>, _layout: Layout) { if self.count > 0 { self.count -= 1; } if self.count == 0 { self.b_pos = self.start; } } fn total_bytes(&self) -> usize { self.end - self.start } fn available_bytes(&self) -> usize { self.p_pos - self.b_pos } fn used_bytes(&self) -> usize { self.b_pos - self.start } }
impl<const PAGE_SIZE: usize> PageAllocator for EarlyAllocator<PAGE_SIZE> { const PAGE_SIZE: usize = PAGE_SIZE; fn alloc_pages(&mut self, num_pages: usize, align_pow2: usize) -> AllocResult<usize> { if align_pow2 % Self::PAGE_SIZE != 0 { return Err(AllocError::InvalidParam); } let align_pow2 = align_pow2 / Self::PAGE_SIZE; if !align_pow2.is_power_of_two() { return Err(AllocError::InvalidParam); } let p_pos = self.p_pos - num_pages * Self::PAGE_SIZE; if p_pos < self.b_pos { return Err(AllocError::NoMemory); }
self.p_pos -= num_pages * Self::PAGE_SIZE; Ok(self.p_pos) } fn dealloc_pages(&mut self, _pos: usize, _num_pages: usize) { } fn total_pages(&self) -> usize { (self.end - self.start) / Self::PAGE_SIZE } fn used_pages(&self) -> usize { (self.end - self.p_pos) / Self::PAGE_SIZE } fn available_pages(&self) -> usize { self.p_pos / Self::PAGE_SIZE } }
|
感悟
第三节阶段的代码相对于第二阶段清晰了不少,很后悔没有持续跟进下去,其实本人一直想学习的是基于协程异步机制的操作系统/驱动这一部分,不知道为什么没有相应的前置课程,希望第四阶段能够坚持下去,学习一下协程、异步机制以及操作系统的一些新实践。