0%

2023开源操作系统训练营第二阶段总结报告-14432222

实验总结

lab1

本次实验要求我们实现一个sys_task_info来获取进程的信息。最开始的时候我的设计是在TASK_MANAGER中存放两个桶分别存放每项任务的调用次数和起始时间,查询运行时间时有当前时间减去起始时间即可得到系统调用时间。但由于调用次数会变化,而TASK_MANAGER是个静态变量。遂放弃调用次数的桶转而在TCB结构体中存放记录。

lab2

本次实验要求重写sys_get_time 和 sys_task_info,同时实现虚拟内存和物理内存的绑定与解绑。
由于虚拟储存机制的引用我选择了重写一个get_kernel_ptr的泛型函数获取物理内核中的指针。但是由于虚拟缓存机制的影响导致我的lab1中的起始时间桶出现了一些奇奇怪怪的问题,我又含泪放弃lab1的起始时间桶,选择把起始时间封装到TCB中。

lab3

万恶之源!!!和前面两个lab兼容性极差,刚切过去的时候让我怀疑人生,我lab2的代码根本找不到原本放置的位置。我需要重新找位置来存放一些函数。又让我重构了大部分lab2的代码。优先级设置较为简单,但还是出了点问题。最开始我是选择在任务队列里按顺序插入,fetch任务时在遍历队列找到stride最短的任务,但是我本地测试电脑风扇轰鸣了二十分钟也没出结果。经过参考其他大佬们的总结我决定在任务进入的时候按照stride长度进行插入,提前将队列有序。重构了代码之后才通过。

个人感想

本次的rcore总结下来并不难,但在写的时候还是挺崩溃的。一是虽然有一阶段的rust语言的学习,但对于多个文件协同运作对我来说还是比较艰难的。二是由于对于测试方式的不太理解,导致我无法通过gdb进行调试,只能通过不断的打印信息来获取bug原因。三是几个lab的兼容较差,需要我们自行向前,没有类似于一些公开课的项目那种前面模块的测试通过之后后续基本可以不用改动前面模块的模式,心智负担极大,希望后续能有所改进。
最后很感谢训练营能为我们提供了这么一个学习平台,让我们有机会接触到操作系统比较底层的内容。让我们对操作系统有了更深刻的体会。

背景

我本人特别看好Rust语言及RISCV指令集架构的,所以也经常会查找一些相关的信息。在看到这次训练营之前,我在网上关注到了rCore的教程。之前也自己跟着教程学习过一段,那次是只完成了前两章,实现了在终端打印彩色字符串。和本次做实验不同的是那次是从 cargo new r_core_study 开始的。所以那时候看到彩色的字符串输出,还是很有成就感的。

这是我之前学习的仓库连接:https://github.com/aLingYun/r-core-study

第一阶段

第一阶段的难度还好,可能是因为我之前有学习过Rust,所以前大半部分的题很快就做完了。后面涉及到Rust的一些复杂特性,做起来要慢很多。需要不断的查教程,理解教程。也是很高效的学习过程。

第二阶段

由于工作关系,第二阶段的第一周基本上什么也没有做。第二周也基本上只有每天晚上十点之后可以学习。

由于时间不较短,我的策略是先看实验题目。以实验题目为目标去看教程,所以速度还可以,但是对知识的理解程度应该会差一些。

完成lab1之后又陷入没有时间学习的窘境,知道第二周周末,我跟让我的家人带孩子。自己全身心的去做剩下的lab2和lab3两个实验。

那两天每天都是到凌晨1点以后。终于还是勉强完成code了,不过实验报告再也没时间写了。

总结

通过这次训练营的一二阶段,对Rust的理解加深了,尤其是对Rust在底层开发的应用。

之前自学时,第三阶段开始感觉到困难,一直没有过这关。这次训练营帮助我跨了这关,所以真的感谢各位老师的辛苦付出。

2023rcore第二阶段学习总结和个人与计算机系统的漫游

初识

rcore开源操作系统训练营 的相识,算是一个很偶然的机会吧。我与计算机结识很晚,在我上大学后,才从一个对计算机连打字都不会的人到慢慢熟练使用以及熟悉各种技术的人。与操作系统(Linux)结识,是大一下学期的 计算机系统基础课(教程是那本鼎鼎大名的 深入了解计算机系统),那节课开启了我Linux的漫游旅途。

兴趣

在我刚接触计算机的时候,一直认为开发出一个web网站或者APP,就是一件特别特别酷的事情。在整个刚接触计算机的事情,写出一个web网页或者APP便是我一直想要做的事。但后来,大一结束的暑假,学习了一些这方面的技术,扒开了web的真实面目,便慢慢失去了很多兴趣,曾经很酷的事情,突然感觉很无味了。所幸的是,在这个时候,学校的 OS 课开了,杨老师是一名非常知识渊博、热爱体系结构的老师,他 OS 第一门课留给我们的作业便是

下载linux内核源码,并往内核中添加自定义系统调用

这算是我开启了我正式与操作系统内核接触的旅途。永远无法忘却第一次下载linux内核源码,然后编译的时候,满屏报错的电脑界面,特别是每次编译的时候,都会让我等待很久,几乎每次都是编译了三十多分钟,然后给我报错,如此循环往复……最后终于把代码编译完成。第一次进入kernel 目录下,进入代码里面,映入眼帘的是 Linus Torvalds 的大名,那是我第二次那么激动(第一次激动的时候是第一次敲出 “Hello, World”)。最后在各种操作之下,各种文件之间来回修改的条件下,我终于让自己自定义的一个系统调用成功运行了起来,那一瞬间,像是打开了潘多拉的魔盒,从此我开始对体系结构、操作系统方向的东西产生了很大兴趣,便也萌生了写一个OS的想法,从此整个想法,便一直根深蒂固着。

遇见rcore

诚恳的说,我是因为心中那个根深蒂固的想法才会有机会遇见rcore,刚开始的时候,我其实知道的是 ucore, 后来因为个人非常喜欢c++,而某段的时间里,网上的各样信息都在告诉我 Rust 是c++的强大竞争者,Rust 是如何的安全,如何的高效。便萌发了我对这一门新型语言的兴趣。

Rust:

第一次用Rust的时候,它的cargo便惊艳了我很久,用c++的时候,每次安装第三方包,亦或是换个平台,编译东西,都会让我折磨很久,总是在各种编译器之间的实现困惑,msvc有的特性,在gcc有时候却无法运行,有时候在gcc能够运行的东西,在clang也无法运行。同样让人痛苦的时候,c++20/c++23都出了很久了,但是不同编译器的支持却是层出不穷……。换到Rust,突然很多东西便让人清爽了许多。也便逐渐开始了学习Rust的旅途。

risc-v:

对于risc-v的了解,在开始rcore之前,我也只知道它是开源的,文档内容少(远没有X86和Arm那样内容复杂和繁冗)。后来了解了一下龙芯,一生一芯等,便也对risc-v有了极大兴趣,恰逢此时,rcore便出现在了我面前。

rcore:

rcore Book 的娓娓道来,特别是以各种史前生物 来描述,增加了一番故事书的趣味。而Guide则能够快速地让我明白了代码地框架,每个文件,每个模块是什么样的功能。但无奈个人基础不好,所以大部分时间还是在看 Book。本次实验让我们实现操作系统核心的几个重要功能:

  1. 多到程序与分时多道任务
  • Lab1 需要完善系统调用。对于 sys_task_info 系统调用,我们在 TCP 添加相应字段处理即
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    pub fn sys_task_info(ti: *mut TaskInfo) -> isize {
    unsafe{
    *ti = TaskInfo{
    status:get_current_status(),
    syscall_times:get_syscall_times(),
    time : (get_time_us() - get_current_start_time()) / 1000

    };
    }
    0
    }
  1. 虚拟内存管理

    这部分的内容中,为 Rcore 引入了虚拟内存,为地址空间加上了一层抽象,# 地址空间
    刚学计算机的时候,个人非常总喜欢将所有代码放在一个文件里,觉得分开各种代码很是麻烦。后来因为学习深入,开始对分离代码,抽象多了很多体会。特别是在学计算机网络的TCP/IP模型和操作系统的时候,对抽象,加层的思想确实是不断体会,不断明白了那句话“在计算机中,没有什么是不能加一层解决不了的”。现在来好好感受在ch4中的抽象加一层。

    1. 为什么要添加一层抽象层:

      • 从应用开发的角度看,需要应用程序决定自己会被加载到哪个物理地址运行,需要直接访问真实的物理内存。这就要求应用开发者对于硬件的特性和使用方法有更多了解,产生额外的学习成本,也会为应用的开发和调试带来不便
      • 从内核的角度来看,将直接访问物理内存的权力下放到应用会使得它难以对应用程序的访存行为进行有效管理,已有的特权级机制亦无法阻止很多来自应用程序的恶意行为。
    2. 该抽象层要完成的目标:

      • 透明 :应用开发者可以不必了解底层真实物理内存的硬件细节,且在非必要时也不必关心内核的实现策略, 最小化他们的心智负担;

      • 高效 :这层抽象至少在大多数情况下不应带来过大的额外开销;

      • 安全 :这层抽象应该有效检测并阻止应用读写其他应用或内核的代码、数据等一系列恶意行为。

  2. 进程管理

    • 对于进程、程序、可执行文件等的了解更加深入了
      1. 进程是在操作系统管理下的程序的一次执行过程,程序是一个静态的概念。
      2. 可执行文件是一张“蓝图”:一张编译器解析源代码之后总结出的一张记载如何利用各种硬件资源进行一轮生产流程的 蓝图
      3. 加载同一个可执行文件的两个进程也是不同的:它们的启动时间、占据的硬件资源、输入数据均有可能是不同的,这些条件均会导致它们是不一样的执行过程。
      4. 对于创建进程需要fork()和exec()两个系统调用而不只是一个系统调用。两个组合更加灵活,fork是为了 exec 一个新应用提供空间,然后exec可以读取不同的elf文件,执行不同的操作。
  3. 文件系统(未完待续)

  4. 并发(未完待续)

但很可惜,因为个人基础和时间还有其他各种各样的原因,个人并没有完成五个实验,前面三个实验也只是勉强完成(虽然运行过了,但还是有很多东西之间还不明白)。接下来的时间,我将好好把先前没有弄明白的知识点再好好梳理一遍。并将继续做完还没有先前没有做完的工作。向训练营各位优秀的同学学习,以后要多写博客,多写博客(这次学到的一个优秀习惯),及时梳理知识。纸上得来终觉浅,绝知此事要躬行!!!。

rCore OS 学习总结

本次训练营是 Rust 与操作系统的有机结合。我对 Rust 已经足够熟悉,因此侧重操作系统部分的第二阶段更加吸引我。

从一个裸机程序开始,随着需求的复杂化,我们需要逐步添加各种系统调用的实现,最终完成了一个麻雀虽小五脏俱全的操作系统。

1. 应用程序与基本执行环境

操作系统和应用一样都是软件程序。区别在于,操作系统需要几乎直接与硬件交互,不像普通应用程序可以使用标准库提供的各种功能,毕竟标准库构建在系统调用上,而系统调用又是由操作系统提供的。

在操作系统与硬件之间还有一层 SBI (Supervisor Binary Interface)。在后续的编程实验中,我们直接使用编译好的 RustSBI BootLoader 二进制文件。

RustSBI 规定了 OS 在内存中的位置,那么编译 OS 时需要调整链接器使其生成符合要求的内存布局。

2. 批处理程序

操作系统最主要的作用就是运行应用程序,而且往往是多于一个应用。一种最直接的想法是把操作系统和多个应用打包在一起,输入计算机后,依次执行每一个应用。

我们希望当应用出错时,操作系统可以继续执行剩余的其他任务。由此特权级机制被引入。

3. 多道程序与分时多任务

一个一个运行应用不够灵活,我们希望能够对多任务进行调度。那么必然就需要能够支持任务切换,即保存/加载上下文。

调度则主要有两种情况:一是任务主动让出处理器,sys_yield;二是通过时钟中断定时触发任务的切换。

4. 地址空间

操作系统和应用全都直接访问物理地址通常是低效的。引入虚拟地址机制,让每个应用都拥有逻辑上连续的大量内存,可以使应用的编写更加灵活。

RISC-V 64 平台采用了 SV39 多级页表机制,通过 satp CSR 来控制是否启用。

5. 进程及进程管理

此前,所有任务都是操作系统直接管理的。我们很自然的会希望可以由任务来创建子任务,并且可以管理更多物理/虚拟资源。于是引入进程的概念。

理解进程,最核心的就是相关的系统调用:fork, execwaitpid

6. 文件系统与I/O重定向

文件可代表很多种不同类型的I/O 资源。狭义的文件系统可以对持久存储设备 (Persistent Storage) I/O 资源进行管理。

这一节,我们第一次尝试将一组功能从内核中分离出来成为独立的库,交由内核引入使用。

7. 进程间通信

进程间交换数据大大加强了程序的能力。实现进程间通信的主要方式之一是管道。

8. 并发

操作系统通过不断切换任务实现并发,因为进程间资源是相对隔离的,这种并发容易实现但开销较大。

我们想要在进程内,共享资源的情况下实现低开销的并发,这就引入了线程的概念。

一个进程的多个线程共享资源,但需要能互斥地访问资源,避免数据不一致。为此,我们可以用锁、信号量、条件变量等同步原语。

实验感想

rCore 主要目标还是实现一个 Unix-like 的操作系统,编程实验也基本都是实现 Linux 最关键的一些系统调用。

而 Unix/Linux 本就是与 C 语言一体的。这些 C 风格的系统调用接口,用 Rust 实现起来总是很奇怪,有种削足适履的感觉。

我在思考,如果抛弃这些 C 风格的接口,从 Rust 出发重新设计接口,或许可以更好地利用 Rust 的众多优秀特性。

学习总结

一直以来我对Rust表现出浓厚的兴趣,并且曾经写过一些小工具。在平时的学习中,我也尝试在操作系统方面进行一些探索,但一直没有找到Rust与操作系统如何结合的方法。

从编程语言的角度来看,C语言更像是对汇编语言的一层语法糖,它天然适合操作硬件功能的开发,各种硬件概念可以很好地使用C语言进行抽象。然而,如何使用Rust来完成相同的任务一直让我感到困惑。通过这次的培训,我对使用Rust语言编写操作系统有了初步了解,感觉非常不错。以下是我得出的一些简单结论:

  1. 内存安全:在编程活动中,内存安全问题往往难以调试且容易出现。当使用C语言时,不经意间引入内存安全问题的可能性很高,但在Rust中,由于有编译器的帮助,可以在编译阶段轻松地发现问题。

  2. 所有权系统和生命周期:它们帮助开发人员更好地控制变量的生命周期,尤其是全局变量。它迫使开发者思考如何使用和编写更安全的代码。

  3. 强类型系统:至少在安全代码的部分,可以很好地控制C语言中常见的运行时错误问题。

  4. 语言模块化设计:Rust的模块化设计使得能够更好地对系统进行抽象,将操作系统分解成各个组件,最后将它们组合在一起,使整体结构更加清晰。

  5. 文档系统和构建工具:工具如rustup和Cargo为文档查阅和工程构建提供了出色的基础设施服务。

当然,在学习过程中还遇到了一些问题,主要是因为对Rust语言本身的不够熟悉,有些用法与C语言仍存在较大区别,需要更多的练习和实践。Rust语言引入的高级功能实际上在使用上可能会带来一些成本,就像之前所提到的,C语言就像汇编的语法糖,可以直接操作硬件,而使用Rust则需要重新学习一些新的方法和技巧,学习曲线可能会较陡。

Lab总结

上周,我完成了前三个问题的编码工作,主要是体验了如何使用Rust编写操作系统,并发现了与C语言编写操作系统不同的地方。总的来说,这些都是操作系统的基础概念,在Rust语言的基础上进行了抽象和实现,我感受到了与C语言不同的体验。

关于使用Apple silicon mac完成rcore实验的方法

1.选择docker会比vmware更好

如果使用8g内存的m1 mac来做实验的话推荐使用docker来做,相较于vmware来说docker的内存占用会更低一些(8g内存开个虚拟机和vscode挂几个网页基本上就压力就变黄了,要是再挂着qq、微信可能会直接变红),另外我的vmwaretools安装了也无法正常使用,我想在虚拟机内使用宿主机代理折腾了几天也没能实现,但是在docker上就很容易成功,还有就是arm64linux的软件支持比较少(科学上网方面),docker上的环境配置见 https://docs.qq.com/doc/DWW1GZ3FQekx5dm9T 第24个

2.在mac上直接配置环境(不建议使用)

官方的文档里说可以直接在m1 mac上跑rcore,我成功跑通了,首先需要下载和编译riscvtools,
下载得科学上网并且非常耗费流量(我使用镜像失败了),编译这个过程得花费一到两个小时,我试过使用他们编译好的但是无法运行,具体步骤可以参考 https://cloud.tencent.com/developer/article/1939023  然后就是下载依赖 
brew install gawk gnu-sed gmp mpfr libmpc isl zlib expat
以及qemu建议选择qemu7.0.0(老版本qemu需要补丁 https://github.com/BASARANOMO/xv6-labs-2020/issues/1 )
接下来具体步骤可以参考实验书以及
https://risc-v-getting-started-guide.readthedocs.io/en/latest/linux-qemu.html#prerequisites
make这步由于mac上的make只有老版本所以应该得使用gmake
make -j$(nproc)   //这步需要先安装nproc,如果这步使用make报错了的话使用gmake
接下来就可以克隆仓库运行了,跑ch1没有任何问题但是我运行后面几个实验的时候经常会遇到报错,网上也没有解决办法,所以不建议使用这个方法

ch3

第一个实验要实现sys_task_info系统调用,首先在TaskControlBlock中添加syscall_times和start_time,同时在new中更新添加,接下来要实现系统调用次数信息的更新,我在内核的调度函数 run_next_task 中增加了一个简单的判断逻辑,以确定是否是进程的第一次被调度,并在需要时初始化 start_time。

ch4

第二个实验需要重写 sys_get_time 和 sys_task_info, sys_get_time 的主要功能是获取当前时间并填充传递给系统调用的 TimeVal 结构。首先获取时间戳,然后将其转化为秒和微秒,填充到 TimeVal 结构中,最后将数据复制到用户空间的 ts 指针所指向的内存区域。sys_task_info 用于获取当前任务的信息,包括任务状态、系统调用次数和任务运行时间。首先通过相关函数获取这些信息,然后填充到 TaskInfo 结构中,最后将数据复制到用户空间的 ti 指针所指向的内存区域。

ch5

第三个实验要实现 spawn 系统调用,首先,获取当前任务的控制块 task。使用 translated_str 函数将传递的路径 path 转化为字符串。检查是否可以找到与给定路径匹配的 ELF 数据,如果找到了 ELF 数据,就继续进行后续步骤,否则返回 -1。
用 ELF 数据创建新的内存集合,为新进程分配一个进程 ID(PID)和一个内核堆栈。接下来创建一个新的任务控制块,该控制块包含了新进程的信息,如 PID、内核堆栈、内存集合等。将新任务控制块添加到当前任务的子任务列表中,表示当前任务是新任务的父任务。最后,将新任务添加到任务管理器中,并返回新任务的 PID。关于stride调度算法实现我参考了
https://hangx-ma.github.io/2023/07/07/rcore-note-ch5.html

2023rCore训练营二阶段总结

序言

其实去年刚学 Rust 的时候就有看到这个训练营,感觉用 Rust 写操作系统很有意思,可惜当时没有任何操作系统和体系结构基础,有畏难情绪,所以没有参加。(现在看来其实当时就应该参加,rcore并没有想象中的那么难)

今年正巧在刚做完 xv6 的时候再次看到了这个训练营,想着继续巩固 OS 知识的同时还能重温半年多没写的 Rust,就报名参加了本次训练营。

可惜今年参加的时机并不怎么好,我本人已经找到了Android开发的实习,实习每天工作都挺忙的,只能周末抽时间看看录播,做做 lab。也许 Android 开发投入这么多精力学习 OS 算是有点不务正业,但我还是希望能够在本科阶段尽可能多的学习我感兴趣的 CS 知识。

ch3

ch3 还是很简单的,实现了一个系统调用计数和 task_info。所以这一个 lab 我的侧重于读实验源码。不得不说 rcore 跟 xv6 还是有很多不一样的地方的:

  • ch3 还没有涉及到进程的概念,只是简单区分了分时任务。在做 xv6 时其实我一直在疑惑多线程应当怎样实现,因为在 xv6 中只有进程,并且也只实现了进程的调度。rcore 中对分时任务的区分使我豁然开朗,其实一个线程就是一个分时任务,而进程并不是分时任务,而只是一个存放资源的结构体罢了。

  • ch3 的实验代码实现中并没有一个 shell 用户程序,而是直接将程序分段直接加载到物理内存中执行。这个操作我觉得其实就是在裸机程序的基础上做了一些改良,这么看来相比于 xv6,rcore 确实是在教你从零开始实现一个操作系统。

ch4

ch4 主要是学习了虚拟内存机制,其实之前学习 xv6 的时候就学得不太明白,这次算是整明白了。不过这个 mmap 相对于 xv6 那个 mmap 就比较简单了。让我比较惊讶的是 rcore 中能使用各种需要基于堆内存实现的容器(Vec,BTreeMap等)。

ch5

ch5其实难倒不难,就是需要把前面实验中实现的内容再实现一遍(直接 cherry-pick 代码不行,ch5 实验代码变化较大)感觉有点难受,并且遇到一些比较迷惑的问题时没有方法对源码进行调试(不知道是不是我没有找到,make debug似乎只能调试汇编代码)。

spawn 其实就是 fork + exec,并且不需要复制父进程的内存空间,直接 new 一个新进程的内存空间,设置一下父进程然后直接加载 elf ,搞定后再 add_task 即可。

023秋冬季开源操作系统训练营总结-lieck

在同学的推荐下,我报名参加了训练营。这次经历让我有了第一次通过代码来理解操作系统的感觉。在之前学校的课程中,我对操作系统的认识仅限于文字概念,如进程、页表和文件系统等。

在具体的实验过程中,因为是第一次编写这类 Lab,一开始感觉非常难,但是完成后巩固和掌握了很多 OS 的知识,并且在实践中得到了很大的收益。

Lab1

Lab1 需要完善系统调用。

对于 sys_task_info 系统调用,我们在 TCP 添加相应字段处理即可。

可能存在精度问题,这里我使用了 get_time_us 计算时间。

1
2
3
4
let us = get_time_us();
let sec = us / 1_000_000;
let usec = us % 1_000_000;
let t = (sec & 0xffff) * 1000 + usec / 1000;

Lab2

这部分的内容中,为 Rcore 引入了虚拟内存。

因为 Rcore 中分为内核页表和用户态页表,因此对于sys_task_info 系统调用我们不能直接通过修改参数来完成传值。需要将其转换为物理地址,而内核页表中的虚拟地址和内核地址是对应的。

然后是实现 MMap,通过 VMA 实现。

VMA 记录有关连续虚拟内存地址段信息。对每个 section ,都有一个 VMA 对象。

例如对于 memory mapped file,存在一个 VMA 与之对应,其中包含了文件信息等

mmap 收到范围和 port 后,判断是否冲突或参数错误,然后放入 VMA 数据结构中映射物理页。mummap 也是类似的操作。

在此实验中,测试数据稍弱,并没有要求实现 VMA 分裂的操作。

Lab3

Lab3 需要实现优先级调度和 spawn 系统调用。

spawn 系统调用是 fork 和 exec 的结合。可以分为两部分:

  1. 参考 fork 创建新的进程,但新进程执行的首个函数的调用 exec 的操作
  2. fork 后的子进程执行的第一个操作,用于调用 task.exec

优先级调度较为简单,在 PCB 中维护 stridepass

  • 调用 suspend_current_and_run_next 时增加当前进程的 stride
  • 调用 fetch 会选择下一个要运行的进程,找到当前 stride 最小的进程即可。

因为不要求性能,我们可以简单的遍历的选择当前 stride 的值。

第一阶段

一阶段的rustlings是挺好的rust入门题目,也是没有什么阻碍的完成了100道题。

第二阶段

lab1

这一个lab没什么难度,实现taskinfo不需要太多os相关的知识。

lab2

这一个lab开始上强度了,需要弄懂很多os知识。

关于sys_get_timesys_task_info,题目有提到splitted by two pages问题,但是直接整个转换成物理地址写入也能过测试,

我理解的处理splitted by two pages的方法是,对每一个字段,获取他的物理地址,然后写入。

或者像buffers那样,直接逐字节拷贝进Vec<&'static mut [u8]>

不知道上面的方法是否正确。

关于mmapmunmap,本来是想保存在MemorySet.areas下,但是考虑到回收的内存段可能是分配的内存段的子集,要实现MapArea的分裂,还有性能问题,

就直接在MemorySet下挂一个btree管理了(感觉在mmap这种需求下抽象成MapArea反而是一种负担)。

lab3

lab3感觉比lab2简单,spwan直接复用TaskControlBlock::new再进行一些操作,stride也是采用了简单的实现。

本来想实现,即使stride溢出,也能选择正确的程序(无符号相减,结果转有符号与0比),因为时间问题直接用u64解决咕咕咕了。

补充

感觉test还是少了。

rCore学习记录

在Lab开始之前

配置环境

本来想直接用物理机直接进行配置实验环境的

但是ArchLinux默认的qemu版本是8.1,和仓库中rustsbi要求的版本有冲突。

所以最后还是改用了docker来搭建环境……😢

1
2
3
4
make build_docker

# 用同一个docker容器进行全部实验,不想实验一次创建一次
docker run -it -v ${PWD}:/mnt -w /mnt rCore bash

修改ci-user中的makefile

1
2
3
4
5
6
env:
rustup uninstall nightly && rustup install nightly
(rustup target list | grep "riscv64gc-unknown-none-elf (installed)") || rustup target add riscv64gc-unknown-none-elf
cargo install cargo-binutils
rustup component add rust-src
rustup component add llvm-tools-preview

rustup uninstall nightly && rustup install nightly 注释掉,避免每次本地测试的时候都下载一遍

Lab 1

lab1 本身难度其实并不大。感觉主要还是结合之前的章节熟悉一下 rCore 项目代码的整体结构,

但是由于我对系统结构还不是很熟悉,所以还是花了比较多的时间。

  • 实现 sys_task_info 系统调用:

在 TCB 中添加记录系统调用次数的信息,每次系统调用时,根据 syscall id 更新相应数据。

我这里就直接粗暴地用桶方法记录了这些信息。

后面写到 lab3 的时候,发现我之前 sys_task_info 中的获取时间的方式有些问题。正确的应该需要记录进程第一次运行的时间,然后记录时间差。

Lab 2

感觉在前三个 lab 中,lab2 算是相对比较难的一个了。主要的时间都花在了这里。

因为有了虚拟内存的机制,所以要重写 lab1 中系统调用

  • 实现 mmap 和 munmap 系统调用

    • mmap

    需要注意,port参数不能直接转成MapPermission,两者有区别,并非仅仅是类型不同。
    在进行map前,需要检查对应的虚拟内存地址是不是已经被使用过了,这里可以检查 vpn_range的 start 和 end 看看是否被使用
    下面的munmap同理

    • munmap

    基本上就是map的逆操作

可以根据代码画个结构图,方便理解虚拟内存的结构。

Lab 3

  • 实现 sys_spawn 系统调用:

感觉还是比较简单,使用 translated_str 把 path 指针转成 程序名称,然后就可以创建进程,加入进程队列。要记得把新建的进程push到父进程的child中。

  • 实现 stride 调度:

TCB 上再添加上 priority, stride 的信息,然后每次进程切换的时候更新一下。

进程插入进程队列时比较下 stride 的大小就行。

总结

回顾学习的过程,我觉得完成lab重要的是要有一个清晰的思路,明白自己要做什么,明白要怎么做,要对lab有一个整体上的认识。自己阅读代码的能力还是差了一些,以后要加强。

通过学习rCore,我感觉我对于操作系统的理解更深了,不像以前只是浮于表面,只记得一些概念,而对于操作系统的实现没有真切地感知。

最后要感谢老师、助教们辛苦的付出,让我们能够学习到这样好的开源课程,让我们有机会更加深入了解操作系统。