前言
发了三周完成第二阶段的前三个实验,并进入到第三阶段,第三阶段一开始一周,在接下来的三周里奋起追赶进度,花了两周半完成了第一周的5个实验。
从接触arceos的unikernel整体架构设计,有以下感悟:
组件化设计,内核的所有依赖根据需要引入,包括组件内部的依赖,这个控制是到代码块级,粒度非常小,rust包管理和语法提供很好的支持
平台相关和平台无关的模块相互隔离,这一块解耦合有助于写平台无关模块的代码,提升代码的复用性
练习1和2
需要给应用设计一个头部,包含应用大小,根据大小来加载应用,而不是固定加载32字节。在实验过程中,单纯的添加头部,会导致qemu读取pflash读取失败,经过一番
尝试后发现,pflash一定要32M大小,大一点小一点都不行,然后就是修改应用端构建的命令。先添加头部,然后添加数据段,再加上32M空数据,
最后通过dd命令将整个文件的前32M拷贝到另一个文件,这个文件即为我们的最终文件。
头部设计:
应用数量|第一个应用大小|第二应用大小|….\n
第一个应用数据+第二个应用数据
头部通过\n和数据端区分,两个应用的数据无间隔,因为已经从头部得知应用大小了
练习3
练习3的难点在于如何执行玩应用程序,再返回到内核,这里有个细节,就是无返回 即 -> !,生成的汇编是没有ret的,我们需要ret指令会到内核,只需要改下
用户代码即可。
练习4
实现terminate,通过查看arceos的代码发现,arceos_api中有这块代码,问题就在于引入arceos_api,并调用对应的ax_terminate方法
练习5
比较难,发现只会输出hello world并报load page fault的错误或其他错误,通过排查发现连续调用两次hello world也会报错。通过qemu.log的汇编,
将a7寄存器保存到一个全局变量后,依旧存在错误。第二步,发现连hello_world也没有输出,怀疑应用内存布局的原因,导致start并没有放置在程序开头
位置,通过静态链接文件ld固定内存布局,解决问题。第三步,通过禁止内联消除寄存器被覆盖的现象,发现依旧报错。第四步,发现和编译器生成的调用函数汇编
有差异,无保存恢复寄存器的操作,仿照这部分代码,保存了ra寄存器。实验成功了!
实验6
实验6比较简单,两个用户根页表切换一下,可以一模一样,反正是顺序执行。把会陷入无限等待的代码改一下即可
总结
第三阶段更多的是实验,相比于阶段二,需要更多的探索和debugger的能力。通过第三阶段,我发现了前面学习中遗漏的知识,比如函数调用规范,外设的dbt文件和
cargo的包管理的细节等等。
发时间最多的是第一周的实验2,在不停的开启注释代码修改代码中,了解到看似无比复杂的依赖,其实可以被梳理清楚,到能运行时,依赖并不是很多,也就实现了
最小化原则。和arceos的设计有异曲同工的感觉,如果只用其中一部分功能,很多方法及依赖都不是必须的,且是可以替代的。