0%

前言

因为我已经工作有12年了, Rust也写了有几万行, 所以第一阶段对我来说没什么难度. 但是我还是按照训练营的要求, 完成了rustlings的练习.

rustlings

rustlings的练习很简单, 但是对于新手来说, 是一个很好的入门练习. 通过这些练习, 可以很快的了解rust的基本语法和特性.

不过我仍然从中学到了一些新东西, 比如 BinaryHeap. 之前我在编写应用程序的时候一直用 Vec 来实现优先队列, 现在我知道了 BinaryHeap 这个更好的实现.

总结

理解了 Drop 就入门了 Rust.
理解了 Trait 就熟悉了 Rust.

前言

就Rust语言来说,我认为这是开创新时代的语言,一直在努力学习。
我自己学Rust语言的过程,分了三个阶段,最初纯粹是好奇。
用Rust自己写了一些小程序后,对Rust有了实际的体验,感觉体验很好、值得信赖。
于是有了更深入学习的想法,前面参加了InfiniTensor训练营,了解到使用Rust开发更复杂程序的方法。
现在参加操作系统训练营,是对自己更高的挑战。

开发一个操作系统内核是一个宏大的课题,需要认真的思考、深入地研究。训练营在短时间内整个拉了一遍,
给了学员一个宏观的体验,这非常宝贵,这是我参加训练营的原因。同时,在这个过程中,通过交流和学习,
也触发了我个人的深层思考,这对我个人尤为宝贵。
限于时间,我没法把所有问题思考透彻,但又不能感兴趣的问题轻易丢弃,于是写这篇总结,留待以后。
标题叫Something Not yet done,就是我想做、想探寻但还没有答案的东西。

基础阶段

内容

基础段主要涉及Rust语言的学习。

  • Rust语言的unsafe部分
  • Rust语言的异步和并行部分

Rust语言的unsafe部分

第一次涉及到unsafe部分代码的编写,经历了程序的崩溃,意识到unsafe的危险;到使用安全方法怎么也无
法实现想要的功能,明白了Rust的哲学,unsafe的重要性。从享受Rust的安全编程,转变为谨慎地编写unsafe
然后再享受Rust的安全编程,对于Rust的编程理念有了更近一步的了解。
但如何安全地编写unsafe,仍然有很多知识需要学习和实践,包括:

  • unsafe function
  • unsafe trait
  • unsafe extern

要完全掌握Rust,unsafe是必须跨越的一步。

Rust语言的异步和并行部分

对于如何编写高性能的Rust程序,还缺乏实践。

专业阶段

内容

专业阶段主要涉及对rCore的代码分析、学习,和部分功能实现。

  • 系统调用
  • 虚拟地址
  • 进程管理和调度
  • 文件系统
  • 并行控制

系统调用

这部分功能比较简单。

虚拟地址

引入虚拟地址后,所有系统调用的参数传递,都需要进行地址转换。目前都在系统调用处理函数中,复制粘贴
代码来实现,格外得丑陋。希望在第三阶段的时候,对这部分进行封装。

进程管理和调度

stride是比较简单的调度算法,希望在后面能够尝试将Linux的调度算法移植过来。

文件系统

在实现功能的过程中,在File trait中增加的一个方法。不知道有没有破坏原有的抽象,三阶段看看完整的
项目是如何解决File到Inode转换的。

并行控制

死锁检测的实现中,感觉对于列表的实现有点丑陋,目前可用的集合类就只有VecDeque,三阶段看看有没有
其他实现方式。

项目阶段

待补充。

rustlings 总结

Rustlings 是学习 Rust 编程语言的极佳练习工具,它包含了多个由浅入深的练习题目,帮助学习者快速掌握 Rust 的基础知识和重要概念。

1. 变量与可变性

  • Rust 中的变量默认是不可变的(immutable),即变量在声明后无法更改。要让变量可变,必须显式添加 mut 关键字。
  • 这种默认不可变性帮助开发者避免无意的状态变化,提高代码的安全性和可维护性。

2. 数据类型

  • Rust 是静态类型语言,编译器会在编译阶段检查数据类型。
  • Rust 支持多种数据类型,包括标量类型(整型、浮点型、布尔型、字符)和复合类型(元组、数组等)。

3. 所有权机制

  • Rust 的所有权系统是其内存安全性和性能的重要保障。
  • 每个值在同一时间只能有一个所有者,当所有者变量超出作用域时,内存会自动释放。所有权的转移、借用和引用(可变和不可变)是理解 Rust 内存管理的关键。

4. 借用与引用

  • 借用(Borrowing)允许在不转移所有权的情况下使用数据。
  • Rust 有严格的借用规则:在同一作用域中,只允许一个可变引用或多个不可变引用,确保内存安全。

5. 结构体与枚举

  • 结构体(Struct)用于将不同的数据组合成一个复合类型,枚举(Enum)用于定义一组可能的状态或值。
  • Rust 的枚举非常强大,支持绑定数据,并且可以与模式匹配一起使用,帮助更清晰地处理复杂的逻辑分支。

6. 模式匹配

  • match 表达式和 if let 是 Rust 中处理分支的主要工具,尤其是当处理枚举和结果类型(Result)时。
  • match 语法不仅简洁,还能避免遗漏某些分支,确保代码的健壮性。

7. 错误处理

  • Rust 提供了 ResultOption 类型来进行错误处理和空值处理。
  • 使用 unwrapexpectmatch 等方式处理这些类型,开发者可以编写出更健壮的代码,避免程序在运行时崩溃。

8. 所有权的移动与复制

  • 移动(Move):当变量的所有权被转移时,源变量将不可用。
  • 复制(Copy):对于实现了 Copy 特征的类型(如基本数据类型),赋值不会转移所有权,而是直接复制。

9. 特征

  • Rust 中的特征类似于其他语言的接口,用于定义一组方法签名,供结构体或枚举实现。
  • 特征使得 Rust 支持多态,通过泛型和特征约束实现代码的复用和接口一致性。

10. 智能指针

  • Rust 的标准库中提供了 BoxRcRefCell 等智能指针类型,帮助管理内存和共享数据。
  • Box 实现堆分配,Rc 实现引用计数,RefCell 提供运行时的可变性检查,用于实现更复杂的数据结构。

11. 并发编程

  • Rust 的所有权机制让多线程编程更加安全。
  • 使用 std::thread 库可以方便地创建线程,并且借助 ArcMutex 等类型来实现线程间的数据共享和同步。

12. 生命周期(Lifetimes)

  • Rust 使用生命周期注解来管理引用的生命周期,确保程序不会引用无效数据。
  • 生命周期的概念是 Rust 内存安全的重要组成部分,编译器会自动推断大部分生命周期,但有时需要显式标注。

OS 实现

第二阶段主要分为八个章节,每个章节层层递进,深入揭示操作系统的底层逻辑以及实现原理。

lab1

为了实现 sys_task_info 系统调用,首先在 TaskManager 中为任务控制块 (TCB) 扩展结构体,加入如下字段:sys_call_times:[u32;MAX_SYSCALL_NUM],然后在在mod.rs中增加increase_sys_call和get_sys_call_times函数,进而在syscall函数中调用increase_sys_call函数,

在系统调用处理逻辑中,维护当前任务的系统调用次数计数,每次进入系统调用时在数组中相应位置加一。在 sys_task_info 系统调用实现中,将当前任务的状态(应为 Running)、系统调用次数、以及通过 get_time() 获取的任务总运行时间写入 TaskInfo 结构体。

lab2

  • 完成sys_get_time和sys_task_info函数,需要定义一个translated_struct_ptr,它通过页表(PageTable)将一个指向结构体(*mut T)的指针翻译为对应的物理地址,并返回一个可变引用(&’static mut T),这样可以直接操作映射后的物理内存。在sys_task_info函数中,需要获取系统调用时间和任务运行时间,所以需要在task.rs中定义并实现它们。
  • 完成sys_mmap和sys_munmap函数,用于申请和释放虚拟内存映射。sys_mmap 通过指定的起始地址 start、长度 len 和内存页属性 port 来映射一段物理内存到虚拟地址空间。该函数会检查起始地址对齐情况、port 的合法性,并将内存页映射为可读、可写或可执行。sys_munmap 则用于取消内存映射,释放从 start 开始的一段虚拟内存。(注意,需要判断地址是否对齐)

lab3

  • 实现了自定义的spawn的系统调用来创建新进程,以便简化进程创建的过程而无需使用fork+exec的组合操作。通过sys_spawn,直接从指定的路径启动目标程序,成功返回子程序的进程id,否则返回-1。
  • 实现了stride调度算法,为每个进程设置优先级与其调度权重,使得系统资源能够更公平分配。stride调度通过设置初始优先级与动态步长(pass值),优先调度累计步长最小的进程,并对选中的进程步长进行累加调整,确保各进程的运行比例与优先级成正比。我还新增了 sys_set_priority 系统调用,以便动态调整进程优先级,增强调度灵活性。

lab4

在本次作业中,我实现了三个系统调用:linkat、unlinkat 和 fstat,以支持硬链接和文件状态获取。

  1. linkat:该调用用于创建一个文件的硬链接。它接收原有文件路径和新链接路径作为参数,并将新路径指向与原文件相同的磁盘块。在实现中,我确保在创建链接时不允许同名文件的存在,避免潜在的未定义行为。
  2. unlinkat:此调用用于删除文件的链接。当调用 unlinkat 时,如果文件的引用计数降至零,它将回收与文件关联的 inode 及其数据块。我的实现确保正确处理文件的彻底删除,维护文件系统的一致性。
  3. fstat:该调用获取指定文件描述符的状态信息。它将文件状态结构体填充以提供有关文件的详细信息,如大小、权限和时间戳等,便于用户或其他系统调用进行进一步处理。
    通过这三个系统调用的实现,我的文件系统支持了硬链接的创建与删除,以及文件状态的查询,从而增强了其功能和灵活性。

lab5

我实现的功能包括对进程和线程的资源管理,主要是通过互斥量(mutex)和信号量(semaphore)来控制并发访问。具体而言,为每个线程维护了四个关键数据结构:m_allocation 和 s_allocation 用于记录已分配的互斥量和信号量的数量,m_need 和 s_need 则用于跟踪线程尚需的资源数量。在系统调用中,加入了对死锁的检测逻辑,确保在尝试获取新资源之前检查当前资源的可用性。如果发现潜在的死锁情况,则会拒绝资源请求,并返回相应的错误代码。此外,还实现了调整资源需求的方法,以便在资源分配和释放时动态更新状态。

前言

我是一位已经参加工作有12年的资深工程师, 职业生涯中曾经担任过 CTO, 也做过总架构师. 精通 5+ 门编程语言, 对 10+ 门编程语言有过万行生产环境编码经验.
但因为自己毕业于一所大专院校, 在大专学习期间学校并没有开设过这些计算机系基础课程. 在多年工作中我时常好奇:

  1. 应用程序的内存是如何分配的
  2. 应用程序代码是如何启动的
  3. 应用如何与操作系统进行交互
  4. 操作系统是如何管理硬件资源的
  5. 多线程是如何实现的
    等等问题.
    抱着求知的心态, 我报名参加了这次的操作系统训练营.
    这些基本能力, 对应用开发者来说就像超市货架上每天都能“生长”出来的食物, 操作系统是如何把他们实现的, 我想要了解这个过程.

我曾在2021年参加过一次学堂在线的操作系统课程, 但是因为工作和家庭琐事繁忙, 最终没有完成. 这次我希望通过这次训练营, 继续学习操作系统的知识, 并且完成所有的实验.

我始终贯彻着一句话: 计算机是一门实践工程学科, 不管看的听的再怎么醍醐灌顶, 写不出来就是没学会.
学校的操作系统课程, 大多数都是讲理论, 缺乏实践. 这次训练营, 我希望能开发自己的内核程序, 并烧录到自己的硬件上运行.

第二阶段

Lab 1

这个实验主要是实现一个简单的多任务系统. 通过这个实验, 我理解了硬件是如何在不同的特权级之间切换的, 以及操作系统是如何管理这些特权级的.

Lab 2

这个实验启用了分页机制, 我学习到了地址空间的概念, 应用程序只需要关心自己的地址空间, 而不需要关心其他应用程序的地址空间. 同时我也学习到了内核是如何管理这些地址空间的.

Lab 3

进程: 学习到了进程是如何创建的, 以及进程是如何执行的.

Lab 4

文件系统: 学习到了文件系统是如何与物理存储设备交互的, 块存储设备是如何存储文件与数据的.

Lab 5

多线程: 学习到了操作系统是如何实现多线程的, 以及多线程之间如何通过锁来通讯.
通过课程实验: 我学习了如何用银行家算法检测死锁.

总结

通过这门课程我学习到了很多操作系统的基础知识, 包括:

  • 裸机程序是如何启动的
  • 应用程序内存是如何分配与隔离的
  • 操作系统是如何操作硬件的
  • 应用程序是如何与操作系统进行交互的
  • 文件系统是如何实现的
  • 等等…
    同时我也发现课程框架代码存在许多值得改进的地方, 所以我在尝试编写自己的内核程序.

阶段1:语言学习

rust 是一门非常有意思的语言,它吸收了很多现代编程语言的特性,特别是来自函数式编程的许多特性,比如 默认不可变,模式匹配,流 api,以及 trait,可以以一种不同的方式去抽象与编码。
rust 的“人体工程学”做的也非常不错,有许多语法糖与内置宏,匿名函数的写法也足够简洁,在学习过程中感觉非常有趣。

在语言学习过程中重点学习了基本的语法以及一些库函数的使用,对语言周边,比如 clippy 的使用还不够好,build.rs 也不太会写,多线程编程也不够熟练,希望接下来的学习当中能再注意一下重点学一下这些东西。

阶段2:rcore labs 学习

rcore 今年秋冬的任务是除了进程间通信以及io设备没有做过多要求,其余的每章都有一个编程练习,在我看来,编程练习的难度是适中的,但是依然要求完整的理解整个代码框架,对学习操作系统内核很有帮助。

前三章作为引子一步一步从裸机程序到一个批处理裸机程序的内核,后面几章的顺序是:虚拟内存(地址空间),进程,文件(持久化存储),再到并发。课程顺序与传统授课顺序不太一样,在我个人看来是一种由易到难的渐进学习过程:因为第八章的线程和并发编程真的感觉好难 debug 😂,在这章上浪费的时间也比较多。

印象最深刻的是对文件系统的讲解,将文件系统作为一个库抽象出来,内核也只是管理文件系统提供的 Inode 接口,以此来区分在内存中的 inode 和磁盘中的 inode,这样一层从 BlockDevice 到 FileSystem 再到 rcore-kernel 最后被封装为一些系统调用,这样层层抽象的写法看起来真的挺赏心悦目的哈哈,而且对知识的学习与综合起来也方便许多。

另外还有虚拟内存,内存模型也是内容量非常庞大的一章,需要理解 memory-set, map-aera, pagetable-entry, 以及物理地址和虚拟地址,物理页号和虚拟页号之间的关系,

抽象之下是和底层处理器的操作,内联汇编,riscv 库,以及 C ffi,rust 都支持的很好。处理 trap 的汇编函数也很有意思,以及一些 riscv csr,学习的同时也去了解了一下 riscv。不过本次学习,对rv要求的不是很多,更多的是以问答作业的形式去提问,不知道后面的阶段会不会有深入的机会。

2024二阶段总结

在这一阶段,我对我的学习可以划分两个比较明显的时期,一个是前期比较懵懂地入门,一个是中后期的速通。

Read more »

Rustlings

这是我第一次刷Rustlings,还是很有收获的。最后新增的一些算法题也很有意思,不过整体来说还是适合有Rust语法知识基础的人学习。最后几个算法题刷的很慢,我的数据结构和算法学的不是很好,在这里有学到很多。不过我也参考了许多资料。如果有人看的话,也算一点收获叭。整体来说Rust写起来很爽!

参考资料

Rust语法基础

初学者应该主要从三个方面了解Rust:Rust语法基础,Cargo以及Rust的标准库和官方文档。

Rust 程序设计语言 - Rust 程序设计语言 简体中文版

这是社区推荐的的The Rust Programming Language的中文翻译版本。主要讲Rust语法基础,国内有开源作者撰写了Rust语言圣经两本书的内容比较相似,后者语言比较生动,内容也比较丰富,前者的话语言精炼一些,个人比较推荐去读前者,读不懂的时候再去看Rust语言圣经的版本,会有新的收获。

The Cargo Book

Introduction - The Cargo Book

The Cargo Book是关于Cargo的一本书,初学者可能只会使用到Cargo的一少部分命令和参数,但实际上,Rust受到广泛关注的一个原因,就来自于强大的构建和包管理工具Cargo,值得注意的是这本书的中文翻译版本最后的更新时间是2019年,相关的内容和英文最新版差别比较大,最好读英文版本。

Rust标准库文档

List of all items in this crate

Rust的标准库文档涵盖了基础阶段大部分的内容,结构体、宏、智能指针等等都在标准库文档中有详细的说明,Rust程序相比其他许多语言确实比较在学习和编写上难度更大 ,但是Rust设计者们也在极力减少开发者的心智负担,对于一些数据类型和结构,标准库中定义了一些好用的方法和属性,方便大家学习和使用。另外,标准库中也定义了一些基本的API接口,方便开发。总之Rust标准库是一座宝库。

Rust

crates.io: Rust Package Registry

crate.io是提供了诸多Rust开发者开发的库,可以直接在cargo.toml里面配置库名和版本就能使用,很方便,基础阶段Rust标准库文档是小宝库,在后续进阶开发阶段,crate.io就是名副其实的大宝库。言下之意是,基础阶段暂时不用看这个。

Youtube

Rustlings 5.0 | Intro | Learn Rust Interactively

**目前国内的Rust学习资源还在初级阶段,B站上暂时没有很完善的教程,推荐一个油管博主的视频,讲到比较陌生不好理解的地方,博主会把Rust基础教程和标准库文档贴出来,对着讲解,还是很有收获的,还能教你怎么快速找到自己需要查的知识点。但是这个博主是之前录的视频,没有训练营版本Rustlings最后一些练习的讲解。

Rustlings

这是我第一次刷Rustlings,还是很有收获的。最后新增的一些算法题也很有意思,不过整体来说还是适合有Rust语法知识基础的人学习。最后几个算法题刷的很慢,我的数据结构和算法学的不是很好,在这里有学到很多。不过我也参考了许多资料。如果有人看的话,也算一点收获叭。整体来说Rust写起来很爽!

参考资料

Rust语法基础

初学者应该主要从三个方面了解Rust:Rust语法基础,Cargo以及Rust的标准库和官方文档。

Rust 程序设计语言 - Rust 程序设计语言 简体中文版

这是社区推荐的的The Rust Programming Language的中文翻译版本。主要讲Rust语法基础,国内有开源作者撰写了Rust语言圣经两本书的内容比较相似,后者语言比较生动,内容也比较丰富,前者的话语言精炼一些,个人比较推荐去读前者,读不懂的时候再去看Rust语言圣经的版本,会有新的收获。

The Cargo Book

Introduction - The Cargo Book

The Cargo Book是关于Cargo的一本书,初学者可能只会使用到Cargo的一少部分命令和参数,但实际上,Rust受到广泛关注的一个原因,就来自于强大的构建和包管理工具Cargo,值得注意的是这本书的中文翻译版本最后的更新时间是2019年,相关的内容和英文最新版差别比较大,最好读英文版本。

Rust标准库文档

List of all items in this crate

Rust的标准库文档涵盖了基础阶段大部分的内容,结构体、宏、智能指针等等都在标准库文档中有详细的说明,Rust程序相比其他许多语言确实比较在学习和编写上难度更大 ,但是Rust设计者们也在极力减少开发者的心智负担,对于一些数据类型和结构,标准库中定义了一些好用的方法和属性,方便大家学习和使用。另外,标准库中也定义了一些基本的API接口,方便开发。总之Rust标准库是一座宝库。

Rust

crates.io: Rust Package Registry

crate.io是提供了诸多Rust开发者开发的库,可以直接在cargo.toml里面配置库名和版本就能使用,很方便,基础阶段Rust标准库文档是小宝库,在后续进阶开发阶段,crate.io就是名副其实的大宝库。言下之意是,基础阶段暂时不用看这个。

Youtube

Rustlings 5.0 | Intro | Learn Rust Interactively

**目前国内的Rust学习资源还在初级阶段,B站上暂时没有很完善的教程,推荐一个油管博主的视频,讲到比较陌生不好理解的地方,博主会把Rust基础教程和标准库文档贴出来,对着讲解,还是很有收获的,还能教你怎么快速找到自己需要查的知识点。但是这个博主是之前录的视频,没有训练营版本Rustlings最后一些练习的讲解。

有关于Rust基础,是笔者在做毕设时因需要而学的,故而阶段一没有花费什么精力。但是对于阶段二,情况则截然不同。笔者没有听从项目文档的建议,选择了单枪匹马完成所有5个任务,期间也出现了许多迷之错误…

对于Rust这门编程语言来说,笔者个人认为,虽然它可以被用来编写底层的代码,包括bootloader、UEFI、OS内核等等,但是它的编译器已经决定了Rust的上限。笔者不才,曾经研究过一部分Rust的反编译分析,发现Rust对于栈内存的使用似乎非常奢侈。与C语言不同,Rust对于Shadowing的处理不是复用原来的栈内存空间,而是直接使用新的空间。另外Rust的结构也相比C臃肿很多,这就导致Rust作为底层架构的开发语言时,使用的内存空间要远多于C语言,粗略估计,实现同一种功能的情况下,Rust平均需要比C至少多1倍的栈内存空间。在时间效率上,Rust要实现与C语言相同的时间效率,编写的unsafe代码定然不少,对于开发人员的要求也必然不低,在这一点上,Rust相较于C可能并没有提升太多的编程效率。因此,使用Rust与C编写底层代码各有千秋,Rust定然无法取代C,而C也必然要接受Rust的发展。当前,Rust底层生态严重不足,导致上层建筑难以跟进,一旦Rust适配了足够多的ISA与外设,将大大方便底层中较高级别的编程开发,但对于MCU等内存需求、效率需求高的场景,C依然是不可替代的选择。

在此附上笔者的blog网址,其中有之前编写的9篇Rust逆向分析blog:网址界面丑了点,不要在意

根据笔者对于Rust反编译的研究理解发现,Rust的各类结构具有一定的特征,在进行调试时,如果能够识别出此类特征,就能将汇编代码与源代码相对应,从而大致恢复源代码的执行逻辑,甚至直接逆向还原出源代码。正是因为笔者对Rust汇编的理解,在本次实验阶段二中的调试才能更快地找到问题所在。

rCore 总结

实验文档的第一句说:从零开始写一个内核,但后来发现并不是让学生真的从零开始写,只是在写好的内核上添加功能,有一点点失望,但还是学到了很多。

本次实验让我学到了:

  • Rust 的一些编程技巧,如 UPSafeCell。
  • RISC-V 的特权级机制
  • 使用上下文保存/恢复来进行多道程序调度
  • SV39 多级页表机制及在内核中的实现
  • shell 的原理:fork + exec
  • 文件系统的原理:inode + …
  • 并发同步互斥的原理

虽然没有感觉理解得非常深入,但感谢 rCore 带我在操作系统领域入了门,让我对之后的阶段有更多期待。