0%

第一阶段的总结

  1. 初识 Rust:语法与基础
    Rust 的语法相比其他系统编程语言更为严谨,并且采用了一些新颖的设计,比如所有权(Ownership)和借用(Borrowing)模型。通过学习变量、函数、控制流、数据结构等基础内容,我逐步熟悉了 Rust 的编程风格。同时,Rust 强制规定的编译检查(如变量不可变性、类型检查)帮助我在早期阶段就养成了良好的编程习惯。

  2. 内存管理与所有权
    Rust 的核心设计之一是其所有权系统,这也是区别于其他语言的最显著特性之一。在 Rust 中,每个变量都有一个所有权(Owner),变量的作用域结束时会自动释放资源,无需手动管理内存,这有效地避免了悬空指针和内存泄漏问题。在学习过程中,我对以下几个概念有了深刻理解:

所有权(Ownership):每个数据在同一时间只能有一个所有者。
借用(Borrowing):通过引用传递数据,允许在不改变所有权的情况下访问数据。
生命周期(Lifetimes):通过生命周期标注来管理数据的引用,使得引用的使用更加安全。
所有权系统让 Rust 在没有垃圾回收的情况下也能有效管理内存,学习这些概念后,我对资源管理的意识有了显著提升。

  1. 错误处理
    Rust 的错误处理也与众不同。相比传统语言使用的异常机制,Rust 使用 Result 和 Option 枚举来处理错误和可选值。这种显式的错误处理方式让我更加清楚地理解了每个操作的潜在风险,并培养了防御性编程的习惯。此外,通过使用 ? 操作符,代码变得简洁易读,让错误处理不再显得冗长。

  2. 并发编程
    Rust 在并发编程上的独特设计使其特别适合编写高性能的并发应用。Rust 的所有权模型通过在编译期检查数据竞争,有效防止了多线程中的常见问题,比如数据竞争、死锁等。Rust 提供的 std::thread、async/await 异步编程模型等工具使并发编程更加安全且易于管理。在学习中,我掌握了创建线程、共享数据的方式,并学会使用 Mutex 和 Arc 等工具来处理复杂的多线程情况。

  3. Cargo 和生态系统
    Rust 的包管理器和构建工具 Cargo 是 Rust 生态系统的重要组成部分。在学习中,我逐步熟悉了如何使用 Cargo 创建项目、管理依赖、构建和测试代码。Rust 拥有活跃的社区和丰富的库资源,通过使用第三方库,可以快速实现许多功能,这大大提升了我的开发效率。

  4. 实践中的收获
    理论学习之后,我通过一些实际项目(如简单的 Web 服务、命令行工具)将所学知识应用于实践。Rust 对类型和内存的严格检查在初期可能显得繁琐,但这些设计让我更清楚地理解代码结构,减少了运行时的错误。实践过程中,我逐渐感受到 Rust 的优势:高性能、可靠性和低级控制,使得 Rust 成为编写高效、安全应用的理想选择。

    第二阶段总结

在第二阶段的学习中,我逐步深入理解了操作系统中各个功能模块的实现。这一阶段的主要任务是通过阅读和理解代码的层次结构,来掌握系统的整体设计。在初期,我对项目代码结构的理解较为模糊,但随着长时间的代码阅读和实践,我总结出了一些行之有效的阅读和理解方法:

  1. 分层次理解代码:我发现从高层抽象入手去理解代码结构更为高效。首先理清最高层的抽象层次,理解其核心功能,再逐渐深入到底层实现的细节。在实验中,掌握和利用高层抽象往往能够帮助节省大量时间。

实验总结

实验一:基础系统调用实现

实验一的难度较低,因为此阶段还未实现页表管理,内核可以直接访问用户态的地址空间。实验中,系统调用的返回值可以直接使用内核中的数据。此外,通过高精度的 get_time_us 函数获取时间,能够确保后续实验中通过测试。为实现系统调用计数,可以在 syscall 函数调用时更新计数,而首次调用时间则需在 run_first_taskrun_next_task 中进行设置。

实验二:引入页表管理

在实验二中,由于实现了页表管理,系统调用设计需要改写。内核在处理传入参数时需找到实际的物理地址,并在该地址上读写数据。同时,本次实验引入了两个新的系统调用 mmapmunmap。因为两者涉及一段连续地址,因此在设计中需确保连续地址的映射状态(已映射或未映射)。最后,mmapmunmap 分别负责插入和删除页面。

实验三:进程管理与调度算法实现

实验三的任务是实现 spawn 系统调用和 stride 调度算法。spawn 不能简单地视作 fork + exec 的组合。在实现前需理解 spawn 的语义,然后从 fork + exec 中提取相关代码,使得 TCB 数据结构达到所需的状态。stride 是一种进程调度算法,实现较为简单,按照教程提供的步骤逐步操作即可。

实验四:兼容性与文件系统操作

实验四相较之前难度有所提升,需要保证现有代码的兼容性。一开始 spawn 存在兼容性问题,导致错误频发,最终通过重新实现 spawn 解决了兼容问题。此外,死锁处理是本实验的重点之一。由于某些函数(如 findcreate)对文件系统上了锁,因此这些函数不可直接调用,但可以通过拼接部分代码来实现所需的功能。unlink 目录项的删除操作则需更新目录项,实验中借鉴了其他同学的思路,通过删除原内容并复制新内容实现目录项更新。

实验五:死锁检测算法实现

实验五主要任务是实现死锁检测算法,该算法类似于银行家算法。教程中的描述较为简略,许多细节需要在实现中仔细推敲。semmutex 的实现大致相同。此外,sys_get_time 必须实现,否则某些实例可能会引发死锁。


整体总结

本阶段实验的概念和难度相对基础,更多的挑战在于使用 Rust 编写操作系统。对于首次接触 Rust 的我来说,这种体验充满新奇,同时也加深了我对 Rust 的理解。Rust 强大的内存安全和所有权模型,在系统级编程中尤为适用

来跟大伙唠会

你们好,我是清朝老兵。

我是catme0w,你可能已经在排行榜见过我的名字了。

去年这个时候,我已经来过一次了。为什么我会再来一次?马上揭晓答案。

在继续之前,你可以先看看我上一次留下的记录:https://rcore-os.cn/blog/2023/11/14/2023开源操作系统训练营第二阶段总结报告-CatMe0w/

和上次一样,我的日志里不会有太多关于技术的流水账。如果你想看一个不错的故事,那你来对地方了;如果你还在rCore中挣扎,正在寻找一些攻略,那么我也不会让你白跑一趟,接好了:

写在最前,太长不看攻略:你可能需要的建议

  1. 强烈建议关闭Copilot或Codeium等代码补全器,它们完全无法理解内核,对于系统编程几乎一无是处;它们产生出的代码会浪费你非常多的时间。
  2. 小黄鸭调试法或许会有帮助。
  3. 在你100%理解代码库之前,你是做不出题目的。相信我。
  4. 完全理解代码库之后,先构思好你准备要做的东西,否则出场即大改。
  5. 做好因方向错误而大改的准备。
  6. 小心#[repr(C)]。
  7. rustsbi-qemu可能非常不稳定,遇到灵异现象?换个QEMU版本试试;如果你在用Apple Silicon,也换台机器试试。
  8. gdb或许很低效,多插trace!()。

未讲述的往事

如果你看了上边我参加去年训练营时的记录,你应该已经了解到,当时,我在ch5就停滞下来然后退出了。我其实内心是有些后悔的,觉得自己当时并没有看起来的那么忙,是自己的懒惰造成不得不退出的结局。

纵使rCore从不是什么大事,我还是觉得“抬不起头”,之后也从未和别人再提起这个精致的小操作系统,就好像未曾发生过。

我会自己写一个操作系统吗?也许永远不会吧。就当是一场梦。我这么想。

次年夏天。

在一个非常非常偶然的机会,我了解到我的一位朋友参加了今年春季的训练营。而我们之间最大的不同,便是我自始至终都只抱着“60分万岁”一般的态度,指望着能混到晋级就是胜利,而他从一开始的目标就是榜一。

我仿佛挨了一记响亮的耳光:做不到,只是因为不想。

所幸,训练营从不禁止清朝老兵(而且现在还越来越多了!),我有机会为rCore重新补上一个完美结局。

rustlings II

我也是Rust老兵了,这100题,不是把我去年的补丁直接合过来便是?

然而从今年开始,rustlings多加了十道算法题,成了110题。清朝老兵遇到了它的第一个障碍!

我了解Rust的脾气,知道什么数据结构得开unsafe,只是我已经很长时间没有摸过算法题了,更没有用过Rust写算法题,一打开,满屏的泛型让我虎躯一震。

这需要一点时间……不过,仍然只是小插曲。让我们直面恐惧,走向rCore吧。

rCore II

实不相瞒,我去年压根没看懂rCore的教程,我的确是一路完成到了ch5,可除了ch3以外,我自己都不知道我写了什么东西,我的代码以一种连我自己都无法理解的方式运行起来,大概纯粹是碰对了测例而已。

往好处想,将功补过的机会来了;往坏处想,这相当于一点经验都没有了!

ch3

没有什么困难,凭借着清朝时期的记忆,我还记得ch3要在哪里加上什么。迅速结束战斗。

ch4

我至今都认为,ch4内存管理这一章是整个rCore难度最大的部分,至少在知识上是如此!题目本身反而是不那么难的,代码量不大,理解内存管理的情况下应该很快能找到正确的路;可如果对内存模型有哪怕一丢丢一知半解,你都会在ch4的题目上拥有一段不愉快的时光。

嗯,我承认我是后者……

ch5

ch5做的是什么来着?我又给忘了。它就是如此没有存在感,前后的ch4和ch6都是令人大叫的程度,相比之下,ch5就有些难以给人留下深刻印象了。

哦想起来了是spawn。好像还真没有太多可以说的,在经历过前两章的毒打之后,你应该能很快处理完这一章。

ch6

我抵达了我之前没能征服的地方,硬骨头来了。至此,rCore才真正展现出它的威力,BOSS进入二阶段了!

ch6在知识上稍轻松些,但题目的难度却和教材文本完全不成比例。题目的难点在于非常的绕,为了改动某个功能,你可能需要前后在许多不同的地方修改一连串代码,更不必提这个在内核之外的模块很难调试。

到这里,有件事我一定要骂:用Rust编程,有时真的很让人沮丧。

人们总是说“编译期拦住错误比事后debug更好”,可是只有自己才知道,用Arc啊,Mutex啊那些东西的时候有多令人不快:它明明就在这里,就是这么直观,但就是不让你用。

当天中午11点,我已经几乎完成了ch6的功能实现,我想着,ch6通过就去吃午饭。

你能猜到这样立flag将会如何发展。

晚上11点的我:😅

ch8

就快结束了!最开始看到ch8教程的时候,我心中窃喜:这么简单啊!就好像回到了ch3一样!

题目要求也很简单,甚至详尽地描述了要实现的每个细节。

上机开写,很快就通过了互斥锁的测例,信号量的第一个测例也很快通过了。只剩最后一个测例了,马上就可以结局了!

然后我就在这“最后一个测例上”卡了十多个小时。🌚

要么是第一个测试不过,要么是第二个,两个测例呈现出完全不可理解的结果。可是无论我怎么检查自己的代码,在能插的地方都插满了log,发现每个步骤确实都与预期中的相符,可为什么结果就是不对呢?我也没有理解错题意啊?

“我也没有理解错题意”……等等。

会不会真是我把题目弄错了?我会不会实现了一个错误的算法?

走投无路之下,我开始直接写这个算法的证明,很快发现,(按我理解的)这个算法根本无法检测任何死锁!

题目在算法的描述上,确实有些混乱和容易误导人。我意识到我该寻求外部资料了,到此时,我才终于知道这就是Dijkstra的银行家算法。

在查看了这个算法的精确描述后,我才终于弄明白我出问题的地方:need矩阵,只减不增,更绝非我最初理解的从0开始增长。

终于,所有测例都能通过了,ch8结束了,可是我却有些哭笑不得,因为这么愚蠢和缺乏技术含量的原因卡住,感觉自己有些像是被戏弄了。

rCore EnCore

感谢你还有耐心看到这里。也许你已经留意到,我在今年的排行榜上是榜一。

二阶段classroom放出之后,我使用的是补丁方式提交,因而在补丁里还留有当时的实际完成时间:9月21日。

在我完成rustlings之后,也就是今年报名刚开的那个周末,我把整个周末的全部时间都投入到速通rCore上了。

不为别的,我就想给去年的自己一个耳光。

尾声

做rCore实验的过程,前半程就是一筹莫展:你看了半天文档,感觉自己好像会了,一打开代码却发现两眼一抹黑,根本不知道在干什么。

然后你花了很长时间终于大致摸清楚代码结构了,一准备开始写,发现还是一筹莫展,都不知道要写什么东西。

过了不知道多久,终于你有了一个差不多的思路,可以开始写了,此时,你来到了后半程。

你可能用了不太长的时间就写出来,也可能很久,但一定是跑不起来的。

接下来你会用几十个小时的时间去调试,gdb或者插满trace!()。

然而即使用了gdb也没有太大帮助,因为错误往往出现在你想象不到的地方。

有的时候你会发现测试用例能过,甚至是随机性可以过,有的时候会发现QEMU直接一动不动——rustsbi-qemu并不稳定。

当你第一次发现测试能过的时候,哪怕只是概率性成功,你以为自己离胜利近了,但你很快,或者很久以后才发现,方向完全错了,你必须全部推倒重来。

这种事将会反复发生。

最后,你离最终的目标越来越近,但每次都总差那么一点,你不禁怀疑自己是否真的在接近终点,还是又一次搞错了方向。

到了最后,你可能都记不清这中间到底推翻了多少次,改了多少个细节。你不再期待一蹴而就,而是学会了享受每次解决一个小问题的成就感。

不管失败多少次,推倒多少次,你已经比一开始那个两眼一抹黑的自己强太多了。也许方向错了,代码推倒重来,但至少,你知道自己在进步,哪怕每一步都很艰难。最终你会发现,这些反复挣扎的瞬间,才是整个过程里最有价值的部分。

尾声之后

“我会自己写一个操作系统吗?”

会的。

我回想起一件事,是在我很小的时候发生过的,我已经很难分辨究竟是什么时候了。

人们在讨论《30天自制操作系统》这本书。

当时的我想,啊,这竟然是可能的吗?这本书一定很厚,很难懂……

在那时的我眼里,除开最终呈现给用户的应用程序,其余关于计算机的一切都是魔法烟雾构成的黑盒——麻瓜弄不明白。

但在这么多年之后,我终于明白,做不到,只是因为不想。

我们都可以。

训练营只是一个开始,在这之后,还有更广袤的世界等待着探索。

https://github.com/CatMe0w/attune

一、前言

毕业之后一直工作都是在做c嵌入式驱动开发,是和同事闲聊的时候了解这个项目的就开始学下去了 从2024春季就已经开始学了但是因为一些事情耽误了就没有完成
对我个人来说主要是好奇想看看经常看到的所谓写操作系统是做什么,以及我在工作中也会看到rust的一些内容对这个语言也很好奇

二、基础阶段

错误记录

在基础学习的时候我认为这样的错误记录是很有必要的
因为我基础并不好经常对于书上看到的方法看到就忘了 实际写起来我最大的感受就是
1、如果有多个数据结构嵌套我很容易就搞不清哪里要复制哪里不复制
2、闭包按照我理解就是一个函数指针 在传递闭包的时候一开始没理解总是搞不清楚到底是怎么回事

以及在完成一阶段之后我才安装了rust-analyer也算是踩坑了把 有这个插件才发现可以很快就发现错误不用在一次次编译

三、OS专业阶段

专业阶段在整个学习的过程中对我而言最困难的是第四章和第八章
在第四章 我觉得内存的边界条件卡住了很久 主要还是理解不够 明白了 地址空间 应用空间 需要转换
也看明白了虚拟地址的寻址方法 但是真的感觉新的文档没有图我一开始没理解的就是 他三级页表怎么寻址的过程
后面才真的理解上一级页表的值代表的是下一级要去找的page 再再下一级page上找到对应页表项 才实现了多级页表的寻址
三级页表

整体来说我觉得这个专业阶段 首先文档需要图
并且我发现只看一遍效果很差 需要边看边记 并且对于代码讲解最好是能够记录并且依次添加注释 明白每一步再做什么不是只看文档中的讲解就过一遍就结束
结构体关系
以及我觉得再实验过程中最重要的是搞清楚各个结构体所代表的实际意义
并且可以集中在图上展示 不然我在做ch6一开始总是错就是 没梳理清楚 disk_inode inode OS_inode还有目录项分别都是些什么 导致ch6经常找错
于是我就在草稿纸上根据理解写了我对于这些数据结构的理解和关联很快就解决了

实验的时候还有一点 有时候我总喜欢通过结构体之间的关系来找对应的数据 但是在rust里这样做我觉的很复杂
最好的方式就是在容易得到的结构体中保存需要的数据

第一阶段

第一个阶段是通过 Rustlings 练习 Rust 语言的基础知识,包括变量、函数、所有权等等。在这一阶段, 我更加深入地了解了 Rust 的一些基础概念,比如所有权、生命周期等等。在这一阶段的学习中, 我发现通过 debug 的方式来学习一门编程语言和熟悉其语法是非常有效的。这能够让我更深入的去思考一些语言设计的原因,而不是简单地记住一些规则。

在学习 Rust 的过程中,我发现 Rust 的所有权机制和生命周期机制是 Rust 的核心特性,也是 Rust 与其他语言最大的不同之处。这也是我认为 Rust 是一门非常有意思的语言的原因之一。所有权机制能够让我将每一个值看作绑定, 而非变量, 这种思维方式让我感觉非常新鲜, 也让我能够更多地将注意力放在程序的逻辑上, 而非内存管理上。生命周期机制则是 Rust 的一种保证, 保证了程序的安全性, 同时也能有效的避免 C/C++ 中的一些内存问题, 比如野指针、内存泄漏等等。

在后续的日常开发过程中,我应该会尝试使用 Rust 做为我的首选语言,因为我觉得 Rust 的所有权机制和生命周期机制能够让我写出更加安全、高效的代码。

第二阶段

第二个阶段是通过为 rcore 添加一些简单的 feature 来熟悉操作系统的基本概念和相关实现。在这一阶段,我更加深入地了解了操作系统的一些基本概念,比如中断、虚拟内存、系统调用、文件系统等等。在这一阶段的学习中,我发现通过实践来学习操作系统是非常有效的。这能够让我更加深入地理解操作系统的一些基本概念。对于模糊的概念,我可以直接看到它们的实现,这让我能够更加直观地理解这些概念,也能更深入的思考这些设计的原因。

在这一阶段,我的感受是操作系统是一个非常庞大的系统,其中涉及到的知识点和细节非常多,但同时操作系统对各组件的抽象也非常清晰。只要理解了这些抽象,将各组件串联起来也并不是一件非常困难的事情。

lab1总结

实验一相对简单。由于尚未实现页表,内核地址空间可以直接访问用户态地址空间,因此系统调用的实现只需直接返回用户态地址即可。此外,为了确保后续实验中的测试通过,建议使用高精度的 get_time_us 获取时间,避免因时间精度不足导致错误。至于统计系统调用次数,可以在 syscall 函数执行时进行更新。若需获取系统第一次调用的时间,则需修改 run_first_task 和 run_next_task。

lab2总结

与实验一不同的是,实验二中需要实现页表。因此,之前的系统调用逻辑需要重构。内核必须找到传递参数的实际物理地址,并在该地址进行读写操作。

此外,实验要求实现两个新的系统调用:mmap 和 munmap。这两个调用涉及一段连续地址,因此需要检查该地址范围是否完全未映射或已映射。mmap 负责插入页面,而 munmap 则用于删除页面。

lab3总结

在实验三中,首先需要将之前实验的代码引入本次实验。如果之前的代码有问题,本次实验的测试通常也无法通过。

本次实验的核心是实现 spawn 和 stride 调度算法。spawn 并非简单的 fork + exec,因此在实现前需充分理解其语义。可以借助 fork + exec 的部分代码来调整 TCB 数据结构至目标状态。

stride 是一种进程调度算法,由于本次实验示例较简单,按照教程步骤实现即可完成。

lab4总结

相比之前的实验,实验四的难度有所增加。首先,需要确保兼容之前的代码。一开始 spawn 的兼容性问题导致多次报错,最终通过重新实现 spawn 才解决。

此外,还需注意可能出现的死锁问题。由于某些函数(如 find 和 create)对文件系统加锁,不能直接调用它们,而是需要将其实现逻辑拷贝并进行调整。

最后是 unlink 的目录项删除操作。在找到目标目录项后,需要更新内容。采用了群里分享的方法:删除原目录项后,将内容重新拷贝过去。

lab5总结

实验五的任务是实现一个死锁检测算法,类似于银行家算法。但教程中的描述较为模糊,许多细节未明确说明。因此,需要仔细分析各个细节。sem 和 mutex 的实现思路基本相同。另外,sys_get_time 的实现必不可少,否则某些测试实例会出现死锁。

总结
总体来说,这些实验内容并不复杂,涉及的概念较为基础。但由于是第一次接触 Rust 并使用它编写操作系统,对我而言是一次新奇的体验。实验的主要难点在于 Rust 的使用上。此外,教程的部分内容并不完善,例如死锁检测算法的部分解释得较为模糊。若不是群里有人提到类似银行家算法,我很可能难以理解。因此,希望教程能进一步完善文档内容,帮助更多初学者。

前言

从零基础开始学习 Rust,通过 Rustlings 逐步入门。Rust 作为一门编程语言,以其优秀的内存安全和高性能设计而闻名,非常值得深入学习。

个人感悟

通过 Rustlings 学习了 Rust 的基础语法、数据结构、所有权和引用等核心概念,初步掌握了编写 Rust 程序的基本能力。在接下来的 rCore 学习中,通过实践进一步深化了对 Rust 编程的理解。这种从基础到实践的学习方式非常高效。未来还需要通过更多项目积累,不断提升 Rust 编程能力以及对操作系统的理解。

Rust 的所有权机制是学习过程中最具挑战的部分。变量所有权的转移、引用的使用,以及 Copy 和 Clone 等特性的分析,都需要反复实践和大量代码积累,才能在实际编写 Rust 代码时得心应手。

第一阶段

是我第四次学习 Rust 编程语言,第一次看 course.rs,第二次用 rust 刷 leetcode,第三次刷 rustlings,第四次再刷了这里的 rustlings。每一次学完后总是不知道学到了什么,脑袋空空,但实际上写的时候,体验在逐渐变好。可能还是留下了些什么东西,只是没有察觉到。

不知道这次留下的东西能持续多久。

Rust 很有特点,枚举变量和模式匹配相得益彰,所有权机制比 std::move 舒服了不少,生命周期让我似懂非懂,trait 很有意思,宏看不明白,并发和异步根本没看。

Rustlings 比官网多了很多东西,例如后面的算法题。数据结构那一块,实在找不到什么优雅的方式来实现移动,应该是学的东西太少了;前几个题目对 copy trait 进行了滥用。后面的倒是简单,不涉及什么 Rust 独有的特性,如果会用其他语言写,那么这里就会写。

相关资料:course.rs,the book,rust by practice

第二阶段

rCore 是一个的小内核,长得像 xv6,基于 riscv ISA 和 RustSBI。

前面几章的内容大同小异,主要是熟悉操作系统的基本概念,例如内存分页管理。文件系统章节需要对 easyfs 有基本了解,这一点通过阅读 guide book 可以达到。

死锁检测算法很新颖,是 xv6 里面没有的内容,一直以来,只学过算法,没考虑过要怎么把这个东西切实地加进内核里面,现在又多知道了一点点,还是有不少收获。

测试用例很弱,可以轻度 hack。

第一阶段总结

1年前开始学习rust,初衷是了解一门操作系统级别的开发语言(深入了解,作为工作语言那种)。并为此写了系列的微信公众号文章《拥抱未来语言Rust》并在社区取得了不错的反响,感兴趣的可以微信公众号搜索“三角兽”,欢迎关注。
rust作为一门系统级语言,拥有高性能的同时还具有高安全性,基于RAII风格自动资源管理,避免了很多内存安全问题(这也是吸引我学习的主要原因)。本次比赛是我第一次参加的系统级比赛,通过比赛,夯实了对rust语言的理解,包括:所有权,作用域,生命周期,智能指针等。非常有意义,在此感谢主办方!

第二阶段总结

一直以来对OS非常感兴趣,本次通过身临其境的“代码调试”,熟悉了整个项目架构,并对OS有了进一步的深刻认识。在调试过程中不仅熟悉了OS,还对Rust语言有了更深入的认识。第二阶段的操作系统实现基于RISC-V指令集,之前没有了解过RISC-V,因此看汇编语言会有些头痛,但结合RISC-V手册加上AI的辅助,理解这些汇编代码完全没有问题。
通过第二阶段的学习,破除了一直以来对操作系统底层实现机制的迷雾,那层隔阂在应用开发人员心底的对操作系统的朦胧自此打破,世界上没有彩蛋,只有认识盲区,破除这些盲区,就是扩大自己的认知,增加自己的技术自信。后面打算写系列的博客来总结、分享操作系统,影响更多的人来学习操作系统。

下面是第二阶段各个实验的总结,重要的知识点我都画成了流程图,希望帮到需要的人。

lab1

这是第一个实验,整体难度不大,通过print打印信息调试,一天过关。

lab2

地址空间映射这一章知识密度较高,反复看了几遍才基本弄懂,调试代码陆陆续续调试了3天。(还是太菜,菜就多练!)

简单总结下本章:在开启分页SV39分页之前,OS和都是直接访问物理地址,这给系统带来很多潜在的安全隐患,例如地址空间未隔离等。开启分页模式后,OS和用户代码中就都是虚拟地址了,需要通过页表和MMU进行转换,并且页表上的属性区分出了U和S,进行了权限和空间的隔离,分别在特权级和地址空间上保证了OS内核的安全,同时也保证了用户程序之间相互隔离,彼此空间不会重叠。(虚拟空间可以重叠,但通过页表映射后通常是隔离的,有种特殊情况是通过映射到相同的物理也实现内存共享)

另外为了OS在开启分页后能平滑的访问,对于OS采用的是恒等映射(虚拟页号=物理页帧)。而对于用户程序通常采用Framed映射,通过栈式页帧分配器分配页帧并和虚拟页号建立映射关系,动态生成页表及页表项,实现物理页帧的按需分配。

另外一个比较好的抽象是地址空间MemorySet,它作为任务的一部分,管理着页表及和逻辑区。在实现采用了RAIL机制,加上rust的所有权及drop trait自动实现页表项的释放。

lab3

sys_spawn第一版实现测试通过,但是到实验4发现实现有问题,然后修改为exec+fork的方式完成。

lab4

文件系统章节是我花时间最多的一个章节,时间主要花在了对文件系统的理解上,看源码也费了些时间。将细节通过在线文档整理如下图所示:

lab5

死锁检测可以基于银行家算法实现,参考Wiki

阶段一:rust语言学习阶段

作为一名C++程序员,不得不说,rust确实是让人眼前一亮。

所有权机制

rust的所有权是rust语言内存安全的重要特性之一。一块内存(或者说一个值)在任一时刻只能有一个所有者,而其他的地方只能持有这块内存的引用。并且在同一时间内,不能有多个可变借用或同时存在可变和不可变借用。生命周期机制还会在编译时检查内存的生命周期,防止野指针的出现,大大地提高了内存安全性

Option

rust的Option类型(一种枚举类型),有Some和None。这与传统意义上的NULL不同,虽然麻烦了点,但是也保证了安全,并且可读性更强。Option类型是一种显式的枚举类型,要求程序员在定义一个可能为空的值时必须使用Option类型。这种方式使得空值情况成为类型系统的一部分,强制程序员在编译时考虑到值可能不存在的情况。相较之下,Null带来的麻烦反倒会比使用它的便利来说更大一点。

阶段二:rCore OS设计实现阶段

lab1

lab1实现了get_time和get_task_info。粗略地了解了一遍现有的这个框架。

lab2

lab2实现了mmap和mumap,并且重写了lab1的两个实现。由于页表有关的知识之前已经学过了,所以也是光速完成了(自己写了给v2p,虽然后面发现已经有线程的接口可供调用了,难蚌)。

lab3

lab3实现了spawn和stride调度算法(其实压根没实现调度,本来想着先过了拿分,但是截止到这篇博客写下都还没实现)。spawn通常通过fork+exec实现,在这里就各copy一点出来就行了。

lab4

lab4实现了linkat、unlinkat、fstat等。当时写完的时候发现fstat一直无法正确的输出nlink,后来才发现是我inode_id算错了。我一直在找一个只存在于虚空之中(bushi)的inode_id。

lab5

lab5实现了死锁检测(又见到了我们熟悉的dijkstra大神的身影:指银行家算法)。虽然只是粗略地实现了semaphore的检测,但是确实让我更深入的学习了这个死锁检测算法。

其他:

参加这个训练营的时候也才大二,暑假刚看完CSAPP,在看MIT的6.s081,刚看没几节课就听说了这个训练营,不仅很好地巩固了自学时所学到的知识,还学了一门新的,非常优秀的rust语言。非常享受这种解决一个又一个挑战所带来的成就感。