首先感谢各位老师提供的学习资源和平台
2023开源操作系统训练营第二阶段总结报告-Beavercat7.md
这是我第一次参加这种形式的训练营,感觉相当于收获而已,这种体验更让我有强烈的兴奋感,希望借此机会多接触开源,在开源社区中不断精进自己的技术。
在lab1中,利用结构体TaskInfo来追踪一个进程的任务状态,分别调用了哪些系统调用,以及对应的次数,距离任务第一次被调用的时长,然后来完成sys_task_info,这个相对简单,只要了解系统调用的大致流程就行,知道add_syscalltimes要放在syscall函数体内,知道大致代码框架就能写出来。
但是最重要的是,问答题检测了你对于特权级切换的理解,这里才是本章的重点和难点。利用汇编语言操作寄存器。
在lab2中,先是要重写sys_get_time和sys_task_info,因为一旦引入虚存机制以后,就需要将用户态的虚拟地址根据当前的进程页表来转换成实际的物理地址,
这里需要去调用获取用户根页表地址的接口,然后解引用指针修改就可以了,
接下来mmap和munmap匿名映射,除了一些错误要特殊判断以外,主要是利用insert_framed_area函数,在一些循环判断逻辑段是否有效(find_pte函数)就能解决。
在lab3中,sys_spawn主要看fork+exec来仿照写,这里不必像fork一样复制父进程的地址空间,是因为exec本身就要替代原进程(包括地址空间),没必要了。
然后stride调度算法,首先在TaskBlock添加优先级和stride,首先要把所有
初始化和要更新的加上一个stride更新,其次在task.add中,维护一个单调递增的队列。
2023开源操作系统训练营第二阶段总结报告-Luv_Ray.md
2023rCore 训练营第二阶段总结
其实很早以前就知道 rcore 这个项目了,之前学 rust 时做过 Stanford 的 CS 110L。
不过由于之前做过 MIT 的 xv6,在操作系统 lab 方面定位有些重合,就没有去做 rcore。
在今年暑假想要找些 rust 项目看时,在 github 上找到 arceos,后面才知道 rcore 只是社区众多项目中的其中一个,这次报名也是希望能进一步参与后面的项目。
实验
rcore 的实验其实挺简单,重要的是理解系统概念以及现有的 api,这一点其实和 xv6 有点相似。
然而这也对应了另一个问题,只做实验其实不一定有很好的学习效果,有可能只根据类型试着调用 api 就能通过测试,学习的深度某种程度上只能靠自觉来保证。
另外,感觉各个 lab 的测试样例并不算强,其实编写 kernel 是很容易在 corner case 上出各种漏洞的,希望自己的实现没太大问题。
由于 rust 本身的约束,产生漏洞的风险比 c 要小,但是在使用 unsafe 时,由于 rust 本身做了更多抽象,直接接触底层时就会有更大的潜在隐患。
比如在 sys_task_info 中,需要将 &TaskInfo 转换为 u8 的 buffer,我使用了 as 来直接进行转换
1 | let k_ti = &TaskInfo { |
转换为裸指针是 safe 的,但是访问裸指针时很容易触发未定义行为,unsafe 里有很多隐秘的 unsound 情况。
实际上,对于 TaskInfo 数组:
1 | /// Task information |
其内存布局是不确定的,编译器甚至不保证 status,syscall_times,time 是按顺序排布的,添加 #[repr(C)] 才能保证会是我们想象的类似 C 语言的内存布局。
同时,这个结构体中的元素比较简单所以没问题,但是直接裸指针转换是很不好的风格,在面对内部可变性、内存对齐、未初始化元素时极易访问未初始化内存,对未初始化内存的任何写入和读取都是UB。
在 bytemuck 提供了对于类型转换的更好方式。
类似这种,其实很有多潜在的需要解决的问题都被忽略了,实验中保留了比较核心的概念和问题,但是仔细挖掘文档和现有的代码的话,会有更多的值得学习和思考的问题。
总结
rcore 是个很好的 rust 和 OS 项目,国内的开源教学项目实在很少,大部分学生都会去学习 MIT、CMU 等学校的开源课程,不过国外的英文课程对国内学生有一定门槛,会有一些额外的负担。
我之前写过的 Stanford 的 rust 课程在 20 年后的版本都不公开代码了,难免说开源的行为会持续多久,所以国内还是很需要这种优质的开源课程来弥补空白的,在此感谢各位老师和助教的付出,也希望未来在各个方面能有更多的优质开源课程。
2023 开源操作系统训练营第二阶段总结报告 - Misonoi
楔子
第一次看到这个训练营是在QQ频道上面的rust频道, 正巧当时有人在说OS训练营的问题, 正巧也确实对实现操作系统很感兴趣就参加了。 在此之前其实已经有rust的基础和使用rust开发项目的基础。
关于rust
rust是我非常喜欢的语言, 可以说是目前最喜欢的语言也不为过。诸如C, C++, java, python, go, kotlin, js这些语言我都学过, 也用它们或多或少的写过东西, 但是其中rust是最深得我心的。
比如说它的枚举中可以存放关联值, 这样就可以通过枚举的类型来进行分发; 还有它强大的泛型能力, 在学习rust之前其实我对泛型编程的理解一直不太深, 直到我学习了rust, 参考了rust标准库的源码, 对泛型的理解才更上了一层。
一阶段的许多东西都在圣经里面有所提及, 但是多出的那几个test确实是我不曾知道的, 后面我又去了解了一些rust插件开发, 对于有反射的语言, 可以使用反射动态读入来进行插件化。 而没有反射的诸如rust这样的编译型语言, 可以利用FFI进行插件化, 也就是把rust打包成动态链接库, 比如说用#[no_mangle]来阻止混淆函数名称, 然后通过调用库的方式调用链接库中的函数, 这样就可以实现插件化。 毕竟不能添加用户自定义的插件什么应用是完整的呢?, 所以一阶段确实是对我也很有帮助的。
关于二阶段
一阶段我用一个晚上速通之后就开始看文档, 也大概花了两周时间。
对于这个阶段我最大的感想就是, 理论需要与实践相结合。 在学习也学过操作系统这门通识课程, 当时也觉得课程里面说的什么比如特权级, 中断, 分页分段之类的学习起来并不难, 但是要是让我用代码来实现, 那必然是抠破脑袋都想不出来的。 直到现在我虽然是把所有的实验都完成了, 文档也看完了, 但是如果要我独立写一个操作系统, 也不一定可以。
我看的主要是v3的文档, 于我而言最有难度的是第四章和第六章, 分页管理和文件系统, 真的和其他章节不是一个难度。 不然即使如此, 要做完实验并不需要完全理解文档, 实验相对来说还是比较容易的。
关于各个实验
ch3
ch3是实现获取taskinfo的系统调用, 主要想法就是在运行过程中维护调用计数。 第一次遇到了爆栈空间的问题, 所以就给entry.asm的栈空间调大了, 事实证明也确实可以
ch4
ch4实现sys_mmap的方面, 我是通过参数分配对应的页表, 生成一块MapArea然后插入到MemorySet里面, sys_umap也只需要从MemorySet里面删除
ch5
ch5实现sys_spawn, 我觉得它与fork + exec的区别就是不用复制父进程的空间, 那么生成一块空白的空间按照exec处理即可
ch6
ch6实现sys_linkat, 在索引中加一个entry即可, 而sys_unlink我认为不容易删除一个entry, 我选择的是向里面写了一些非法数据, 让文件名读取不到这个节点
ch8
ch8实现死锁检测, 银行家算法需要提前获知需要的资源总数, 对于互斥锁我想资源数量就是1, 对于信号量就是对应的值
总体
rCore逐步递进, 从一个功能较少的小系统成为一个功能较多的大系统, 但是我感觉文档里面还有很多东西没有说的很清楚, 比如说rust函数和汇编中函数的互相调用, 参数和返回值传递之类的不是很明白。 除此之外文档还是非常全面的, 我感觉我主要的问题就是不太理解汇编和rust的交互。
另外就是在调试方面有一些问题, 不容易以代码级的方式调试, gdb调试也不太直观。
2023秋冬rCore训练营第二阶段总结-ChoHee15
2023秋冬rCore训练营第二阶段总结
以“进化”的形式来讲述操作系统确实很有意思,同时能让了解到一些os的历史(但代价是什么:每章都要合并代码啊喂
Lab1-ch3
这个syscall还是很直观的,加一些标志然后相应写一些函数就行了。(没想到几行代码合了三回
但是直接给个大数组感觉还是太粗糙了,拷贝一下感觉开销也不小的样子,但是也没想到其他好办法。
简答作业要求理解trap的过程,细读,细读。(写过pa的看到这个有种亲切感
Lab2-ch4
由于rcore用了双页表,内核代码也没法直接掏到应用的数据,得手动查一轮;sys_get_time和sys_task_info都需要往用户的某个地址写数据,都得重写一下儿(Nanos-lite:陌生的设计
mmap和munmap大都可以复用或者照猫画虎现有api搞出来。
Lab3-ch5
sys_spawn的处理就是新建进程但是要维护父子关系。(文档同时解答了我对于“为什么先得来个fork再exec”的疑问
stride算法也比较形象,和sys_taskinfo一样有想法了就容易实现,简单但是有效√
总结
总的来说二阶段读大于写,理解要干什么比较重要,有想法以后码一码调一调还是比较容易成的。(感觉rust比较难
rust也能保证不会出现太玄学的错误我才没有在ch4乱玩指针导致segmentfault捏
非常好rcore,使我的qemu spin√
2023-rCore-StageGuard-summary
首先感谢各位老师和学长能够创造出让大家接触并学习实战编写操作系统的机会,这在国内的大环境下是非常难得的,感谢你们的无私奉献。
第一阶段我们从 Rust 基础语法开始学起,在此阶段我巩固了我的 Rust 基础,理解了使用 Rust 语言作为编写操作系统的语言的优势。
相比于传统的 C 语言,Rust 的语法更加现代化,这对于一个 Java/Kotlin 人非常友好,可以方便地用到现代编程语言的技术,如闭包,迭代器,流式 API 等。而且 Rust 语言也有了出了名的安全性,几乎完美杜绝了垂悬引用和二次释放等危险操作。
但是,奇迹和魔法不是免费的。同样地,Rust 的语法和编码规则也更为复杂,我们编码必须完全遵循 Rust 所谓的借用和生命周期规则,而且也出现了许多新概念,这也是 Rust 比较劝退的地方。但是如果能坚持下来,我们将能享受到 Rust 语言带给我们的便利,享受编写高安全性代码的快感。
第二阶直接进入操作系统的学习,这还是第一次实际上手实现操作系统的内核功能,这注定是一个漫长的学习过程。虽然说是使用 Rust 编写操作系统,但接触了底层,难免会和汇编打交道。
首先要理解文档中的汇编代码才能做好基础准备,虽然在学校课堂上学习了特权级,CPU 中断这些知识,但是把他们体现在代码上还是有些难度。特别是切换特权级别这一过程,他们都直接修改了 pc 寄存器,直接跳转执行地址,我们不能用常规编程的程序流控制思路来理解。而 CPU 级别的直接跳转在 Rust 代码中的体现就是无条件地从这个流跳转到另一个流执行了,对于第一次接触这类操作的我产生了极大的震撼。
在理解了这个之后,接下来就是逐步实现操作系统应有的功能:进程调度,虚拟内存空间,文件系统,并发操作,IPC 以及 IO 设备管理。目前二阶段的实验主要还是基于 RISC-V 架构实现的,没有做到架构无关,而且这些功能也是直接嵌入到内核中,没有做成单独的模块。
在实现这些的过程中,我按照教程文档逐一理解代码,然后再将一些调试信息打印出来,直观感受一个操作系统底层运行起来的样子,然后逐步构建起一个庞大的系统架构,每一个模块都精密运作,这是设计师的浪漫。
这次 rCore 学习让我充分学习了操作系统的底层逻辑,也领会到了 Rust 语言的强大,希望后续可以更进一步学习。
2023秋冬季开源操作系统训练营总结-lispking
开源操作系统训练营第二阶段总结报告-江昊
- 在这学习的一个月里,我投入了大量时间和精力来学习Rust编程语言以及操作系统开发。我的目标是掌握这门高效、安全的语言,并借此机会深入了解操作系统的底层原理和实现。
第一阶段 Rust编程语言
- Rust语言基础:我通过阅读官方文档和相关书籍,进一步掌握了Rust的语言特性,包括内存管理、所有权系统、生命周期等。我还通过编写一些小的程序来巩固这些知识,比如实现简单的数据结构和算法。
- 在学习过程中,我也遇到了一些挑战。比如,Rust的所有权系统和生命周期机制让我感到有些困惑。通过阅读更多的文档和示例代码,逐渐好像克服了这些困难。
第二阶段 操作系统实现
- 操作系统原理:为了更好地理解操作系统开发,我深入学习了操作系统的基本原理,例如内核启动过程、地址空间、内存管理、进程管理、文件系统等。我通过阅读经典的操作系统教材(rcore book)和通过实践加深对这部分内容的理解。
- 在整个学习过程,以动手实践为主,用最简单的方式重新实践一遍,理解整个过程,细节,原理
实践1
- 按照教程动手实现最简单的系统,输出一行文字,目标是熟悉整个开发环境,编译,打包,启动环境,调试方式
实践2
- 熟悉一些rustsbi调用,riscv汇编指令,特权模式,完善一些日志输出,系统调用方式,了解内核各个段的地址,用途
实践3
- 实现系统调用处理,时钟中断处理,s->u及u->s的切换过程,栈的切换,任务的运行环境的保存及恢复,任务进程的调度切换
实践4
- 实现以sv39模式的地址空间管理,页表的管理,虚拟地址到物理地址的转换,在这个实践上花费时间比较多,特别是异常处理时入口地址,返回地址,任务空间的页表地址切换,及程序各个段的映射,按照rcore的实现trap_context是放在应用层地址空间的,基本上可以随意修改,不够安全,准备尝试把trap_context放到内核地址空间
实践5
- 实现了解进程信号的发送接收过程,在内核层触发应用层的回调函数,实际上是一次任务的切换,必须在当前进程调用系统调用后才能触发,了解各种锁通过任务调度的实现,进程间的通信,管道消息的发送接收
实践6
- 实现通过对接调用easy-fs对文件的读写,块设备驱动采用的是virtio接口,这块比较复杂,待进一步理解
实践7
- 尝试启动双核,对任务进行调度,主要涉及到公共资源的加锁,当前任务的结构指针存储到tp寄存器(x4),获取当前进程通过读取tp寄存器实现,trap_context和task_context加入对tp寄存器的保存和加载(针对系统调用和任务调度)
总结
- 回顾这个学习实践过程,我认为我对操作系统原理上的理解取得了很大的进步。但我也意识到,还需要不断的继续学习和实践。
- 我希望能够继续深入学习Rust和操作系统开发,参与更多的开源项目,提升我的技能和经验。同时,我也希望能够将我在学习过程中积累的知识和经验分享给更多的人,帮助更多的人一起进步。
- 总的来说,这个学习过程充满了挑战,但我收获了丰富的知识和技能。我对后面的学习充满了期待。
感谢老师和助教提供的帮助!!!
rCore Learning Summary 陈禹译
整体评价
我参与 rCore 训练营,是为了较深地学习、掌握 os 知识。此前,我学习了 NJU ics pa,对 riscv isa 即配套的简易版的 os 层有一定的理解,这使我能非常高效地理解 rCore 的文档内容。
我个人是比较推荐有点基础的人去阅读、完成 rCore 的。若对计算机组成原理或基本的操作系统知识不了解的话可能无法读懂前两章,导致无法理解内核代码,从而无法很好地理解后面章节的逻辑。有一定的基础之后,rCore 就会从一个比较低的起点开始慢慢构建一个功能强大的 os,稍微花点时间就能看懂全部的代码,而不存在“庞大而不可测的代码块”。此外,Rust 的 no_std 环境也特别友好,很多实用的东西(如 format、RefCell)都会提供;而且也提供了动态分配内存的需求接口,实现之后就能非常方便地使用各个智能指针和容器了,大大简化了 os 中细微逻辑的实现难度。
Rust 编译器的强大也大大改变了实验练习的“画风”,它要求我们必须在很好地理解 API 后,自己使用 unsafe 代码并保证接口是 safe 的,然后要想办法把代码组织起来,不让编译器报错。也就是说,Rust 的特性使得我们不得不深入地理解实验代码、需求才能完成练习。只要编译不报错,就离完成不远了,而且此时也往往说明我们并没有误用已有的代码。
rCore 之旅
前几章最有意思的是各个链接脚本。较好地组织链接脚本,才能不浪费空间同时正确地启动 os,而想要理解后面的章节的许多需求(如加载 app)也需要先较好地理解 boot 过程。
app 都是通过构建脚本自动生成链接脚本来加载到内核中的,这非常有意思,我称之为“交叉构建”(化用“交叉编译”)——要清晰地认识到,内核代码和用户代码是完全分离的,rCore 中依靠 user 来编译出可运行在内核上的代码,pa 则是使用 navy-app。这打破了我们平时思考的习惯。我们能在电脑上写代码、跑起来,其实是因为 windows 有能在自己上面跑的面向 windows 的编译器,它可能是自举的,也可能是用其他 os 交叉编译了一部分的。若是没写好这样的自给自足的编译器,就只能向 rCore 一样通过元编程来加载。
关于特权级切换时的上下文保护,我发起了一个讨论。
第三章引入时间片相关逻辑最大的意义是引入了中断,使得 os 的状态切换更复杂,要求更好地处理上下文关系。而第三章里有 lab1,使得我更加理解了 task 的数据结构构造(此时的 task 都被较简单地、笼统地管理,每个 task 并不能很好地自己管理自己,这在第五章(lab3)得到了改善)。
第四章,设计者让每个 task 都维护一个地址空间和一个页表,两者甚至是解耦的,使用一个地址空间时要显式指定究竟使用哪个页表,这大大增强了系统的可拓展性。而物理页帧分配器的抽象也十分令人惊叹,它是让丑陋的物理地址变得简单易用的第一步。此外,我觉得此章对跳板的解释并不是特别具体,因此我也发起了一个讨论。
第六章引入进程后的数据结构设计十分精妙,而配套的 lab 也“逼迫”我去理解这种设计,希望同学们也能亲自体会到。另外,我也学到了这一非常巧妙的 Rust unsafe 代码封装技巧:当原始指针作为结果时,可以将其转化为&'static mut,这样就不需要 unsafe 也能使用该数据:
1 |
|
之后的章节我也在逐步学习,to be continued…
Axsl666-2023秋季OS训练营第二阶段总结
Axsl666-2023秋季OS训练营第二阶段总结
首先感谢learningOS社区的所有贡献者。前几次训练营因为错过报名时间而未能参加,终于在2023秋季参与到了本次训练营活动。
实验一内容总结
多任务系统的简单实现
- 扩展
TaskControlBlock:start_timestartedsyscall_times
- 设计
TaskManager的公共接口add_syscall_timesget_current_task_info
- 在相应函数中更新 TCB 数据, 记录开始时间:
run_first_taskrun_next_task
- 在系统调用处理函数中调用
add_syscall_times更新 TCB 中的记录 - 完成
sys_task_info系统调用- 主要是调用
get_current_task_info并返回TaskInfo。
- 主要是调用
实验二内容总结
系统开启虚拟地址空间
重写 sys_get_time 和 sys_task_info
- 在内存管理中增加 translated_mut_ptr 函数,实现进程用户地址空间中的指针到可变引用的转换
- 其余类似 ch3 中的操作
mmap 和 munmap 匿名映射
- 内存管理接口
- delete_frame_area
- 任务管理接口
- mmap:调用insert_framed_area增加映射区域
- munmap:调用delete_frame_area删除映射区域
- 系统调用的实现
- 合法性检验
- 调用任务管理接口 mmap 和 munmap
实验三内容总结
实现进程管理相关系统调用
实现系统调用 spawn
- 扩展 Task 公共接口
- spawn
- MemorySet::from_elf 新建地址空间,用户栈,程序入口点
- 分配 pid 与内核栈
- 建立 TCB
- 加入父进程的子进程链表
- 准备 TrapContext
- 返回新子进程的TCB
- spawn
- 完成系统调用功能
- sys_spawn
实现 stride 调度算法
- 扩展 TCB
- stride
- priority
- 扩展 Task 公共接口
- set_priority
- 修改 TaskManager 调度算法
- fetch 修改为 stride 调度算法
- 完成系统调用功能
- sys_set_priority 调用 set_priority
实验四内容总结
实现几个文件系统相关系统调用
sys_linkat
- 给 File trait 增加 fstat 接口
- 扩展 efs 中 DiskInode 接口
- get_inode_id: get_disk_inode_pos的逆过程
- 在 vfs 中 Inode 增加接口
- create_link
- 新建目录项,(new_name, old_inode_id)
- create_link
- 完成系统调用 sys_linkat
sys_unlinkat
- 给 File trait 增加 fstat 接口
- 扩展 efs 中 DiskInode 接口
- get_inode_id: get_disk_inode_pos的逆过程
- 在 vfs 中 Inode 增加接口
- delete_link
- 遍历目录下所有目录项,找到与对应文件名相同的inode
- 删除(清空)对应目录项
- delete_link
- 完成系统调用 sys_unlinkat
sys_stat
- 给 File trait 增加 fstat 接口
- 扩展 efs 中 DiskInode 接口
- get_inode_id: get_disk_inode_pos的逆过程
- 在 vfs 中 Inode 增加接口
- inode_id: 实现获取自身 inode id
- isdir:判读是 inode 否为目录
- linknum: 获取 root inode 下的某一个 inode id 对应的硬链接数量
- 完成系统调用 sys_stat
实验五内容总结
本节主要几种同步原语与及其内核实现。
- 加入死锁检测机制,实现银行家算法。
- 扩展 PCB 和 TCB
- 可利用资源向量 分配矩阵 需求矩阵
- 数据更新
- 相关系统调用时进行更新数据
- 检测算法
- 获取资源前进行死锁检测
