arceos 总结
前言
arceos 是基于组件化的思想构建的,通过使用不同的组建,可以分别编译出Unikernel, Monolithic Kernel, Hypervisor三种内核架构。我是第一次深入了解Unikernel,Hypervisor这两种架构,几乎没有合适的书籍去展开这些内容,整一个五一假期下来,我对它们有了初步的理解和探索经验。
Unikernel
Unikernel 这部分是渐进式地学习,U.1~U.8一共8个Unikernel的内核,其中插入四个练习:
- 带颜色的输出
- 手写一个HashMap
- 实现bump内存分配算法
- 在axfs_ramfs下实现rename功能
第一个任务,只要理解了用户使用println!()
的调用流就可以完成:
1 | main --> axstd --> arceos_api --> axhal --> 硬件 |
这样的调用关系也基本和arceos中系统架构的层次关系吻合:
1 | 硬件 --> axhal --> ax_modules --> arceos_api --> axstd --> user |
第二个任务,手写一个HashMap,这个任务其实很让人为难,因为它非常具有灵活性,简单的可以从OI wiki上面把哈希表的C++代码“翻译”成rust,再实现一个拉链法就完成了。复杂的可以参考rust的标准库,用裸指针操作,加上PhantomData标记生命周期和所有权。
我偷了点懒,实现了一个丐版的,我首先是参考xxhash实现了一个Hasher用于支持hash操作和随机数支持,然后包装了Vec用于内存分配和动态扩容的机制。避免hash冲突的方案,我选择了比较简单的线性探测法。
第三个任务,bump内存分配算法,基本原理就是维护一个指向内存起始区域的指针和一个偏移量,根据传入的Layout依次分配内存,无法单独释放内存块。只需要实现arlyAllocator
trait就可以接入内核中了。
第四个任务,虚拟文件系统会调用具体的文件系统进行操作,我最终找到调用的终点是axfs_ramfs下的DirNode结构体中,接下来就很简单了,就是修改两个DirNode下的BTreeMap<String, VfsNodeRef>
。
唯一一点胃痛的就是arceos/modules/axfs/src/root.rs
的rename
函数中,src_path
去除了挂载点的前缀,但是dst_path
没去掉,这样实际上就不知道,dst_path
是相对ramfs还是相对主文件系统根目录的了。
Monolithic Kernel
在宏内核中,基于Unikernel引入了用户态和系统调用的概念,可以看到相比U.8,M.1中增加了一些额外的模块。但是相比从零构建,我们大量复用了原有的模块,发挥了组件化内核的优势。
宏内核这部分只有一个练习,实现sys_mmap系统调用,并能够对文件进行内存映射。如果真的想要实现一个完整的sys_mmap,恐怕有些复杂。我们需要一个新的内存映射后端对文件进行映射,第一,要求能够从文件描述符中访问文件;第二,要求能够接受来自文件系统的信号,并及时将脏页同步到磁盘中。
这工作量确实有些大,所以我选择了参考提示信息“可以参考m_1_0中加载应用到用户地址空间的代码段”,先使用fd
读取源文件的内容,使用alloc
的后端分配内存,在查找用户态的页表获取对应的物理地址,最后写入原文件的内容。于是完成了本次练习。
Hypervisor
这一部分,在Unikernel的基础上引入RISC-V的H扩展,获得了虚拟化的能力。但是目前虚拟机的能力还非常的弱,准确来说只有两个对象,虚拟地址空间(aspace)和虚拟处理器(Vcpu)。
这一部分的练习中,我们只需要了解客户端在 vmexit_handler 中处理异常,然后针对我们的测试样例给客户端的a0
,a1
寄存器进行赋值即可。
第三阶段总结
相比rcore,arceos复杂了许多,不过arceos中的内容在rcore中都有了一个丐版的实现,我做起来也快了不少。
在arceos中第一次了解到了组件化内核的概念,这三种不同类型的内核都可以通过像搭积木的方式,把需要的组件加上,再补齐特定的细节就可以实现从Unikernel到宏内核或者Hypervisor的跨越。
最后的最后,我谈谈自己的感受。开源操作系统训练营真的是一个非常优质的课程,有非常专业的教授老师,有积极回复的助教团队,在这里我一个普通的大学生感受到了深深的震撼,也许这命运的齿轮就在这里开始转动。