0%

2023秋冬季开源操作系统训练营第三阶段总结报告_邱沛中

第三阶段主要分成四周的学习。

第一周

第一周的学习主要围绕 arceos 的架构。在这基础上进行一些小实验。

第一个小实验比较简单,需要有彩色的输出。这只需要在 println 宏的字符串前后添加神秘代码即可。

第二个小实验是要实现一个 hashmap。这个的实现比较复杂,但是由于不是所有的方法都需要实现,而且网上相关的代码有许多,所以只需要从网上找一个 no-std 的实现,然后将其删改即可。另外,还需要实现随机函数,这里主要是根据时间来生成一个伪随机数。

第三个小实验是要实现 early 算法。这个算法的内容和实现比较简单。就是内存的两边一边分配字节,一边分配页,两者相遇即内存满。但是这个算法的回收操作不是很方便。

第四个小实验是解析 dtb。这个原本是一个复杂的工作,但是已经有相关库实现了。所以可以直接拿来使用。大大减少了工作量。

第五个小实验是把 fifo 改成抢占式的操作。这个实验要根据源代码 cfs 和 rr 的相关声明和实现来帮助理解。

总的来说,第一周的实验还是比较简单的,而且帮助我理解了 arceos 的相关架构。

第二周

第二周主要有六个练习,为最终的实习项目做准备。

练习 1 和练习 2 需要为 image 设计一个头结构,包含应用的长度信息,在加载时就能获取应用的实际长度。为了解决这个问题,我使用 python 将多个应用合并为一个 bin 文件,并在 bin 文件首部添加每个应用的长度和偏移地址。这样在读取应用时就能够按需读取。

练习 3 是要批处理方式执行两个单行代码应用,第一个应用的单行代码是 nop ,第二个的是 wfi。这个在前面练习 2 的基础上,循环读取每个 app 的代码,执行结束后就返回即可。

练习 4 是要实现 3 号调用 - SYS_TERMINATE 功能调用,作用是让 ArceOS 退出,相当于 OS 关机。这个的实现跟相对应的 SYS_HELLO 等调用方法一样。依葫芦画瓢即可。

练习 5 把三个功能调用的汇编实现封装为函数, 基于 putchar 实现 puts 输出字符串。这个我认为是整个第二周坑最多的一个练习。这个练习由于 puts 要输出字符串,所以需要一个链接文件将其放到指定的位置。另外,在內联汇编中,每次执行出来寄存器的值就变的不可预知,所以需要加上 clobber_abi(“C”), 来保护寄存器内容。

练习 6 实现一个应用,唯一功能是打印字符 ‘D’。 现在有两个应用,让它们分别有自己的地址空间。 让 loader 顺序加载、执行这两个应用。这个在练习 3 的基础上修改即可。需要修改的就是给每个应用都加一个页表即可。

第三周和第四周

最后的实验是要完成 linux app 的移植。这个我采用的方法是变更 elf 动态链接库,使其连接到自己写的函数中。我自己完成的部分是 elf 的解析,代码能够在没有开启分页的情况下运行 c 代码。由于我对 arceos 的分页有一定的误解,导致当分页开启时,原来的代码就不能正确访问和运行。后面当我看到郝同学的代码时,我发现他的方法跟我具有相似性,都是解析 elf,并将动态链接部分修改为自己的函数地址。所以后面我就跟着他的代码写了一遍,最终实现了功能。其中遇到的坑和解决方法如下:

  • 分页开启时,内核虚拟地址和物理地址之间是线性映射,但是中间有个偏差 axconfig::PHYS_VIRT_OFFSET,所以,之前所有的地址,比如 PLASH_START 就要加上 axconfig::PHYS_VIRT_OFFSET 才能正确访问到。
  • 每个物理地址除了内核的虚拟地址外,还可以根据三级页表建立映射。每个应用都建立一个三级页表,这样每个应用就都会有自己的地址空间,内存分配也会更加灵活。
  • 在 loader 的 Cargo.toml 开启 axstd 的 multitask 属性,可以正常编译,但内核 panic 退出,信息为 current task is uninitialized,定位到 axtask::task::CurrentTask::get() 函数。显然,在初始化时,已经设置了 current task,这里为何报错?经过进一步阅读源码,发现 current task 的指针通过 axhal::cpu::current_task_ptr() 获取,而 axtask/multitask 开启了 percpu 功能,也就是说此时的 current task 指针是一个 percpu 变量。目前,ArceOS 中通过 gp 实现 percpu,这与 riscv 的 ABI 是不符的。具体体现为 hello app 一开始就重新加载了 gp,把内核里维护 percpu 的值冲掉了。因此,为了保持 ABI,我将 percpu 改回了使用 tp 维护(前一个修改的 commit 将 tp 改为 gp)。此时在打开 axstd/multitask 后可以正常退出。(本段来自郝淼大佬的 github 文档)

总结

这一个月的生活还是非常充实的。没有独立完成最后的实习任务有点遗憾。通过这次的学习,让我对 unikernel 有了较为深刻的理解。也跟老师同学们学到了很多。希望这个活动以后越办越好。