进入第三阶段当然很高兴,但时间上实在太紧,我一开始的心态是按看懂源代码中的每一步来走的,但目前只是看完了所有相关的 makefile,以及看了部分 PPT 中强调部分的源代码。实验的话也是完成了第一周的内容以及 Hypervisor 的实验一。
arceos流程小结
总体执行流程:axhal的boot.rs中先引导,用于初始化内核栈,设置初始的页表,这里映射了两个 1G 的大页,分别是低地址恒等映射以及高地址的一个映射。随后要调用 axruntime 的 rust_main 来进一步完善运行时,这里会涉及到 axfeat 里面用到的 feature 条件编译并初始化一些东西,比如启用了 paging 这个 feature 就会重新映射页表这样。调用 rust_main 的时候加了一个偏移,偏移的大小其实就是高地址映射的偏移这样。随后 axruntime 做好运行时之后,调用 main 函数。随后调用结束函数,结束整个流程。
课后练习1:支持带颜色的打印输出。
具体实现:修改axhal中putchar的实现,在打印每一个字符时都加入相应的转义字符。
关于转义字符的具体解析的话:颜色定义的部分可以参考 console_codes(4) — Linux manual page 的 ECMA-48 Select Graphic Rendition
部分。在 makefile 中的 run_cmd
函数用于辅助输出同时会以颜色输出命令,参数 1 控制颜色为白色,参数 2 控制为灰色,随后执行命令。
颜色控制部分关键如下
1 | param result |
ECMA-48 SGR
序列以 ESC[ parameters m
的格式控制显示的属性。parameters 用分号隔开,\033[92;1m
为例,033 为八进制,表示 ESC,92 和 1分别是 param,参考上文的 ECMA-48 Select Graphic Rendition
92 表示绿色明亮前景色。1 表示加粗,0 表示重置属性。
课后练习2:支持 HashMap
ax_api 作为下层 module 的抽象,随后 axstd 作为我们自己定义的标准库供用户程序调用。
我的想法其实也是直接看 std 的 HashMap 怎么实现,然后改一改,做到最小的满足测试案例中的那些操作。然后慢慢改一改,后面看到官方也用了 hashbrown 作为实现,然后我就改了改,把随机数用到 hashbrown 这边大概。然后就好了。
课后练习3:实现 alt_alloc
这个分配器应当是用作早期boot之后系统初始化阶段的内存分配器,所以实现上相对简单,每次分配都向后增长。dealloc的时候尝试减掉已经alloc的空间大小,如果为0,那就重新开始从头分配这样。
挑战题目:实现特定分配算法
这个算法的话,还是要感谢万能的群友,我一开始想的也是在TLSF基础上改一改这样。后面发现有取巧的地方,主要也是循环一直进行,奇数项的内容永不释放,所以就取巧了,会记录每次奇数次申请的空间,下次申请如果比上次的小,那就返回该地址。如果比上次大,那就重新申请,释放上次的内容这样。
达到的轮次是65536应该是,也是一个非常 unsigned int 的值。
思路详细描述:由于每次申请的空间大小会加1,相比上一次。例如,delta 为1,申请的空间依次是32 + 1, 32 * 2 + 1, 。delta 为 2,申请的空间依次是32 +2, 32*2 +2,。所以,第一次循环会反复替换掉分配器中的记录。
但是,从第二次开始,每次当 index 为 13 的时候,会替换上一次的内容。因为上一次的 13 比这次的 13 申请的空间小 1。但是呢,14 不是最后一次循环吗,因为 14 是偶数所以不管。奇数就很神奇,每次都是到 index 为 13 的时候重新申请一次空间。其余时候,其实大家返回的地址都是一样的。嘿嘿。而且,每次替换的时候都会把上次的给释放了,哎,就很神奇。
首先主体部分使用特定内存分配器 tlsf。包括申请,释放等操作。
其次,定制 alloc 函数。用 indicator 记录此时调用 alloc_pass 函数的 delta值。用 index 记录 alloc_pass 函数中的循环次数。
alloc 的时候需要谨慎计算,2的多少次方啊,当index = 14 的时候,重置为 0,然后indiactor需要加 1 啊,之类的。
这里要注意一点,源码里,分配器有个special变量,为bool类型。这里用它的主要原因在于有个很神奇的点:当indiactor为32, index = 1的时候,items需要申请 0x60 的空间,而恰好,下次申请向量空间也是 0x60 的大小。就,重复了。嗯。所以,作为单独判断条件,然后重置,第一次出现先不管,然后继续。然后,bug解决。
Hypervisor课后练习1
hypervisior流程分析:这里初始化阶段我们的hyper会布置好一个现场类似我们在宏内核中设置好sstatus寄存器那样,随后通过sret到GUEST中执行,GUEST中遇到无法处理的情况又会陷入Hypervisior中,通过对不同类型的陷入执行不同的代码,处理好用户的错误设置寄存器等操作,随后返回GUEST继续执行。
这里的第一个实验比较简单:只需要在处理错误函数中设置寄存器的值,然后设置sepc += 4,让GUEST去执行出错指令的下一条指令即可。
Hypervisior课后练习2
这里会涉及到虚拟化里的地址二阶段映射。主要流程:GUEST中的虚拟地址先通过 VSATP 翻译,通过 GVA 到 GPA,然后通过 HGATP 把GPA翻译到 HPA 这样。
课后练习尚未完成,嗯。
Hypervisior课后练习3
这里涉及 GUEST 的中断。主要涉及时钟中断,一种实现方式是当 GUEST 配置 RTC 的时候会陷入 Hyper,由 Hyper 来设置时钟,当时钟触发的时候,Hyper 会触发 GUEST 的时钟中断位这样。Hyper在设置完之后应该是会关中断的,待下次GUEST设置中断的时候再打开,这样保持一致。
课后练习尚未完成,嗯。
总结
这里总结的话,三阶段实在不完美,时间上太赶了,然后各种东西都想弄明白细节,这样就很来不及。原本还准备看RISCV的指令集虚拟化扩展这样,也只看了一小部分,内容上其实不算多,但要补的内容比较多,就比较费劲。