0%

Rust OS 一阶段学习总结

@FinishTime: 2024-09-19 00:35:51

个人背景概述

我是一名软件工程专业的大三本科生,曾参与过 2023 年秋冬季训练营和 2024 年春夏季训练营。并且在 2024 季春夏训练营中,取得了通过的成绩。
今年的秋冬季训练营,我想冲一冲优秀,多做一些项目,多为国内操作系统和开源做出贡献。

今年一阶段学习时间表

  • 2024-09-18 大约 3.5 小时,重新完成 rustlings 的官方基础习题(前 94 题)。之后,又用了大约 2 小时,完成了后续的习题(95-110)。

一阶段学习内容概述

我按照“rust 语言圣经”上的讲解顺序,复习之前的知识点。
由于我在那段时间之前,在参加 InfiniTensor 的 AI 训练营,所以 rust 的基础语法等根本没有落下,这使得我在完成 rustlings 的习题时,几乎没有遇到什么问题。

只有到后续的习题中,我遇到了一些问题,主要是和 智能指针 有关的内容。这些部分我一直不是很理解的。但是因为我的经验,我还是能写出代码的。

总结

在第一阶段的学习中,我巩固了我所掌握的 rust 基础,我更是深深的意识到:Talk is cheap, show me the code!。实践是最好的老师!

Rust rCore 二阶段学习总结

@FinishTime: 2024-10-15 18:44:32

二阶段学习时间表

  • 2024-10-08 开始实验,轻车熟路,一天晚上完事了大部分的 ch3。
  • 2024-10-09 凌晨 ch3 debug 完毕,下午进行提交完毕。
  • 2024-10-09 晚上 ch4 提交完毕。
  • 2024-10-11 ch5 提交完毕。
  • 2024-10-13 ch6 提交完毕。
  • 2024-10-15 ch8 提交完毕。撰写报告完毕。

问题复盘

通过上面的学习时间表可以看出,我虽然是第二次做大实验,但仍然每一部分都需要 1 到 2 天时间。虽然不是将大段时间全部投入,但可能也是比较慢了。
以下是我实验过程中的问题复盘。

ch3: 本地测例初始化问题

在 ch3 里,我遇到了两个问题,第一个是本地测例的初始化问题。

由于我是第二次参与训练营,所以在实验时,我就直接去看指导书的习题部分,在尝试开始实现时,在 rust-anaylzer 的使用上时发生了错误,插件因为找不到某些依赖库,拒绝提供服务。

我去查看了插件的报错信息,发现找不到 user 目录。而我在源代码仓库,也确实没看到源代码里有 user 目录。后续,我在自行尝试解决无果后,去微信群问了助教。得到的回答是 看文档。然后我去重新看了文档,找到了一个下载 user 的方法。这个问题是由于我的粗心导致的。

ch3: 时间初始化问题

ch3 的第二个问题是,os 时间初始化问题。

在我实现的 os 里,在时间初始化,采用的是 直接在任务加载时便初始化为当前系统时间。这显然是错误的。但它的报错并不友好。

此问题的表现为:在直接指定 BASE=1 执行测试用例时,能够通过测试用例。指定 BASE=0 时,也能够通过测试。但指定 BASE=2 时,竟然不能通过测试!?同时,这种情况下,qemu 会出现“卡死”,让人一直等下去。

我通过细细阅读报错,发现 qemu 卡死是因为有一个用户测例 panic 导致的。经过分析的测例执行逻辑为:

1
2
3
我分析,ch3里,qemu在跑测试用例的时候,qemu的退出机制是,当全部测试用例成功时,才会退出。

如果有测例没过,他会接着执行别的测试用例,但最后全部测试用例执行完后,就不会退出了,还没有信息提示。

能够定位位置,之后的工作就要简单多了。我发现了问题所在,然后将时间改为 Option 类型,以 None 来初始化,在为某任务进行 sys_call 计数时进行判断,如果为 None,则进行初始化。这样,问题就解决了。

后续,我看到交流群里有个同学,和我遇到了一样的问题,于是,我也帮助了他。

ch6: 又一次卡了一小点

在 ch6 时,平心而论,要求实现的逻辑并不是很复杂,只是需要层层的传递,最后由“easy-fs”进行实际干活即可。但我再次遇到了一个极为恶心的问题。

在春夏季训练营,我曾经遇到了“File is shorter than the first ELf header part”的问题,翻译过来就是,文件比文件描述符还短。我发现,只有 ch5_spawn0,也就是 helloworld 的测例出现了这个问题。

而这一次,我又遇到了这个问题。然后,我通过微信群的聊天记录,找到了当时一位热心同学给我的解答,即,此情况可能是由于 rust 的智能优化导致的,只需要把一个位置,由 [u8; 512] 改为一个 Vec 即可。

还好我留有聊天记录,再次感谢当时的那位同学!

ch8: 系统时间系统调用未实现

来到了 ch8,我没有看到指导书文档里,写着的 “除 sys_get_time 以外”,所以,我就没有合并我之前的代码。但问题是,在我实现了 ch8 的内容后,进行测例,有两个始终过不去,就是那两个死锁相关的。而且,它没有报错提醒。这使我很懵,是不是我的银行家算法写的有问题?

在一次又一次地尝试为银行家算法进行 debug 后,我发现,好像我的算法没问题啊。于是,我又一次详细阅读测试代码,发现:这里面有个sleep(500),这是调了哪个 sys_call?我查了,发现是获得系统时间的那个。但我没有合并之前的代码,导致其会进入死循环。由此,我才改好了问题。

总结

第二次来 rCore,感觉熟能生巧了一些,之前很多不太理解的部分,已经慢慢构不成威胁了。果然,实践能够带动理论!但我也因为自己的粗心,导致了一些不必要的麻烦。这是需要我进行反省的。

展望

这一次的三阶段,将会涉及到“组件化”。希望我能尽快完成相关内容,及早进入后续项目内容的学习!希望能够冲一冲优秀!

Rustlings 练习总结

作为一名前端全栈开发者,刚开始学习Rust时,给我的感觉是没想象中的难度那么高,学起来还挺轻松…
然而,还是太年轻了,当我开始真的用起来之后,我发现,编译器成为了我前进的“恶梦”,它无时无刻不在教我做人,总是会在不经意间鞭挞我:“你,还很菜呢!”
所以,慢慢的,我开始放弃了保守的学习方式:“先将文档中的内容都大致学会,并记下来”,它让我一直停留在学习的舒适区,对学习Rust来说是很低效的。
Rustlings 其实就是一种很好的学习语言的方式,通过大量的几乎全覆盖式的Rust练习,在练习中不断遇到问题,并解决问题,就像打怪升级,心理上会不断的得到激励感,并不断的做下去。
通过Rustlings的110道题目的练习,其实也让我意识到了,自己以为的会使用Rust了,只是井底之蛙吧了。
练习的内容虽然都在文档中,只看文档学习的化,其实有时候会忽略这些知识的真实用途是什么,也就无法在实际项目中灵活的去使用了。
通过这段时间的练习,不一定让我在Rust的熟练成都有很大的提升,但是让我看到了一个Rust用法的大纲,这已经是一个很好的开始了。
希望在接下来的开发里,不断精进对Rust使用,当然,也不能只专注在语法本身,对操作系统本身知识的学习,会更重要,非常期待!

总结

在这之前做过官方的rustlings的练习,重做了前100题,只用了两天中的空闲时间,对于基础的语法和类型系统的相关性质已经相当熟悉了。
与本次练习不同的是增加了数据结构与算法相关的部分,虽然使用标准测试的方式进行代码审查会让数据结构的实现不够严谨,但是通过给出的工具函数进行实现也大致复习了一下数据结构的相关知识,熟悉了一下八大排序以及大根小根堆的处理方式,由于给出的目标实现函数的参数限制,使用非递归完成了很多数据结构的基本实现,这是之前从来没有过的。

遇到的问题

rust是一门内存安全的语言,因此我在编程过程中比较注重程序的内存占用以及与之有关的一些变量管理技术和方法,在做完练习之后感触最深的是有很多地方是因为类型选型的考虑步骤,导致必要时候还是需要进行多余的拷贝,或许有更好的处理这类优化问题的方法,但是目前对此还不够清楚,需要在之后的学习中不断深化。

新的思考

暂时没有,就是找这个blog的仓库脑子一下子短路了hhh,赶紧进行下一阶段吧,两个月挑战第二阶段 + 项目实现(危)。

第三阶段总结报告

没想到为期40天的第三阶段这么快就结束了。现在回想一下这四十天几乎每天都在准备考试和参加训练营两个任务之间时间片轮转。但是很庆幸我还是走到了训练营的尾声。

在第三阶段的项目六:基于协程异步机制的操作系统/驱动中,我了解到了我之前只是略有耳闻的异步,收获到了很多新知识。

首先我先阅读了《200行代码讲透RUST FUTURES》。在这里我又接触到了许多的新概念:绿色线程、Future、执行器、唤醒器。。。现在回想,看的过程实在是非常折磨,不仅要重新回顾本就不扎实的Rust语法,还要把多线程多协程带来的混乱执行流捋清楚。但是当我把它看完了之后,我才豁然开朗,我才明白Executor、Waker、Reactor、Future是如何协同工作以提高计算机的使用效率的。这为我后续阅读Embassy以及完成最终的成果打下了坚实的理论基础。

在《200行代码讲透RUST FUTURES》提供的理论知识的加持下,我进一步阅读了《Embassy Book》。当回到我最熟悉的领域时,总是让人心旷神怡。在阅读《Embassy Book》的全过程中,我并没有遇到太多的障碍(可能这就是专业对口的优势吧),但是我却无时无刻不在感叹Embassy作者对Rust语言的了解程度、他对丑陋的硬件近乎完美的封装以及Embassy向用户提供的友好接口。这让我觉得Embassy好像并不是一个嵌入式运行时库,而更像是一个开销更低的操作系统。

接下来就到了我觉得很有意义的部分。在这一小阶段,我对Embassy源码进行了逐步的调试,尝试理解Embassy的执行流。在Embassy中,有许多宏。Embassy的作者通过这些宏能够暴露给使用者干净、高效的接口,但是这对于我这种初学Rust,并且想理解Embassy执行流的人而言无疑是令人厌烦的。因为它隐藏了太多协程的细节(Rust语法对我而言也是很大的问题),所以我只能从头开始对Embassy源码进行调试。通过调试工具的帮助,我揭开了Embassy神秘的面纱:从创建执行器,到创建协程池;从创建主任务,到协程的不断循环执行。在此时我才真正领悟了Embassy的强大之处。

终于到了coding部分。我与我的队友一起分工合作,成功使用Rust重构了uC/OS II的线程调度模块,并在uC/OS II的线程支持下应用了Embassy的协程机制。这使得Embassy的执行器对MCU不在是独占的,而是可以基于线程的调度算法进行轮转。并且在线程的支持下实现协程,进一步提高了我们的系统的并发度。而且有了线程的调度机制,我们也可以实现不同线程的协程在任意点切换。

现在回想我在阶段三中完成的任务我才发现这些内容是多么地有意义。我了解到了Rust中的协程的实现机制,也了解到了Rust协程的一个实际应用——Embassy,最终我基于这些理论知识成功将Rust的协程使用起来,让我基本了解了一个能有效提高计算机系统性能的“秘籍“。

三阶段固然有意义,但是如果没有他人的帮助,我绝对坚持不到现在。

首先需要感谢我的队友。从三阶段一开始,我们就一直在交流关于Rust Future的各种细节。而到了调试Embassy的部分以及后面的coding,我们也都紧密配合。在调试Embassy的部分,如果没有他的学习成果,我很有可能就会被Embassy高级的Rust语法劝退;在coding部分,如果没有我们共同的努力,我们肯定不会在这么短的时间内使用一个我们都不熟悉的Rust重构一个操作系统的灵魂——线程调度模块,并且在这个基础上使用Embassy的协程机制。

最后还需要感谢向勇老师。在向勇老师的悉心指导下,我和我的队友明确了我们将来的目标。我们希望在堆栈池的支持下,实现线程内部的协程抢占。并且在这个基础上进一步完善我们在阶段三产出的阶段性成果,并将其应用在我们的四轴飞行器中。

再次回顾我的阶段三全过程,虽然我做事情不多,但是我仍收获颇丰。我相信不紧不慢依旧能走向未来。希望我今后也能保持着对异步、对Rust、对OS的满腔热爱,笃行不辍,履践致远。

转眼间训练营都结束了,这里大致总结一下我的第三阶段。

我第三阶段选择的课题是跨内核驱动开发,具体的要求,就是实现一个驱动,其能够在arceos和linux上运行,且其纯驱动代码部分不必修改。

一个设备在操作系统看来无非就是一些寄存器的集合,根据设备手册,我们可以知道如何通过修改和读取寄存器来控制和了解设备状态,对任何一个操作系统来说,都是如此,这也是纯驱动(也就是直接操作寄存器)代码不必修改的原因。

但是,对不同的内核,我们还需要不同的驱动适配器,比如,在linux中,我们需要向内核注册这一个设备(比如注册成杂项设备),并将设备抽象成一个文件,实现抽象的read和write方法。

此外,不同的内核地址映射的方法可能不同,在适配器中我们需要调用各自内核映射地址的接口,得到对应寄存器的地址,然后就可以使用纯驱动部分代码了。

在这个阶段里,我实现了一个GPIO点灯驱动,并在linux和arceos里成功运行,在这个过程中,知道了如何为linux编写驱动,rust是如何与kernel相配合的,以及在开发过程中由于踩各种坑被迫学习了一些关于linux kernel是如何编译的知识。

训练营结束之际,想感谢各位老师的帮助,各位同学的陪伴,也想感谢坚持到最后的自己。

学习内容:

rust异步编程,阅读200行代码讲透RUST FUTURES、rust圣经异步模块、embassy book,向勇老师的视频《并发与处理器、操作系统和编程语言》。

成果:

使用Tokio模块实现豆瓣图书爬虫。

学习心得:

通过阅读资料,学习rust 异步,针对IO密集型程序,异步相比多线程能够有较大的性能提升。rust 语言通过async和await 支持通过接近同步代码形式编写异步程序。rust 异步需要使用异步运行时,标准库并没有提供,常用的第三方库Tokio。
将异步移植到OS里面,遇到一些问题和困惑。1、ArceOS引入异步的话,是否只要引入异步运行时就可以;2、引入的运行时是否能用运行在标准库的运行时。
在继续学习,后续努力在ArceOS 网络模块引入异步时。

第三阶段总结报告

完成的独立工作

前期学习内容

  • 阅读《200行讲透Rust Future》(原文)
  • 阅读《Embassy Book》(原文)
  • 阅读《通过迭代 WebServer 逐步深入 Rust 异步编程》(原文)

项目实践内容

  • 首先由代码demo在debug单步执行中了解整个embassy的基础执行流程,关于executor和future部分有了自己的理解,于是产生了一个改进的思路,尤其是对于原来embassy的interrupt executor实现机制的改进思路
  • 由之前十分熟悉的UCOSII实时操作系统出发,进行功能裁剪和修改,适配到embassy的底层做一个线程运行时,让我们能够同时使用多个embassy thread executor,并且支持他们之间的优先级抢占,解决了原来interrupt executor方式支持的优先级抢占的两个问题:
    1. 优先级会占用中断,使得可用的外设中断减少,并且不灵活
      项目的实现做到了不使用额外中断,只需要pendsv和systick中断来维护线程的切换以及时间片优先级的调度
    2. 高优先级任务如果没有await的操作,也就是没有内部future会poll返回pending的话,其它低优先级任务不会得到执行,它会独占MCU/CPU,除非它被设计为timer wait一段时间来定时交出控制权
      这里我们的项目实现是通过stride算法(略有修改),实现优先级抢占的线程调度算法,一个executor会独自占用一个线程,从而赋予了其优先级以及实现了executor之间的抢占,而且通过stride算法的调度策略,避免了高优先级executor独占MCU/CPU
  • 调试工作的实施:学习到了rcore里面的makefile的思路,gdb配合openocd调试,通过读反汇编代码以及查看内存情况,解决了代码过程中的一些比如栈溢出,莫名进入hardfault,时钟频率不对等的bug

一些体会与收获

感觉前期的学习思考是最关键的时期,两周的时间都是花在看文章,跑跑demo程序,然后理解,思考,没有前面对于rust异步机制的理解,后面阅读embassy的代码肯定是无从下手的。中期的embassy部分,我觉得真的很需要一个真实的板子来跑它的示例程序,这样一步步调试着学习,能很快的明白整个流程,如果只是单纯看着代码做的话,很难把握整体的流程,而且调试过程中可以对embassy里面代码做修改,来查看你猜想的结果,这样对于细节处理的地方也能搞清楚,因为对于我这种情况(从训练营才开始接触rust语言)来说,单纯只读代码不运行,不调试,挑战性会很大,这是我自己学习embassy过程中的一点体会吧。

还有对于后期项目开发的方式,我也有一些收获。首先是合作的开发方式选择,最开始和我的队友的确打算分branch分模块开发,但是,对于这部分硬件相关部分(并不是开发一个后端或者前端项目),而且还是在项目起草初期,我们很难把模块分得很细,而且中途更改会很多很频繁,所以我们选择现在很成熟的实时协作方式,在vscode上利用live share插件。到了开发中后期的项目debug以及调试模块的部分,的确可以分branch分模块开发,我们的流程基本是这样。然后是在嵌入式单片机调试方面的一些收获和经验吧,我在调试过程既用到了图形化调试,也用了gdb+dashboard的方式,我觉得这两个方式都很重要,很需要。在单片机上开了gdbserver过后,gdb命令行调试的反汇编查看效果,以及单汇编指令执行过程的效果看起来更好,能够在一些关键的bug查看上很方便(比如查看栈溢出的情况,看看是否溢出到把一些全局变量的内存单元改变了,还比如查看一些hardfault的进入原因,在我们用的cortex-m系列里面,它会把描述信息压入任务栈(也就是psp堆栈),和riscv里面是放入在特定的寄存器里面,主要是查看进入异常的pc以及lr还有当时处理器的状态(xpsr),这样来debug),而图形化的调试更简单,能快速的查看一些其它小的bug,并且可视化查看代码也更方便,所以我的推荐是两个都使用。最后想谈谈我在移植UCOSII线程模块到embassy的项目的思考吧,其实观察到embassy这边的任务是一个统一的栈空间,而纯的线程支持的话就会是让每个线程独占一个栈,这样会导致栈的划分肯定是有内碎片的,所以统一的栈空间能让这些任务对于栈的利用率更好,一个任务的开销就变得更小,我们跑出来的测试效果也显示出在混合线程的情况下,任务的切换情况以及开销是优于纯的线程,只是和纯协程的情况几乎相同(在线程的切换systick较高10ms这种情况下),后续也想看看如果把线程和协程的混合更彻底,是否能真正做到无缝的task切换(目前实现方式的“缝隙”是源自于线程切换走后时间片的10ms会让另一边被切走的executor阻塞较长时间)

总结我的整个训练营的体会

开始

我刚好是在4.7号训练营开营的时候报的名,在刚开始做这个训练营的任务的时候,我就觉得这些lab的设计真的很好,使用官方的rustlings这种资料以及rcore实验和详细的参考书。我因此变得很有兴趣并且毫不犹豫的分享给我身边的同学和室友,让他们也来参加。事实上我能有机会在这里发言,离不开在训练营当中和室友之间相互学习,讨论分享,这些让我学到了更多,做出了更好的成果,这也是我训练营里面额外的收获。

中间

在前两个阶段的实验是对于操作系统的实践学习部分,很好的弥补了我在课内学习操作系统所缺乏的实践不足,这里我想引用LINUX之父说的一句话来说明这部分对于我学习操作系统的重要性: Talk is cheap. Show me the code。在这个过程里面,我慢慢的习惯上了命令行调试,自己也开始写脚本来做自动化的流程,从前不那么熟悉的makefile、shell脚本还有gdb都习惯起来了。不过不管怎么说,debug的过程还是有些煎熬的,遇到隐蔽的bug,还得看看内存或者看看寄存器。但是做实验的时候真的是干劲十足,那几天里面,每天就是起床,然后写lab,每过一个实验就去看看排行榜上排名提高了多少,正好就在5.1劳动节快结束的那几天把lab实验部分做完了,后面我就重新去看了rcore-tutorial book并且把它上面的思考题给做了,真的觉得这本书写得很不错的,是我入门操作系统看的第一本书,给我的印象很好。

结束

然后就是参加项目6的过程了,最开始选择这个项目就是看到了embassy,查到它是一个嵌入式的异步框架,自己感兴趣的是在嵌入式方面,而异步这个内容对我是一个新鲜未知的东西,于是我就更有兴趣来从项目中学习新的内容,也有一点信心能做出点什么。但是完成这个项目学习后,我发现我的收获不止于这些内容,因为作为大二的学生,其实我就逐渐开始迷茫于自己到底要做什么了,特别是当我做了一些经典的lab比如:csapp、cs144以及rcore,并且也学了编译原理、数据库、计网、操作系统还有一堆的数学基础之后,我不知道自己能做出什么东西。我只知道自己对计算机的体系结构感兴趣,还想做与嵌入式相关的内容,但是该从什么地方切入,怎么才算作是做研究,我一点头绪也没有。而在参加项目6这6周的时间里面,它更重要的是给我打开了一个小的缺口,让我知道在操作系统这个广阔的领域里面可以接纳许多不同的诉求,许多学到的内容以及想要应用的场景都可以和操作系统相关联,或者说操作系统和许多地方都能联系起来。在我和我的队友这一个多月的项目所做的成果中,我们在研究异步操作系统的同时,也给我们之前在学校里面所涉及的四轴飞行器的项目优化有了思路,对于如何做研究,怎么做也有了认识,在我自己的目标里面,我是希望能做一些虽然长期且有挑战,但是很有意义的事情。而这次的经历,就让我知道在我本科接下来的时间里面可以在什么领域做工作,并且这些工作是极具挑战且很有意义的。

感谢

最后我想感谢和我一起工作的队友:施诺晖同学,我和他是室友,做这个项目和他交流学习中收获很多,后续开发过程中和他的合作让我能够高效的完成工作。

还有很感谢向老师很耐心的指导而且老师也没push得很紧张,让我能在学校考试和训练营项目的交替中成功的都分得了合适的时间片完成任务。

第三阶段:R4L&跨内核驱动框架

只完成了前三个练习,后面各种大作业+期末考试没时间完成最后一个练习,还是比较可惜。

整个阶段3下来收获还是挺大的,在学校的操作系统大作业里只写过简单的字符设备驱动,在这里才真正把嵌入式、外设等结合进来。

  • 了解了跨内核驱动框架的含义和意义。
  • 了解了linux内核模块,设备树,设备IO(内存映射)等。
  • 阅读树莓派数据手册,完成了跨内核框架pure driver层和adapter层关于bcm2711的gpio驱动编写。
  • 对驱动和嵌入式有了更深入的了解。

继我的训练营第 1-2 阶段总结之后,终于结束了第 3 阶段。

这里的内容与我的幻灯片《阶段 3 学习总结 + 实现 rCore 异步定时器驱动》基本一致。

参加训练营以来的所有笔记,都在 https://zjp-cn.github.io/os-notes。

每周任务(均完成)

开发日志

学习笔记

其他工作

致谢

向老师是我遇见的最好的老师!感谢向老师在这 6 周时间里倾囊相授,尤其对我充满了耐心、热情和鼓励。与您所交流的一切,远远大于我在训练营里学习的所有知识,也是我所得到的最具价值的收获。

在最后一周,我其实在笔记本里,用笔写了很多很多页想告诉向老师的话,但最终没有开口。

我是一个很容易受到鼓励而逐渐喜欢一个知识领域的人。十四年前,我向英语老师请教的第一个问题,打开了我对英语的兴趣大门,在之后的八年直到人生再无英语课堂,我一直是英语课堂上最跃跃欲试的学生,也常常从第一节晚自习缠着英语老师请教各种问题,直到晚自习放学而不知疲惫。七年前,我发现自己能够自学编程,随后在参加各种数学建模比赛中,负责编程,在收获名次的喜悦中,发展起对编程的爱好,而编程这个兴趣持续到现在。我只是凭借自己的努力而缓慢地学习着,所以,当向老师您每周热情地给予我宝贵的学习机会,我内心充满了感激。我甚至在第三阶段的前几周里,就不止一次地梦见您成为了我的老师,当梦醒来,我就已经知足了。但即便我有时非常希望请教您,我依然不得不拒绝这些机会:我不想麻烦任何人,尤其不想浪费向老师的时间和精力 —— 费心去教一个操作系统领域之外的人,是不值得的事情;此外,我害怕因为向老师而发展起对这个领域的兴趣,维持现有的兴趣已经让我支付了巨大的机会成本和沉没成本。


感谢陶要仲同学将异步运行时放置到时间中断处理函数里面,这是完成向老师所提的第二个步骤的最重要的一环;也谢谢你在最后一周、连续三个晚上倾听我喋喋不休,最终我们愉快地合作成功。


感谢操作系统训练营里的每位老师!感谢第三阶段杨杰老师、赵方亮老师对我的帮助。感谢 rCore 教程的作者陈渝老师、吴一凡老师(如果我了解错误或者仍有遗漏,请告诉我 ta 的名字)。rCore 教程是我阅读操作系统领域的第一本书,它带我领略到操作系统环环相扣的精巧设计,是我能够完成第二、三阶段非常重要的基础。


感谢全球的、开放的、包容的 Rust 社区!我所有的 Rust 知识来自 Rust 社区,来自我所阅读的每一篇文档、每一个博客、每一条帖子、每一则帮助、每一处讨论……是社区的力量,让我发现了新的兴趣,培养了新的习惯。没有积累充分的 Rust 知识储备,我不可能参加这个训练营,也不可能轻松阅读每一行 rCore、embassy 里的 Rust 代码,更不可能在训练营里与各位结识,最终走到现在。