2024 春夏季开源操作系统训练营 日记
2024 春夏季 操作系统训练营正在进行中:4/7-6/30。
注意:本文所有观点仅为个人想法,不具有任何法律效益。
相关资料:
Day 1-5 2024/4/7-11
这几天主要就是在写 rustlings。完成到 102/110。
我越学越觉得 Rust 的很多特性其实是为了给他严格的所有权机制打补丁。最明显的就是生命周期了,还有诸如 unsafe
等。刚好最近同时也在一家公司实习做操作系统内核开发,正在使用 C 语言,因此对这两门语言的风格深有所感:
- C 的原则就是「完全相信使用者」,因此你可以用 C 实现几乎所有操作,非常自由。但是为了安全,必须人为设计一些规范来进行约束。
- Rust 的原则则是「完全不相信使用者」,所以你会发现 Rust 的很多语法都是为了约束程序员,强迫程序员写出安全的代码。但是,有些时候编译器还是不够聪明,或者说是无法进行判断,因此必须开点绿灯——
unsafe
。
在 Rust 身上可以找到很多为了弥补所有权机制而设计的语法,因此在学习的时候才会觉得 Rust 的语法很复杂。不过这种「语言规定好的规范」对于多人之间的项目合作,特别是开源来说,就是一种优势了。相比于 C 语言项目之间可能存在代码风格相差巨大的情况,Rust 写出来的代码基本上不会有太大的风格差异,这样在参考别人的代码,以及贡献代码的时候就会更为轻松。
Day 6 2024/4/12
完成:
- 完成 rustlings。
问题:
- 宏
#[derive]
和#[link]
还不太熟悉作用与用法。
笔记:
- 101-102 中的
NonNull()
是一个裸指针类型,但是实际使用的时候需要用.as_ptr()
来获取成为真正的裸指针,并且需要在unsafe {}
中操作。 NonNull()
的.as_ptr()
和.as_ref()
的区别是:前者返回的是指针,后者返回的是引用。换句话来说,前者需要解引用才能使用,后者直接使用就好。let mut b = &mut b
中,前面的mut
代表b
可以绑定到另一个变量上,后面的mut
才是代表可变借用。- 使用借用需要用
*
来解引用,不过如果调用了方法的话,Rust 会自动解引用:(*a).fun()
可以简化为a.fun()
。 let a = || {};
的意思并不是计算闭包的值然后绑定给变量a
,而是将这个闭包绑定到a
,后续就可以用a()
来调用闭包。
Day 7-8 2024/4/13-14
这两天在弄别的事情,没有进行 OS Camp 训练。
Day 9 2024/4/15
完成:
- 准备开始学习 rCore,配置相关环境,阅读第零章。
- 阅读第一章,并完成实验。
问题:
-
#![no_std]
一直报can't find crate \
test`` 的错误。
Day 10 2024/4/16
笔记:
- SMP 代表一种多核内存结构架构。具有多核的操作系统在启动的时候会有一个主核 0 先启动,然后再依次启动其他的从核。每次复位或者加电后,主核总是立即启动,从核总是延时启动。
Day 11-16 2024/4/17-22
这段时间在弄别的事情,没有进行 OS Camp 训练。
Day 17 2024/4/23
完成:
- 完成训练营 rCore 环境配置。
- 阅读讲义 ch1-2。
笔记:
- 操作系统用户态和内核态的切换是通过机器指令在硬件上实现的,在 RISC-V 中这个指令是
ecall
。因此,当一个应用程序想要进行系统调用的时候,需要按照约定将参数放入寄存器中,然后执行ecall
指令,这时硬件会自动保存当前 PC 寄存器的值到 CSR 中(特权级别的控制和状态寄存器,如mepc
或scause
),然后设置异常原因寄存器,以指示发生了环境调用异常,接着跳转到预设的异常处理程序地址,这个地址在启动时由操作系统或监控程序设置在mtvec
(机器模式陷阱向量基址寄存器)或stvec
(监督模式陷阱向量基址寄存器)中。至于其他通用寄存器的值,则需要操作系统来进行保存和还原。
Day 18 2024/4/24
完成:
- 在 mac 上配置实验环境,撰写了一篇博客:在 macOS 14 (M1 Pro) 编译 QEMU 7.0.0。
- 撰写第一阶段总结 blog。
- 阅读完 ch2
Day 19 2024/4/25
完成:
- 阅读完 ch3。
- 完成 lab1 实验。
Day 20-22 2024/4/26-28
这期间在做别的事情,没有进行训练营学习。
Day 23 2024/4/29
完成:
- 完成 lab1 实验报告的撰写。
- 开始学习 ch4。
Day 24 2024/4/30
进度:
- 继续学习 lab 2。
笔记:
- 内核态采用恒等映射,用户态不采用。
Day 27 2024/5/3
完成:
- 完成 lab2 (ch4) 实验
- 进行
笔记:
- GDB 在实现了切换地址空间后就无法进行调试了
- 折腾了半天终于把分页机制弄清楚了……我之前一直在想缺页会怎么办,但其实并不会发生缺页,因为我们没有实现虚拟内存!所以一旦缺页就一定是非法内存访问。
- 恒等映射只是在内核态使用,而且就是会和用户态分配的内存发生冲突。这样就可以直接访问物理内存,从而能够在内核态对用户态的数据进行访问。而用户态的页表采用的是 Framed,所以不能访问内核态和其他用户态的内存。
- 当我们尝试在代码中解引用的时候,就看当前 satp 是哪个地址空间,硬件就会已经对应的页表去访问物理地址。
Day 30 2024/5/6
进度:
- 完成 lab3 实验。
问题:
-
当 git checkout 到任意一个之前的提交后,
make run
无法正常编译。是
os
目录下的Makefile
文件中的第 26 行:1
CHAPTER ?= $(shell git rev-parse --abbrev-ref HEAD | sed -E 's/ch([0-9])/\1/')
这里会将目前 HEAD 所指向的节点的名字用正则表达式取出目前所在的 ch,然后在第 50 行传入
user
目录的make run
来编译对应 ch 的应用程序。如果目前 HEAD 所指向的节点不是 ch* 的话,那么这里的正则表达式就会失效,然后就会传出一个非法的参数,影响
user
目录的编译。- 解决方法:在编译的时候手动指定
make run CHATPER=[0-9]
就能解决。
- 解决方法:在编译的时候手动指定
Day 31-34 2024/5/7-10
进度:
- 完成 lab4 实验。
Day 35-37 2024/5/11-13
没有进行操作系统相关学习。
Day 38 2024/5/14
进度:
- 完成 lab5 实验。