Skip to content

Commit d24de66

Browse files
committed
add 2025s-oscamp-xxxuuu
Signed-off-by: xxxuuu <[email protected]>
1 parent 87a129f commit d24de66

File tree

1 file changed

+177
-0
lines changed

1 file changed

+177
-0
lines changed

source/_posts/2025s-oscamp-xxxuuu.md

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
---
2+
title: 2025s-oscamp-xxxuuu
3+
date: 2025-05-22 22:54:44
4+
mathjax: true
5+
tags:
6+
- author:xxxuuu
7+
- repo:https://github.com/LearningOS/2025s-arceos-xxxuuu.git
8+
---
9+
10+
偶然有天看到 Rust 中文社区的公众号转发了一个开源 OS 训练营的消息,点进去一看原来是 rCore,早就听过 rCore 这个项目,但一直没来得及学习,自己也对 Rust 在嵌入式开发和内核中的应用很感兴趣,于是转发到群里忽悠了几个朋友一起组团参加
11+
12+
<!-- more -->
13+
14+
训练营总体分为四个阶段,前三个阶段是练习和实验为主,第四阶段是偏自主探索的项目部分
15+
16+
## 第一阶段
17+
18+
第一阶段是 Rust 基础的学习,基本是完成 rustlings 训练即可,由于之前已经有了一定 Rust 基础,所以没什么障碍很快就做完了
19+
20+
## 第二阶段
21+
22+
第二阶段是 rCore 的实验,跑在 RISC-V 架构上。ch1 是如何运行一个裸机程序的简单指导,正式的内容从 ch2 开始
23+
24+
### ch2 批处理系统
25+
26+
ch2 实现了一个批处理系统:
27+
28+
- 允许按顺序执行多个应用程序,这些应用在构建时被静态链接到内核 binary 中。启动应用时复制其 binary 到入口地址 `0x80400000` 上,然后保存上下文切换状态跳转执行
29+
- 实现了特权级切换机制:
30+
- 应用在 U 特权级运行,通过 `ecall` 触发 Trap 切换到 S 特权级
31+
- 内核处理系统调用或错误后,通过 `sret` 指令返回U特权级继续执行
32+
- 提供了错误处理机制:
33+
- 当应用访问非法地址或执行非法指令时触发 Trap
34+
- 内核可以终止当前应用并执行下一个
35+
36+
核心组件包括:
37+
38+
- AppManager:负责应用的加载和运行管理
39+
- TrapContext:保存 Trap 发生时的上下文信息,包括通用寄存器和 CSR
40+
- 两个特权级切换的关键函数:`__alltraps`(保存上下文) 和 `__restore`(恢复上下文)
41+
42+
### ch3 多道任务与分时多任务
43+
44+
相比 ch2 执行完一个才执行下一个的批处理系统。ch3 实现了分时多任务
45+
46+
有几个主要变化:
47+
48+
1. 需要能同时加载多个程序,ch2 每个程序被加载到固定的地址中运行。ch3 则为每个程序划分了单独的地址范围
49+
2. 任务切换,ch3 需要在多个任务间切换,要为每个任务保存单独的上下文,任务间切换时也是在 Trap 中进行,通过 `__switch` 保存当前任务上下文,设置新任务上下文,`__restore` 再回到用户态。流程变成 `__alltraps``__switch``__restore`
50+
3. 提供了一些新的系统调用允许用户态程序自行让出,例如 `sys_yield`,以及为用户态程序添加了对应的状态以进行管理和维护
51+
4. 设置时钟中断,在中断中切换任务,实现抢占式调度
52+
53+
Lab:
54+
55+
- 实现一个系统调用 `sys_trace`,可以读取、修改内存地址,以及追踪系统调用次数
56+
57+
比较简单,在 `TaskManger` 里加一个 map 统计每个 task 的调用计数即可
58+
59+
### ch4 地址空间
60+
61+
ch4 实现虚拟内存,在前面的部分中,我们都是直接读写的物理内存
62+
63+
虚拟内存并不是完全由内核纯软件实现的机制,而是软硬件结合的。RISC-V 64 中包括 SV39 和 SV48 两个虚拟内存模式
64+
65+
SV39 模式中,所有 S/U 特权级下的访存都被视为 39 位的虚拟地址。页面大小 4KiB,页表项大小 8 字节,能支持 56 位($2^{26}$ GiB)大小的物理地址
66+
67+
内核现在需要维护页表本身的信息,即元数据的存储,包括页帧,虚拟页和物理页的映射等
68+
69+
另外,内核现在读写的也是虚拟内存。rCore 采用了内核和应用地址空间隔离的设计(与 Linux 中应用地址空间和内核映射在一起的设计不同),在特权级上下文切换时需要切换地址空间。过程中必须确保切换前后的正常运行,例如切换后的下一条指令地址也需要能正常访问,这里通过 trampoline 技巧实现了这一点
70+
71+
Lab:
72+
73+
1. 重写 `sys_get_time``sys_trace`,参考 `sys_write` 即可
74+
2. 实现 `mmap``munmap`,但不包括文件和 I/O 相关的映射,类似于 linux 中 `mmap` 带上 `MAP_ANON` flag。参考每个 Task 维护的 MemorySet 内容即可
75+
76+
### ch5 进程及进程管理
77+
78+
ch5 主要实现了进程管理机制,引入了一个简易的 shell 来运行应用程序。核心改动包括:
79+
80+
- 新增了三个核心系统调用:`fork``exec``waitpid`
81+
- 进程机制的改进:
82+
- 链接和加载机制改为按应用名进行
83+
- 引入进程控制块(PCB),将应用 id 改为 pid,并维护子进程信息
84+
- TaskManager 职责部分转移到 Processor,支持每核心一个 Processor(当前仅实现单核)
85+
86+
Lab:
87+
88+
- 重写 `sys_get_time``sys_mmap`/`sys_munmap` 系统调用,适配新的进程结构
89+
- 实现 `spawn` 系统调用,效果上类似 `fork``exec` 的组合(但没有复制地址空间的必要了)
90+
- 实现 `stride` 调度算法,注意到调度时通过 `TaskManager``fetch()` 来获取任务,在这实现即可
91+
92+
### ch6 文件系统与 I/O 重定向
93+
94+
ch6 实现了文件系统与 I/O 抽象。核心包括:
95+
96+
- 使用第三方的 VirtIO 驱动库实现块设备 I/O
97+
- 设计了单层目录的文件系统 easy-fs,独立为一个单独 crate 实现:
98+
- `DiskInode` 作为磁盘上的索引节点
99+
- `Inode` 作为内存中暴露给用户的结构体
100+
- `DirEntry` 维护文件名称和 inode_id 的映射
101+
- 应用程序改为以文件形式动态加载,不再直接链接进内核
102+
103+
Lab:
104+
105+
- 实现 `linkat`/`unlinkat` 系统调用:
106+
- 只要理解了文件系统的底层数据分布就比较简单了
107+
-`DiskInode` 中添加链接计数
108+
- 实现文件的硬链接创建和删除
109+
- 实现 `fstat` 系统调用:
110+
- 可以扩展 `File` trait 增加 `stat` 接口,为 `Inode` 添加 `inode_id` 字段以获取文件信息
111+
112+
另外需要注意文件系统中的锁使用,这里不支持重入,需要避免嵌套 lock 导致死锁
113+
114+
### ch7 进程间通信
115+
116+
ch7 扩展了进程间通信机制,包括:
117+
118+
- 管道(pipe)机制的实现,允许进程间通过管道进行数据传输
119+
- I/O 重定向功能,使进程能够重定向其输入输出流
120+
121+
这些功能为进程间的数据交换和通信提供了基础设施支持
122+
123+
### ch8 并发
124+
125+
ch8 主要实现了线程管理和锁机制:
126+
127+
- 线程管理:在 `TaskControlBlock` 中实现线程,与进程共用地址空间但有独立栈指针
128+
- 锁实现:包含两种锁机制:
129+
- `MutexSpin`:非阻塞锁,使用自旋等待和 yield
130+
- `MutexBlocking`:阻塞锁,维护等待队列进行线程阻塞和唤醒
131+
132+
Lab:
133+
134+
- 实现死锁检测和避免,文档中给的是类似银行家的算法
135+
136+
### 总结
137+
138+
阶段 2 更多是读文档和熟悉 rCore 代码,实际开发工作量不高,每个实验基本不到百行。但实验文档写得非常详细,质量很高,每个设计也很不错
139+
140+
也体会到 Rust 在内核开发上,得益于强大的类型系统和零成本抽象设计,确实比 C 更加具有表现力,体验更好
141+
142+
## 第三阶段
143+
144+
三阶段是 ArceOS,包括六个实验
145+
146+
### **print_with_color**
147+
148+
提供一个带颜色的 `println!` 宏即可,主要是熟悉 ArceOS 整体结构
149+
150+
### **support_hashmap**
151+
152+
此时内核里已经有了 allocator,所以可以使用 `alloc` 里的 `Vec` 等集合(`std ::vec``alloc::vec` 的 re-export),但 hash map 的 hash 函数依赖随机数生成器,这需要系统提供,所以不在 `alloc` 中,这里就需要我们添加 `HashMap` 支持
153+
154+
实现上可以用简单的多项式 hash 套 `Vec<Vec<T>>` 即可,代码百行左右,Trait 的设计可以参考标准库里的 `Hash``Hasher`
155+
156+
### **alt_alloc**
157+
158+
实现一个简单的 bump 内存分配器,同时作为字节分配器和页分配器。这个分配器很简单,只需要递增分配内存空间即可,甚至不用考虑回收实现
159+
160+
这里也可以看出 ArceOS 的可插拔设计能够很灵活地替换各个组件
161+
162+
### **ramfs_rename**
163+
164+
为 ramfs 支持 rename 操作,这个地方比较坑的是跨 mount point 的处理有问题,这导致在 rename 时无法跨 mount point 操作,但涉及底下框架无法修改,只能实现成同目录下的操作
165+
166+
### **sys_map**
167+
168+
实现 mmap,相比 rCore 的这回是可以映射到文件上了。但实现是让 mmap 时直接把文件内容写到内存里,Linux 里的 mmap 应该是一个特殊的缺页处理函数,只有当缺页时才会去读入这个文件内容
169+
170+
### **simple_hv**
171+
172+
这里开始涉及虚拟化的内容,要给 vmm/hypervisor 添加 guest 的两个 trap 处理,一个是特权指令,一个是缺页。理论上这里应该要解析 guest 指令然后在 vmm 执行后再把结果设置回去,但实际上只要给 guest 寄存器写死固定值即可
173+
174+
### 总结
175+
176+
三阶段难度比预期要低,ArceOS 本身还是非常不错的项目,但实验的测试用例有点过于水了,导致很多不完善甚至错误的实现也能轻松通过测试。从教学角度个人感觉体验不如阶段二,但可以学习 ArceOS 本身的设计和代码,对比 rCore 的差异和改进
177+

0 commit comments

Comments
 (0)