0%

rcore第二阶段

rcore的五个实验,捋清楚代码,就比较好做。这是我第一次捋这么多代码量的项目,学到了很多。以下几点是我理解并做出实验的心得。

  1. 不需要全部细节搞懂。理解调用过程并不需要关注所有细节。
  2. 先提出问题,再去找答案。可以用gpt辅助提问题。
  3. debug其实后面用的都少了,多用log。
  4. 关注层次,有些理解不了就是站的角度不对,从内核视角看,从用户视角看,从文件系统各个层次看,等等,可以切换视角来整体性的把握代码。
  5. 有些是代码写出来是方便查看,但并不一定有啥功能上的作用。所以有些地方不用太纠结。
  6. 画图!只要你能画出内存、追踪寄存器、理解调用过程,那其实内核没啥难的,每一步都是可以追踪的。多画图让自己可视化的把握这整个过程。尤其是ch8那个死锁算法,我自己画图,把整个过程都画一遍,案例自己走了一遍,那就很清晰了。
  7. 读代码不要线性读过去。可以让gpt梳理流程再读。同时可以一个变量一个变量追踪,上来全部看会云里雾里的。
  8. 个人感觉makefile和一些rust嵌入式可以搜索b站相关资料学习一下。有这两个基本技能就能先对内核代码是怎么生成的、为啥是内核、怎么写的,有个框架性的把握。
  9. 不需要让自己了解很多前置知识才能开始,盯着具体问题去思考补啥知识。
  10. 对于每个题目,印象最深的就是多体会案例吧,毕竟是练习用的,很多情况不考虑的。
  11. 文档第一遍应该是略看,大概知道一下知识,最重要的是自己捋代码。
  12. 印象最深的是死锁那里,没有drop导致多重借用了。

arceos第三阶段

  1. 一开始不理解feature怎么传递的,感觉搞不懂了,心态爆炸。但是往后做就理解了,em所以前期真的不用纠结太多我觉得。
  2. arceos这里在toml里面一堆文档阿,先看这个会有更宏观的把握。
  3. 最难的感觉就是开始,了解了re-export这个机制,重新认识了一下rust的项目管理,之后就没啥了。
  4. 这里印象最深的是文件系统和对齐,前者再让我体会了lsp机制不完善吧,然后通过条件编译管理以达到组件化的效果也是学到了,我看大多数都会有一个接口或者一个默认的实现,然后再用条件编译来管理不同的实现,估计第四阶段会进一步用到吧。

这是rcore训练营前三个阶段的总结报告。

做实验的过程中有写一些笔记,不过是写给自己看的,行文有些杂乱:rcore-notesarceos-notes


在群里看到有人分享rcore训练营的消息,然后在rcore开营启动会那天加入了进来。对os的很多概念一直只能靠想象,没有代码级的理解,通过这次训练营,学到了非常非常多。以前尝试去看过《一个64位操作系统的设计与实现》这本书,但是这本书并无过多讲解,并不适合入门os代码,读不动。现在有了训练营的基础,后面有时间再试试。

训练营的内容大部分时间是读代码,完成练习所需写的代码量并不大。训练营准备了详尽的文档与指导书,并已经准备好了构建脚本,能非常顺利地上手开始学习os。

此外,这次训练营学习还连带着学习了rust,之前听说过rust和其它语言在思维上有些不同,不过实际接触下来感觉还好,还是和c++很相似的,只是有个所有权的机制很特别。rust吸收并处理了c++的一些好的概念与不足(移动语义,没有包管理器等等),有Cargo这一点比c++方便多了。

第一阶段

学习rust基础,做rustlings的练习。

这个比较基础,我是看了下rust的菜鸟教程学rust,然后做的过程中不知道的问chatgpt就行。

第二阶段

学习rcore指导书并完成5个实验。

rcore指导书的质量非常高,前几章基础的代码需要仔细阅读理解,这样就能知道基础的执行流程。

前面的章节把一些代码(比如那几个汇编写的函数)交给chatgpt解读非常方便。后面的章节效果要差一些,有些主要起个提示作用。

第三阶段

学习arceos并完成6个练习。

原本以为第三阶段是第二阶段的递进,会去学习M态的rustabi什么的,但是实际第三阶段的arceos是和rcore指导书平级的,相当于是另一种设计思路下实现的os。

有了第二阶段rcore指导书的基础,第三阶段问题不大。

总结

训练营的质量非常高,特别是考虑到了对学生的教学,文档和工程都很齐全,rcore指导书的质量非常高。学习到了很多。

感谢清华大学的老师们开放的课程,以及助教,群友等的答疑。

一阶段

总结

第一阶段主要是rustling,只需要应用Rust本身的设计理念来满足测试要求。整体难度不大,理解起来也比较顺利。

难点

相比于语言本身,算法部分的挑战更大。虽然按照要求可以找到相应算法的描述,照着实现基本能完成任务,但有两个方面:

  1. 同类算法的取舍:平均复杂度同为O(n)的两种算法,在同一实际场景中的表现不同,我并不了解这些算法的最佳适用区间。
  2. 不同实现的差异:同一个算法,采用不同的数据结构实现,所需的时间和空间开销也会不同。

    二阶段

    总结

    第二阶段主要是对操作系统的认识。每个实验大致分为三步:
  3. 阅读相关材料,了解操作系统的相关知识;
  4. 梳理实验中涉及的类与函数的层级结构及其功能;
  5. 编写实验代码,完成具体实现。
    整体过程也比较顺利。

    难点

    二阶段对文件相关内容印象最深,文件系统这个从纯指针一步一步抽象到文件的过程,通过多个层级的抽象来提供简单的交互,通过各种锁和标记来管理权限和控制并发,让我对C的便利性、内存管理有了更深刻的理解。

    三阶段

    总结

    三阶段有些难度,主要侧重于基于unikernel的模块化内核,不像二阶段那样深入去看代码,这就导致有些实验,虽然我知道应该怎么做,但是不知道我需要的函数在哪里,这也跟vscode工具不熟练有关;有的实验,我虽然知道怎么做,但是实现的位置不对,导致与其他实验冲突。

    难点

  6. 在rename里,由于入口时fs.root.rename,而在dirnode里只能通过parent查找父节点,通过children查找子节点,造成了根节点的识别与文件查找的差异,之后再看到rename的标注,仅能在同一mountpoint的文件系统内使用,才明白实验的ramfs就是在/tmp下,就是这么设计的。
  7. 挑战题没有过线,tlsf实现后达到162。本来的计划是先实现一次tlsf,再优化。但是tlsf实现的工程量超出预期,本身的代码量就有很多,还有很多debug用的log和test,应该是我抽象的层级不够。

    未尽

  8. 找到更好的代码分析方法,更好地梳理函数调用
  9. 使用debug mode,目前都是通过log调试
  10. 完成挑战题,重新设计一个内存分配方法
  11. 最后两个视频未完成

偶然有天看到 Rust 中文社区的公众号转发了一个开源 OS 训练营的消息,点进去一看原来是 rCore,早就听过 rCore 这个项目,但一直没来得及学习,自己也对 Rust 在嵌入式开发和内核中的应用很感兴趣,于是转发到群里忽悠了几个朋友一起组团参加

Read more »

作为二战选手,上次秋冬训练营止步于第四阶段,之后痛定思痛恶补了一下CSAPP还有RUST圣经,操作系统等这种基础课,还猛猛的参加了Rust训练营,今年磨拳擦掌卷土重来⸜( •ᴗ• )⸝

第一阶段

第一阶段属于是轻车熟路了,通过链表的编写更深刻的理解了Rust的裸指针和智能指针,感慨学无止境,同时也在读张汉东老师的Rust编程之道,后面死灵书等一系列内容都在排队等着学习,对语言的探索多深都不为过。

第二阶段

上一届对于汇编ld文件makefile文件这种东西稀里糊涂,经过过年前后的闭关,把这些知识都补起来了,为了更了解RISCV都汇编指令,通关了傲来训练营的一二三阶段,又自己学习了RISC-V体系结构编程与实践,现在ld文件也看得懂了,makefile也能自己改了,汇编和高级语言之间的调用也知道咋回事了,剩下的就是确定范围和补全逻辑了,有了上一次的经验,这回对从批处理,到进程再到线程的进化有了更深的体会。

第三阶段

第三阶段,上一届前进到写完hashmap因为别的事情就没有继续了,这回有时间慢慢来,一层一层弄清楚调用关系,感受何为模块化操作系统。不过仅是对功能的修改觉得不够尽兴,我也自己拉了一个repo写一个自己的操作系统玩一玩。

阶段一

这一阶段是rustlings刷题,主要是对rust的学习,我接触Rust有一段时间了,Rustlings之前刷过两遍,对我来说还是比较轻松的。

阶段二

这一阶段是rcore的练习,涉及到很多计算机底层软硬件的知识,收获很多。

1. 1~3章

  1. 对与程序的内存布局,代码段,数据段,堆,栈,有了更加深刻的认识,学习了高级语言如何与汇编语言进行交互,以及对程序编译链接过程有了的进一步认识
  2. 对中断机制有了进一步的了解,学习了中断的处理流程,以及如何在中断处理中保存和恢复寄存器的值
  3. 学习了如何在Rust中嵌入汇编语言,以及如何在Rust中调用C语言函数。
  4. 学习了CPU硬件特权级的概念,以及如何切换特权级
  5. 学习了处理机调度的概念,中断处理流程,以及如何实现基于时间片的的任务切换

2. 第4章

第四章是我花了最多时间的一章,以前对操作系统的内存管理仅仅停留在课本的知识,这一章让我对操作系统的内存管理有了更加深入的了解。

  1. 学习SV39的页表结构,对逻辑地址和物理地址的转换有了更加深入的了解,尤其是加深了对逻辑地址,物理地址,页表项,页内偏移量的理解
  2. 学习地址空间的切换,以及如何在切换地址空间时保存和恢复寄存器的值,确保操作系统和用户态程序能够正常运行。
  3. 学习了操作系统如何管理物理内存,以及如何在物理内存中分配和回收内存。
  4. 学习地址空间映射,了解和恒等映射,之前一直在纠结如何保证再开启虚拟内存后操作系统的正确运行。

3. 第5章

第五章是进程管理的章节,主要是学习了进程管理的相关知识,包括进程的创建,退出,等待,以及进程的调度,对进程的概念比较熟悉。

4. 第6章

第六章是文件系统的章节,主要是学习了文件系统的相关知识,包括文件系统的概念,文件系统的实现,以及文件系统的接口。

5. 第7~8章

学习了进程rCore的并发机制,包括信号量,锁,条件变量等概念,以及如何在rCore中实现这些机制。对银行家算法中的资源有了更加深入的了解。

阶段三

第三阶段是对arceos的学习,对组件化OS的设计有了宏观的认识,从unikernel到宏内核再到Hypervisor。相比于rCore,arceos的项目组织更加清晰,代码更加模块化,也更加易于理解。
让我印象最为深刻的是Hypervisor的实现,我之前对Hypervisor的理解是模拟计算机硬件,然后客户机在这些虚拟硬件中执行,对于硬件虚拟化的认识是比较浅的。

总结

这次操作系统训练营是一次非常有收获的经历。

rCore 2025 spring blog

第一阶段

  • 这一阶段主要就是了解学习Rust的语法和特性,以及完成所有的rustlings题目, 以及实现一些基础的数据结构和算法, 为之后的项目内容做准备.
  • 由于我之前是写Go的并没有接触过Rust,第一阶段以至于后续阶段的编码过程的感觉都非常割裂。

第二阶段

  • 第二阶段由多个小lab组成,个人感觉是有一些lab比第三阶段的lab更难,测试用例也更多,每次做都得看看实验指导书才敢下手,也是提前开始做阶段二后面的进度才能赶上来。

第三阶段

  • 第三阶段跟着ppt和自己的os理解在慢慢探索,如果没有给出提示我可能要花费两倍的时间才能写完,最后也是瞎摸索完成了阶段三,感觉对操作系统的理解又进了一步的感觉,但还需要持续学习。
Read more »

前言

本人是大三的计科专业学生,上学期学习了操作系统,颇有兴趣,这学期听说了 opencamp os 训练营,就来深入学习一下。本文是我在完成前三阶段任务后的阶段性总结,主要回顾这一个月的学习历程,并对知识进行整理与查漏补缺。

  • rust
    本人在参加训练营之前在 rust 语言上是 0 基础。我花了一周的时间学习了基本语法和特性,并通过了 Rust 编程的阶段。虽然如此,但我对 rust 的掌握还不是很深刻。
    在做 rCore 和 ArceOS 的过程中也会遇到很多语言上的困难,只能走一步看一步,一边完成 os 的编程任务,一边补充对 rust 的理解。
  • rCore
    在上学期学习操作系统期间已经了解到有 rCore 这个练习项目,并粗略的看过 《rCore-Tutorial-Book 第三版》的前两章节,只是有了一个大概的了解。这个学期参加了训练营,希望通过学习 rCore 提升工程能力与对 os 的理解。大概花了两周多的时间完成了 rCore-Camp-2025S,期间在内存管理的部分花费了比较多的精力,重点是对多级页表和内核空间与用户空间的理解。在完成了 rCore-Camp-2025S 后,虽不能说对 rCore 了如指掌,但我已掌握了 os 设计的基本思想,当然仍有很多细节上的问题我没有搞清楚的,未来还需要下功夫。
  • ArceOS
    这一阶段着重于组件化操作系统的设计与实现,从 Unikernel 模型逐步过渡到宏内核,再进一步到 Hypervisor 架构,核心在于基于组件构造内核。
    Read more »

第二阶段总结

2025spring-rust-based-os-comp-stage2-report-loichyan

主要收获

抢占式内核任务

前文中,构思了一种单内核栈的思路.但如果需要实现抢占式内核任务,仅凭单个内核栈是无法做到的——当某个内核任务被打断,需要保存完整的上下文,包括整个执行栈.因此,需要在单内核栈的基础上,增加动态分配的内核栈^1.具体而言,

  1. 每个任务启动时从内核栈池中领取一个内核栈;
  2. 如果该任务正常结束,归还内核栈;
  3. 否则,该内核任务被打断(通常是时间片耗尽),将内核栈与上下文一并保存.

这样便能按需使用多内核栈,以最大化利用单核栈带来的优势.

Rust 无栈协程模型

在 Rust 的异步模型中,编译器将每个异步函数转换为一个状态机^2,从而避免了任务挂起时对执行栈的保存(虽然加重了编译器的负担).因此,Rust 无栈协程模型天然地适用于单内核栈的系统:

  • 多内核栈系统中,通过 IRQ^3 (Interrupt ReQuest) 机制等待 I/O 操作时,可以主动放弃执行权从而挂起当前任务,此时需要保存整个执行栈;
  • 而在单内核栈系统中,通过 IRQ 机制实现异步 I/O,await I/O 操作时,只需将当前任务加入 Executor 的等待队列即可.

Thread-per-core 模型

在多核环境中,为了保证数据一致性,原子数据结构(如 ArcAtomic* 等)和同步锁(如 MutexRwLock 等)是必不可少的.但除了文件系统读写等任务必须加锁以外,大部分任务都是在单核上处理的,此时对原子计数和同步原语的频繁读写就成了额外的负担.并且,主流异步运行时(如 Tokio、async-std 等)默认要求 Future 多线程安全,这使得编写异步函数没那么“愉快”,也是 Rust 异步体验被广为诟病的一点^4.Thread-per-core 模型^5便因此有了不少拥趸,例如 Glommio^6 是一个适用于 Linux 的 Thread-per-core 框架,它是以 io_uring^7 为基础构建的.

之所以 Tokio 等运行时要求 Future 多线程安全,是因为它们使用了工作窃取的调度算法^8,即在线程 A 创建的任务可能被线程 B “偷走”来减少线程空闲.Linux 的任务调度算法也使用了工作窃取^9.Thread-per-core 和工作窃取模型各有优劣^5

  • Thread-per-core 中,绝大部分数据结构都不必是线程安全的,避免了多线程同步带来的开销,同时也使得编写异步函数更为简便;
  • Thread-per-core 中,任务基本上都是在单个 CPU 核心是执行的,减少了执行环境变更导致的高速缓存丢失;
  • 工作窃取中,空闲线程从忙碌线程中窃取任务,从而使得各核心之间负载均衡,能最大化利用多核资源;
  • 在实际应用场景中,通常不容易彻底区分 CPU 密集型和 I/O 密集型任务,因此工作窃取适用面更广泛.

此外,从 Glommio 的介绍^6中不难看出,它很大程度上依赖于各任务之间的相互协作,即理想情况下,任务需要周期地归还执行权,来使得权重更高的任务优先执行.并且,在 Thread-per-core 模型中,为了充分利用各核心的执行资源,需要将 CPU 密集型和 I/O 密集型任务细致地划分给不同的核心,这无疑给开发维护带来了额外的心智负担.从这个角度来看,Thread-per-core 和工作窃取又分别类似于内核的协作式任务调度^10和抢占式任务调度^11.前两者和后两者的不同之处在于:

  • 内核的任务调度算法主要面向于用户任务,它们所需要的资源通常是未知的,并且都希望能独占资源;
  • 而同一个应用程序中(内核本身也可以视为一个复杂的应用程序),各任务所需的资源通常有一个预期,因此,在理论上,通过最细致地划分可以让整个应用达到极致的性能.

不过,综合考量各方面因素,还是工作窃取更适用,也更容易实现 :)

总结

以上便是第三阶段的主要收获,接下来需要进一步研究 IRQ 机制与 Rust 异步的集成,以及 io_uring 模型的实现和应用,最终达成对 ArceOS 内核的异步化改造.