Skip to content
On this page

rCore实验记录

rust语言学习

rust语言已经接触比较久了,所以rustlings只是简单做了一小部分. rustlings作为一个工具考试工具还是不错的. 以后可以在此基础上做一些小工具.

实验记录

下面说一下做实验的过程, 操作系统已经学了很多年了,一直也没有上手的机会.现在有了qemu这样的工具, 可以完全脱离硬件来学习os. 当年学校minix的时候,都要用vmware这样的工具才行. 相比之下,qemu依赖要少得多.我看不少同学都做了可视化的调试工具. 基于qemu,我知道可以用gdb调试内核. 刷ctf题的时候调试过, 现在有了vscode这样的超级定制款ide,确实把os开发的门槛降低了很多.

我主要做了四个实验,分别是os3,os4,os5,os6. 由于时间关系,os8就没做了,来不及了.

实验os3

这个实验是练手熟悉系统用的,实现两个简单的syscall,分别是get_time和sys_task_info. 尤其是像get_time这种高频的系统调用,目前都已经不在通过系统调用来实现了,都是通过vdso. 实验中可以看到每次系统调用进出,要保存很多东西,退出还要复原很多东西. 这还是在os3这种没有分页支持,如果加入了分页,系统调用的成本就更高了.

下面给一个目前ubuntu上 vdso的一个样例:

txt
vagrant@ubuntu-bionic:~$ cat /proc/self/maps
563beb95e000-563beb966000 r-xp 00000000 08:01 13                         /bin/cat
563bebb65000-563bebb66000 r--p 00007000 08:01 13                         /bin/cat
563bebb66000-563bebb67000 rw-p 00008000 08:01 13                         /bin/cat
563bed3d5000-563bed3f6000 rw-p 00000000 00:00 0                          [heap]
7fb1c6c63000-7fb1c6c85000 rw-p 00000000 00:00 0
7fb1c6c85000-7fb1c6df8000 r--p 00000000 08:01 7839                       /usr/lib/locale/C.UTF-8/LC_COLLATE
7fb1c6df8000-7fb1c6fdf000 r-xp 00000000 08:01 2243                       /lib/x86_64-linux-gnu/libc-2.27.so
7fb1c6fdf000-7fb1c71df000 ---p 001e7000 08:01 2243                       /lib/x86_64-linux-gnu/libc-2.27.so
7fb1c71df000-7fb1c71e3000 r--p 001e7000 08:01 2243                       /lib/x86_64-linux-gnu/libc-2.27.so
7fb1c71e3000-7fb1c71e5000 rw-p 001eb000 08:01 2243                       /lib/x86_64-linux-gnu/libc-2.27.so
7fb1c71e5000-7fb1c71e9000 rw-p 00000000 00:00 0
7fb1c71e9000-7fb1c7212000 r-xp 00000000 08:01 2238                       /lib/x86_64-linux-gnu/ld-2.27.so
7fb1c7233000-7fb1c7264000 r--p 00000000 08:01 7840                       /usr/lib/locale/C.UTF-8/LC_CTYPE
7fb1c7264000-7fb1c7265000 r--p 00000000 08:01 7845                       /usr/lib/locale/C.UTF-8/LC_NUMERIC
7fb1c7265000-7fb1c7266000 r--p 00000000 08:01 7848                       /usr/lib/locale/C.UTF-8/LC_TIME
7fb1c7266000-7fb1c7267000 r--p 00000000 08:01 7843                       /usr/lib/locale/C.UTF-8/LC_MONETARY
7fb1c7267000-7fb1c7268000 r--p 00000000 08:01 7837                       /usr/lib/locale/C.UTF-8/LC_MESSAGES/SYS_LC_MESSAGES
7fb1c7268000-7fb1c7269000 r--p 00000000 08:01 7846                       /usr/lib/locale/C.UTF-8/LC_PAPER
7fb1c7269000-7fb1c726a000 r--p 00000000 08:01 7844                       /usr/lib/locale/C.UTF-8/LC_NAME
7fb1c726a000-7fb1c726b000 r--p 00000000 08:01 7838                       /usr/lib/locale/C.UTF-8/LC_ADDRESS
7fb1c726b000-7fb1c7406000 r--p 00000000 08:01 7834                       /usr/lib/locale/locale-archive
7fb1c7406000-7fb1c7408000 rw-p 00000000 00:00 0
7fb1c7408000-7fb1c7409000 r--p 00000000 08:01 7847                       /usr/lib/locale/C.UTF-8/LC_TELEPHONE
7fb1c7409000-7fb1c740a000 r--p 00000000 08:01 7842                       /usr/lib/locale/C.UTF-8/LC_MEASUREMENT
7fb1c740a000-7fb1c7411000 r--s 00000000 08:01 5046                       /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache
7fb1c7411000-7fb1c7412000 r--p 00000000 08:01 7841                       /usr/lib/locale/C.UTF-8/LC_IDENTIFICATION
7fb1c7412000-7fb1c7413000 r--p 00029000 08:01 2238                       /lib/x86_64-linux-gnu/ld-2.27.so
7fb1c7413000-7fb1c7414000 rw-p 0002a000 08:01 2238                       /lib/x86_64-linux-gnu/ld-2.27.so
7fb1c7414000-7fb1c7415000 rw-p 00000000 00:00 0
7ffc16f28000-7ffc16f49000 rw-p 00000000 00:00 0                          [stack]
7ffc16f93000-7ffc16f96000 r--p 00000000 00:00 0                          [vvar]
7ffc16f96000-7ffc16f98000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

vdso 作为一种灵活的,系统提供给用户层的代码,像get_time这种高频调用,成本就低多了.

这个实验碰到的比较坑的一个问题是,get_time因为模拟器的原因,并不准. 最后采用了一个很扯淡的方式,直接加了50来应付单元测试. 其实应该深入看看为啥模拟器的时间会飘.

os4

这个实验目的是让人理解基于页的内存管理的实现. 首先第一点,就是因为引入了虚拟内存管理,进程之间可以使用完全相同的地址空间,不像os3中还需要精心布局. 不过话说,在资源受限的嵌入式系统中,os3这种任务管理方式未必不是一件好的方式. 分页以后,复杂度一下子复杂了不少.

其次, sys_mmap的实现,这个相对比较简单,因为已经有参考实现了,insert_framed_area已经是现成的了. 只是自己实现一下is_mapped即可. 关键就是理解MemorySet的结构.

有了is_mapped,相对来说,remove_framed_area的实现,也就不复杂了.

os5

相比之下,os5要比os4简单许多,因为进程调度:

  1. 进程调度算法本身比较简单
  2. 这是每一门操作系统课程会反复强调,甚至会让学生计算的一章.
  3. 环境都已经准备好了,fork,exec的实现都已经提供了剩下的,只是照抄,组合一下即可.
  4. stride调度算法,本身实现起来很简单,只要找对地方,在进程发生切换的地方记录一下pass即可.

os6

文件系统本身比较复杂,不过还好,拿到手的已经有了最核心的基于block设备的inode管理与分配. 比如Bitmap这样的数据结构.

总的来说,依葫芦画瓢本身不难,很快就有了,主要碰到的问题,有两个,一个是easy_fs println的支持,二是发现clear偶尔失败的问题.

easy_fs 支持println

easy_fs这个模块比较特殊,它既要在内核层工作,也要为easy_fs_fuse服务. 所以它本身没有任何log,为了让它支持log,这里用了一个很偷懒的办法,就是函数指针. 让用户层提供用户层的函数指针,内核层提供内核的函数指针,从而都可以println!. 指导书中建议引入log,我对此没有研究,后续有时间可以再看看. 这里还有一个题外话,就是因为忘记在memory中映射MMIO,还以为是函数指针设置的问题,反复测试,浪费了不少时间,主要是方向错了.

clear偶尔失败的问题

这个问题很扯淡,跟踪调试了很久,也不断的看代码,觉得不应该有问题. 最后再读写block的地方打印日志找到了问题,原来是写进去以后读出来就不对了.

这下算是卡住了,在吴一凡同学的提示下,说是跨页问题. 就爬了爬rCore的issue,直接找到了答案. 用Vec来代替数组,因为Vec分配的时候是对齐的,避免了数据的跨页问题.

os8

经典的死锁解决算法, 平时基本上也用不到,加上确实时间来不及了,就战略放弃吧.