0%

第一阶段

工作中已经写了一年多的Rust代码,体验确实不错,特别是完善的包管理工具,大大减少了编译时的心智消耗,所有权和借用系统并没有很复杂,写一阵代码自然就熟悉了

第一阶段算是再巩固下基础,数据结构题比较有意思,不过深入学习还是要抽空看看链表

第二阶段

对操作系统很感兴趣,尤其是第三阶段的虚拟化方向。由于平时工作比较忙,也就晚上和周末抽时间抓紧写写代码,下边主要总结下各个章节产生的疑问和实验遇到的问题吧

lab1(ch2,ch3)

主要熟悉了应用加载,中断,用户栈/内核栈切换,任务切换的机制,再是学习了下risc-v寄存器以及汇编方面的知识,受益匪浅

实验比较简单,主要熟悉下syscall的开发流程

lab2(ch4)

本章新增了地址空间,内核和应用通过页表机制把虚拟内存映射到实际的物理内存,虚拟内存使得不同应用都可以使用连续独立的地址空间,并做到了不同应用之间的隔离

strampoline段用于保存陷入内核和返回用户态的代码,并通过把内核和应用的strampoline设置成相同地址的虚拟页(最高页),使得切换地址空间之后,这段代码地址可以无缝衔接

TrapContext紧贴着strampoline位于应用的次高页(看起来没必要一定要在次高页)。TrapContext用于保存上下文切换时的寄存器以及kernel satpkernel sp等。TrapContext位于用于应用地址空间是因为,切换上下文时只有一个sscratch寄存器可以用于周转,而如果TrapContext位于内核栈(内核栈不是恒等映射),那就只需要先通过sscratch得到kernel satp切换到内核地址空间之后才能访问TrapContext,这时就没有额外寄存器获得kernel sp也就拿不到TrapContext地址。如果位于应用地址空间,就可以通过sscratch寄存器保存TrapContext地址,在陷入内核后,先在应用地址空间保存好上下文之后,再切换到内核地址空间

实验需要注意的点:

  • mmap页面要4k对齐,空间左闭右开

  • syscall复制结构体时,需要通过translated_byte_buffer拿到用户态结构体对应的物理地址(这部分内存在内核是恒等映射的,可以正确写入),再复制

lab3(ch5,ch7)

这两章主要讲了进程和进程通信,是概念相对简单的章节,难点主要在于调度算法

实验是实现spawn,总体来说是new+fork,不复制地址空间,但继承父子关系

lab4(ch6)

本章学习了文件系统,代码中数据结构依赖关系相对复杂,画图梳理下比较方便理解

实验中nlink需要要保存在DiskInode中,同时减少INODE_DIRECT_COUNT保证DiskInode只占用128bytes

这里我把file_count也存下了,方便unlink时候,直接把最后一个DirEntry替换到被删除的Entry位置。其次可以用union保存nlinkfile_count来节省空间,毕竟文件没有file_count,目录不允许有link,这里偷懒了

1
2
3
4
5
6
7
8
9
10
11
12
const INODE_DIRECT_COUNT: usize = 26;

#[repr(C)]
pub struct DiskInode {
pub size: u32,
pub nlink: u32,
pub file_count: u32,
pub direct: [u32; INODE_DIRECT_COUNT],
pub indirect1: u32,
pub indirect2: u32,
type_: DiskInodeType,
}

一定要注意锁不能重入,在开发unlinkat时,因为调用其他也加文件锁的函数导致了死锁,被卡了有一阵

lab5(ch8)

本章主要讲了内核中线程和锁的实现机制

下面有几个注意点

  • 调用schedule切换线程前一定要手动drop资源,否则会造成资源泄露或panic

  • exit_current_and_run_next在调用schedule之前分配了TaskContext::zero_init()作为dummy TaskContext,开始还想着这个线程不会再恢复了,那这个栈空间在后边是如何保证地址合法并最终回收的,而这就是sys_waittid存在价值之一

  • 代码中的PROCESSOR并不是thread_local的,但有很多exclusive_access的调用,可能教学用的rCore并不存在多个cpu,这里暂时不需要考虑这个问题

1
2
3
4
5
6
7
lazy_static! {
pub static ref PROCESSOR: UPSafeCell<Processor> = unsafe { UPSafeCell::new(Processor::new()) };
}

pub fn current_task() -> Option<Arc<TaskControlBlock>> {
PROCESSOR.exclusive_access().current()
}

实验是死算检测,并不复杂,按着文档来就好

2024年秋冬季开源操作系统训练营第一、二阶段总结报告

我个人的话原本这段时间的计划是重新深入学习一下 RUST 语言,通过 RUST 圣经找到的 RUST 官方社区的 QQ 群。刚好在群里看到群友的开源操作系统的邀请,其实时间上对我来说刚刚好,主要原因在于明年暑假可能该找实习了,否则可能不太好找工作。我本人也想通过这个开源来完善自己在开源项目的 PR 上的缺陷,所以,刚刚好,那就坚持到最后吧。

机会总是有的,抓住了还得坚持到底。嗯,就这样。

第一阶段总结

第一阶段怎么说呢,嗯,不太好总结,之前因为一些机缘巧合看过 RUST 官方社区的教材,所以只有简单了解,也写过教材里的demo。所以,总体上来说,对我难度不大。嗯,也是在一阶段前截止前三天吧开始的,后面每天完成一点。大概这样。对我来说的话 RUST 实现链表那边是真的很难,但是只完成 task 倒还好。

第二阶段总结

二阶段的话,分不同的章节吧,收获不同。

第三章实验内容的话是taskinfo系统调用的支持。其实实现这个系统调用不难,这边主要的收获是对程序链接有了更深的了解。在linker链接器脚本中指定的符号可以被OS中以外部符号的形式使用,嗯,链接的作用应该是了解更深了。其次,了解更深的地方在于任务切换,一个任务从elf到一个进程,如何构造,如何执行,如何调度,如何进入User模式执行这样,包括一些寄存器SStatus,SEPC,Stvec等等。

第四章的话,mmap和munmap这两个的实现上也没问题。主要收获其实是在于跳板实现的一些细节,最后跳转trap_handler的时候使用jr指令主要原因在于linker脚本链接出来的地址差值和真正跳板代码的虚拟地址位置还有很大差别。所以,不能这么搞,差值还要加上和跳板的距离。

在进程管理里,先前的任务结构体被重新修改为了进程。并且要实现的系统调用也与进程的产生有关系。此外,对stride算法也有了一定的了解。本质上还是进程里添加了一个字段,然后在每次执行进程时都重新处理一次prio字段并更新记录,以决定下一次执行的进程。

进程管理的主要收获在于,task和进程切换的差别。他们差别很大,进程切换要切所有的通用寄存器等等。任务切换切pc,切栈,切被调用者保存寄存器这样,因为调用者保存寄存器在调用schedule的时候已经保存了。然而切换的时候还有被调用者保存寄存器还没保存好。嗯,就是这样。

这里,有个问题就是使用usize的MAX作为大整数,没想到后面会反复调度任务0,后面debug之后才知道要取余这样。

并发的话,就真的太折磨了,银行家算法真的难搞。最后没过测例的原因居然是没实现sys_get_time。太TM离谱了,以后一定好好看任务手册。

最后

如果这次没坚持到后面的话,可能后面就真的没机会了。学业上有科研要求,还想去实习,嗯,对我的规划来说,可能到毕业之前没有再来一次的机会了。

一定坚持到最后。

Rust学习感悟

学习Rust语言可以是一个既挑战又充满成就感的过程。以下是一些常见的学习Rust的感悟:

  1. 安全性:Rust的设计哲学之一是内存安全,它通过所有权、借用和生命周期的概念来保证。学习这些概念可能会有些复杂,但一旦理解,它们会极大地减少内存错误,比如空指针解引用和数据竞争。
  2. 性能:Rust提供了与C/C++相媲美的性能,因为它允许直接的内存操作和避免运行时垃圾回收。理解这一点可以激励你更深入地学习如何编写高效的代码。
  3. 并发编程:Rust的类型系统和所有权模型使得编写无数据竞争的并发代码变得容易。这种安全性在其他语言中通常是通过运行时检查实现的,这可能会影响性能。
  4. 编译时检查:Rust的编译器非常严格,它会在编译时捕获许多潜在的错误。这可能会让初学者感到挫败,但长远来看,它有助于提高代码质量和减少运行时错误。
  5. 学习曲线:Rust有一个陡峭的学习曲线,特别是对于那些习惯于垃圾回收和动态类型语言的开发者。然而,一旦习惯了Rust的思维方式,你会发现它提供了一种更清晰、更可控的编程方式。
  6. 社区和文档:Rust社区以其友好和乐于助人而闻名,官方文档也非常全面和易于理解。这些都是学习新语言时的重要支持。
  7. 工具链:Rust有一个强大的包管理器Cargo,它简化了依赖管理和构建过程。学习如何使用Cargo可以提高开发效率。
  8. 所有权和借用:这是Rust中最具挑战性的概念之一,但也是其核心特性。理解所有权如何工作,以及如何通过借用和生命周期来管理资源,是掌握Rust的关键。
  9. 错误处理:Rust使用ResultOption类型来处理可能失败的操作,这鼓励开发者在代码中显式地处理错误情况,而不是依赖于异常。
  10. 泛型和trait:Rust的泛型和trait提供了强大的抽象能力,允许编写灵活且可重用的代码。
  11. 宏系统:Rust的宏系统非常强大,允许在编译时执行复杂的代码生成。这为元编程提供了强大的工具,但同时也增加了学习的复杂性。
  12. 生态系统:随着Rust生态系统的成熟,越来越多的库和工具被开发出来,这使得Rust在各种领域,如Web开发、系统编程、嵌入式开发等,都变得更加实用。

学习Rust是一个不断进步的过程,随着经验的积累,你会发现自己对语言的掌握越来越深入,同时也能够欣赏到Rust在系统编程领域所带来的独特优势。

rCore学习感想

学习是一个不断探索和发现的过程,它不仅涉及到知识的积累,还包括技能的提升和思维的拓展。最近,我深入学习了 Rust 语言和 rCore 操作系统,这段经历让我对编程和计算机科学有了更深刻的理解。Rust 语言的安全性和性能给我留下了深刻的印象。通过学习 Rust,我学会了如何利用所有权、借用和生命周期这些核心特性来编写既安全又高效的代码。这些概念在一开始可能会让人感到困惑,但随着实践的深入,我开始欣赏它们在防止内存错误和提高代码质量方面的强大能力。rCore 项目让我有机会将 Rust 的理论应用到实际的操作系统开发中。这个过程不仅加深了我对 Rust 语言的理解,还让我对操作系统的工作原理有了更直观的认识。从内存管理到进程调度,每一个组件的实现都是对 Rust 能力的一次考验,也是对我解决问题能力的一次提升。学习过程中遇到的挑战也促使我不断寻找解决方案,这不仅锻炼了我的问题解决能力,也增强了我的自学能力。每当我解决一个难题,那种成就感都是无与伦比的。我意识到学习是一个永无止境的旅程。随着技术的不断发展,总有新的知识等待我去探索。我对 Rust 和 rCore 的学习只是一个开始,我期待着在未来的学习和工作中,将这些知识应用到更广泛的领域中,继续成长和进步。

Rustlings

我使用过一段时间 Rust,但是已经有大约一年时间没使用了,Rustlings 的练习让我重新找回对 Rust 的熟悉感。Rustlings 前面的一些部分都是通过魔改代码,让编译通过并通过测试,从而理解 Rust 的特性。因为魔改的地方就是教学的地方,所以通过魔改少量代码的方式教学,我认为是非常高效的,而 watch 功能则更快捷方便连续地学习,就跟闯关一行。

相比于 Rust-by-Example,Rustlings 更偏向于实践,更方便开发者在了解 C/C++ 等高级语言的基础上,了解 Rust 的内存安全特性,包括所有权、生命周期等机制,迁移到 Rust。Rust 更强大的枚举、元组等类型以及支持更完善的标准库,更方便开发者专注于程序算法本身,而不是对程序的具体过程操心。Rust 的迭代器抽象还能够方便的提供函数式编程,使用一行语句就完成对数组全部操作,在数学上更美观,同时应该也更方便编译器进行优化。

Rustlings 最难的部分应该是一些算法的实现。正如 Rust 的所有权机制,通常对象之间有树形的嵌套关系,而描述双向链表、图这种数据结构则并不适合使用这种关系;其中有一个练习是二叉排序树,但是不要求实现平衡。如果要实现平衡树则要考虑到树的旋转等操作,在这种所有权机制下则更难实现。而对于图,则只能对于整个图整体分配存储、整体管理,最后很容易写出 C 风格的代码,或者使用了大量的 unsafe( algorithm1 的单链表合并中就有大量 unsafe)。我也尝试过使用 Rust 练习 AtCoder,体验堪称坐牢,写 Rust 就是和编译器作斗争。所以使用 Rust 完成漂亮的算法类代码,是一门深刻的艺术。

本人算是对操作系统比较熟悉,之前做过 xv6-riscv 实验,这次体验了 Rust 的操作系统试验。

ch3

ch3 可以说是 rCore 的入门题,帮助上手 rCore 的代码以及 rust 系统编程,还帮助熟悉 risc-v 的特权级指令。

ch4

ch4 主要是熟悉了操作系统的内存管理,从此,用户的地址空间和内核的地址空间隔离开,同时开启了页表。 ch4 实验重点是在内核和用户空间传输数据,因为内核是恒等映射,而用户空间开启了分页。如果二者没有共用页表,那么就需要手动模拟地址转换,并且还需要按照分页去写内存,因为一次返回的数据可能在不同的页中。此外还实现了简单的空间分配和管理,实现了简单的 mmap 和 munmap。

ch5

ch5 主要实现的进程管理和进程调度。把 fork 和 execve 融合魔改出来一个 spawn 能加深对 fork 和 execve 的印象。

ch6

ch6 是完善 easy-fs 的功能,但是我认为 easy-fs 没有解析路径、构建树形目录结构不能让学生了解到一个功能比较正常的文件系统的样子,而在此过早将 easy-fs 与其它内核模块解耦合也带来了增加功能和 debug 上的困难。

ch8

ch8 要求实现死锁检测算法,但是传统的银行家算法要求预先知道各个线程需要的资源,并且没有考虑线程因等待资源而阻塞的情况,这与现实的操作系统有出入。在现实中,可以认为阻塞的线程所需要资源为它所需的全部资源,将检测的申请资源调用后的状态作为最终状态,只需要检测此次申请是否可能死锁。

总结

对 rCore 的学习与实践更深入了解了操作系统的基本原理,也锻炼了 rust 代码能力。

前言

我是来自于海南大学密码科学与技术专业的本科生,对于计算机体系结构方向很感兴趣,但由于学校条件所限,不能满足个人学习需求,所以来参加这个课程

在之前,我一直对一些东西比较好奇

比如

  1. 盗版激活软件所说的模拟硬件激活
  2. 计算机取证中,DMA技术如何用来解密BitLocker的,DMA外挂是如何实现的(当然,这个漏洞已经被修复),(我觉得,作为一个本科生,不应该只知道表面,而是更深入的了解,正所谓,知其然,知其所以然)
  3. 内核的内存管理
  4. 并发
  5. 动态链接的一些细节

等问题.

所以, 我报名参加了这次的操作系统训练营.

第一阶段

我C写的比较多,而且习惯确实不太好,所以在这阶段,也是磕磕绊绊,尤其是在所有权机制上,给了我“不灵活”的印象,我觉得也不算坏事吧,通过尽可能多的约束,把不安全的代码存在的范围尽可能缩小,方便发现问题和解决。

此外 rust的cargo非常好用。

还有,教程使用的rustlings有点旧?我用着是有点不太方便的,貌似没法很好的调试,我依赖于rustings run name这种方式来debug,但是每次都要写那么多,显然有点麻烦

我通过把如下内容加入 .bashrc,简化了这个操作

alias check=’rustlings run’

alias hint=’rustlings hint’

这样,就可以通过check name的方式检查题目了

第二阶段

Lab 1

一个简单的多任务系统,我曾经参加过海南大学南海鲨战队的电控培训,看过ucosii的代码,所以这点对我来说还好

Lab 2

这个实验启用了分页机制, 在这次实验之前,看过xv6 内存管理部分的代码,所以还好(这在之前,逆向程序时的内存地址,让我有不少困惑)

Lab 3

进程: 在这地方,我觉得问题不大

Lab 4

文件系统: 这里问题挺多的,做的有点糊涂,不过不管怎么样,能用

Lab 5

多线程: 了解了线程与并发的相关知识,还有银行家算法

总结

通过这门课程我学习到了很多操作系统的基础知识, 这个学期补补计算机组成原理,算法。rust也不熟练,不优雅,还得多练,下个学期二战。

我在此感谢此训练营的组织者和助教们,你们为很多人打开了一个新的天地。

前言

我自己之前参加过ysyx,有一点操作系统的相关基础知识和riscv指令集基础,pa4后面还没做完,对于很多操作系统基础概念理解还是不够深刻,通过rcore的学习也是补上了这一部分。操作系统作为最和硬件联系紧密的软件,特权、异常、中断等概念的引入,让cpu具有了安全的连续不停运行各种各样程序的能力,这非常美妙😄,这也是我学习操作系统的目的。

个人感悟

通过近一个月的学习,对于操作系统的相关概念和rust这门编程语言有了更深入的了解,之前对于操作系统也只是停留在很基础的部分,对于操作系统的基础概念的认识也是非常模糊,也没有上过操作系统的相关课程,通过这次的学习,也是对操作系统的基础概念(进程管理,地址空间,文件系统,并发等等)有了基础的理解。
在rcore的实验课程中,最让我印象深刻的就是虚拟地址部分,由于之前接触的操作系统全部都是nommu类型,直接访问物理地址的概念非常不容易被打破,并且ch4也是一个门槛,需要非常深入的理解rcore的整体代码框架,对虚拟地址的映射,软硬件协同操作,多级页表,地址空间的概念理解了很久,在不断查找资料,不断理解的情况下,才对虚拟地址的概念有了很浅显的理解,把任务完成。
在完成任务的过程中,其实rcore的整体框架非常完善,作业的部分也就是一些扩展功能,整体还是对rcore的实现的理解,在完成任务时,也是对已有实现进行模仿,理解整体框架,之后调用已有函数或自己根据数据结构实现函数,实现功能。

rCore 总结

这个阶段大约花了3周左右的时间,更多是对OS概念抽象risc-vCPU架构的一些了解。跟rust的关系在我看来不算特别大。
核心的找地址、上下文切换都是c-like/asm实现的,当然这部分代码占比很小很小。这里面内存frame等等几个地方,充分利用了RAII机制,
大大减少了内存释放的心智负担,感觉是rust语言优于传统C的典型佐证。

这个阶段花时间较久是在ch8银行家/死锁检测算法上,因为概念的不熟悉,套用模型产生的死胡同。一直没有找到各个线程对于初始化资源的需求表,所以也就套不进银行家。中间放弃准备用循环图检测,发现不支持数量(权重)。最后看了一下向勇老师的在线课《操作系统-20.4 死锁检测》,找到了灵感,按照这个思路写算法,大概又花了几个小时调试通过。

下面按照实验内容分章节进行总结。

Read more »

前言

本人2024年8月份的时候,工作需要一部分图像处理代码,最后选择用Rust实现,并通过krpc-rust跟java完成调用。后面发现rust的一些理念尤其是RAII确实挺不错,就关注起来了。

后来在9月份的上海Rust大会上,了解到清华rCore项目训练营,感觉挺有意思的,既能学习os又能学习rust,就种草了

Rustlings 总结

基础阶段相对简单,却也兴奋,熟悉了各种rust语言的特征。比如下阶段会频繁用到的extern "C"#[no_mangle]
算法阶段,通过unsafe处理指针、链表,对unsafe有了一些熟悉,尤其是类型转换。还有就是熟悉了堆的实现,加上rust的运算符重载机制,可以灵活的在大小堆之间切换。
另外整个过程中发现rust并没有传说中的所谓曲线陡峭,秉着实用主义的原则,先进行一下元学习,也即了解这门语言的层次结构,函数传值、内存模型等大的层面,其余部分,用多少学多少,在出现问题的时候知道去研究哪部分就可以了。
所以我觉得学习也像小马过河,重在实践。

本人自己其实也在用 c 重写 linux 0.11 的内容,但越往后走,越觉得自己在闭门造车,担心最后做出来的东西已经与目前主流方向脱轨,
偶然的机会接触到 opencamp 这个社区,了解到这个社区现在在做什么。发现与自己所需要的东西强吻合,故毫不犹豫的加入了这次活动。
本人是一名软件工程师,主要语言是c和go,学习rust这门相对比较新的语言其实还是有点吃力。尤其是rust语言中的一些语法糖以及trait,更是
让我这个初学者苦不堪言。由此,我把学习过程记录到了这儿。

rust学习记录

第一阶段,让我对rust的基础语法有了了解,发现rust编译器一直在思考c工程师常考虑的两个事情:这个内存什么时候被申请,这个内存什么时候要释放。
弄清楚这个之后,使用rust就变得不那么困难了,尽管学习完这个课程后还不能写出很优美的rust语法糖,但至少可以保证代码的正确性了。