0%

背景

我本人特别看好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,我感觉我对于操作系统的理解更深了,不像以前只是浮于表面,只记得一些概念,而对于操作系统的实现没有真切地感知。

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


自我介绍

我是一个来自于山东某普通一本的学生。爱好广泛,不过最喜欢的是计算机。在小的时候就非常喜欢看数码方面的内容,比如各种手机电脑,对其中的参数,性能,价格方面都有自己的见解。也经常关注圈子的各种最新的资讯等。但由于生活在乡下等种种原因,与计算机的接触也仅限于此了,什么编程语言,操作系统几乎都没听说过,唯一擅长的就成了处理各种电脑坏了的问题。。。

来打大学之后,对计算机有了更深了一步的了解,这更加提起了我的兴趣。中途接受学校课程学过C,Java,Python,JavaScript等语言。由于平时喜欢玩Minecraft(使用Java编写), 所以当时对Java产生了浓厚的兴趣,觉得自己要好好学习Java,既能满足我对游戏上需求(为游戏编写mod), 也能成为我以后能靠着吃饭的技术。

但。。。随着我对编程语言的认识越来越深,加上种种原因,我渐渐的对Java失去了兴趣。现在看来,Python也是一门我非常喜欢的语言,但是它效率又太低。本着对编程语言的信仰,我希望能学会一门效率非常高的现代语言,现在来看,它就是Rust

与大家相遇的故事

相对来说,当今用的人非常多的Java,JavaScript,或者加上Go,这类语言现在来看就像是为写业务而存活的,他们大多出现在某产品的前端,或后端中,做成网页或者客户端。而我对于计算机的热爱并不是做网页,学到现在,相对于用这些语言写业务来说,我更喜欢原理和技术上的事情。而且我是一个喜欢挑战相对更难的事情的人(并不是说写业务简单,我只是说可能在我的视角里原理和技术上的问题是相对来说更加解决的),所以我故意没有学习搞业务的东西。

我觉得能写一个OS是一件非常厉害的事情,非常酷炫的事情,比网页要厉害得多(个人而言),正好与上学期接触了某一个课程,是与计算机组成原理和操作系统有关的课程,这激发了我的兴趣与斗志,我希望能在最后的课程设计上写出来一个简单的OS,能脱离我们现在的操作系统,直接在裸机上运行的那种。而且在这之前,我正好凭着兴趣自学了Rust,毕竟Rust靠安全和很多新颖的设计著称,而且拥有非常高的性能,我想这不写操作系统正合适!

于是我在网上搜索了Rust和操作系统相关的资料,找到了rCore和blogos, 由于先找到的blogos,于是就照着blogos做了起来。当然,结果没有那么的好,由于有限的时间,加上知识的匮乏,最终只完成到了简单的中断部分,但我觉得这是一个非常好的经历,在我真正去自己写操作系统的时候,我发现并学习到了很多很有意思的设计,而且感觉成就感满满,虽然我做的内容不多,但我觉得我收获很多。我最后也是把它交了上去,虽然是个半成品,不过毕竟时间比较短,这也不算是次的作品了。

之前看过rCore的book,上面有训练营的链接,但我每次看到的时候,都发现已经开展了一段时间了,也不知道怎么参加, 不知道确切的时间。不过在这学期,我有个朋友发了训练营的链接,正好碰上正式开启的直播,朋友知道我学过Rust,就鼓励我参加。虽然深知困难,但我觉得依然要抓住机会,于是就报名了咱们的训练营,不过有点遗憾的是我朋友并没有参加本次训练营。

第一阶段

第一阶段对我来说还是相对轻松的。。。(也许)。其实虽然开启直播后马上就开第一阶段了,但是我却还是在开启了一周后才报的名,原因是在某个小岗位上写业务相关的东西(虽然不喜欢但有时候觉得还是得写写……)。不过由于提前学过Rust,直接做rustlings就可以了,然后就是比较顺利的用半周差不多的,每天的空余时间写了写就过了,然后剩下的时间就去忙校内安排的课程了(课太多了),再加上正好体测加各种实验实在忙不过来,等再次反应过来第二阶段都过了半周了。。。

其实我还是挺愿意写一个rustlings100题的题解的,不知道有没有空。毕竟用github page+hexo搭过简单的博客,还是很愿意写这些东西与大家讨论分享的,能学的很多很多的新知识和新方法。

第二阶段

第二阶段对我来说就比较煎熬了。。。

一窍不通

毕竟前面说,等我反应过来二阶段已经过完半周了,正好有空我抽出来了很多很多时间去研究第二阶段。我错过了第一节课的直播,且正好没有录播……而且我也没找到多少关于二阶段的资源,我甚至不知道哪些是二阶段的资料,Github的那个仓库怎么用我也不知道,命令也跑不通,题也不知道是什么……反正一头雾水。而且感觉自己落下很多了,也不好意思发消息问。没办法就硬回去看群里的消息,几百条划上去从头硬看,好不容易能翻出点东西来,就去尝试了。我甚至不知道到底是clone哪个仓库,到底是自己的还是README.md里面的仓库,我寻思不管啥先拉下来再说,拉下来然后也没跑通。后来翻消息发现有指导书和一个哥们发的公众号,于是花了一天也是把环境搭好了。但之后怎么跑呢,反正也是不知道……好不容易后来看到朱老师说从某一条指令开始运行,再加上第二次直播,我立马就明白了每一条是干啥用的,后来就成功能跑起来了。

初入茅庐

后来就是针对任务书进行学习,ch1,ch2,ch3相对来说我还是容易理解的,因为之前做过一次差不多的缘故,理解起来相对容易一些。不过读代码可就没有那么容易了,太煎熬了,我刚开始学急了,不知道从哪里下手。不过现在来说好像因为过去一段时间了,不是很记得当时是啥状态了。而且当时很讨厌unsafe这种东西,但是这里又有很多这玩意,我每次都会发出疑问,我真的可以直接修改内存吗,真的可以吗,真的吗?总是就是很不习惯,因为之前也没有接触过。不过后来也是慢慢稳住了,真的要冷静下来看代码,掌握一点技巧读起来也不会那么困难。系统的主要结构都会放在结构体中,然后提供一些方法,其实无非就是那老几样,找到头找到尾,捋清了关系,程序的运行逻辑也就慢慢的清楚了。而且幸运的是,我第一次尝试make test,就直接全部通过了,这给了我非常大的信心,然后我尝试去挑战ch4……嗯……差点人直接在ch4没了。

怎么说呢,ch4我感觉确实是看book好一点,ch4之前我也是看指导书,到了ch4之后我看了一遍指导书,感觉一头雾水,就只好去硬扒代码,但这代码吃素的?直接给我上嘴脸,看的我一头雾水。而且这次我没有基础了,上次做也没有做到内存这块,这学期老师讲的也没听懂(校内),这里卡了我好久。而我这段时间是几乎天天看这个东西,课上看这个,晚上也看,看到24点,研究代码,但……效果一般。后来看群里又去扒了一遍book,半天就扒完了,豁然开朗,第二天就做出来了,如释重负的感觉。兴奋的我提交之前,提交之后又没有做任何改动摁跑了几次make test,太开心了。(而且代码里面还有一些我的心理路程和各种翻译,虽然删了一些……)

实现上来说的话,就是差不多大体搞清楚了框架,address.rs就是usize包装成了各种地址页号之类的,两个allocator感觉我说不太清楚,而且感觉可能用不到就没再继续细看,然后memory_set和page_table就关键一些,一个是页表,一个MemorySet存页表和相应的地址空间,MapArea虚拟页号的连续区间。当然,我觉得大大简化难度的是原本就提供了一些方法,我直接拿去用了。读着读着我就觉得这个insert_framed_area就很适合去做这个实验,尝试了一下测试通过了,然后接触映射的我就照着insert_framed_area反过来写了几个函数,包装了一些。然后我写代码的时候还有一点洁癖,比较喜欢整洁一点……然后不喜欢破坏原本的结构,还有封装性,我很少会把原本private的东西改成public,反正……就是洁癖……然后也是非常顺利的通过了。

渐入佳境

ch4困扰了我好几天,半周多,ch5就好很多了,ch5我仍然看的book,而且这次不是我学的相对薄弱的地址了,这次进程相关的内容我正好前一阵子做业务的时候接触过很多了,理解起来相对容易一些,然后也是差不多两天左右时间通过了,找到技巧之后,感觉一切都好了起来,而且每章的那个新增结构,加上每个结构体前面的描述,基本上不用看具体的代码就能理解个大概。不过……第二阶段结束了,勉勉强强及格吧,还是得加把劲。

未完待续

关于收获

感觉这次二阶段收获还是蛮多的。同时压力也比较大(因为一开始觉得二阶段结束没全做完就无法参与三阶段了),晚上睡觉都想这些事,不过后来宽限了条件加上实验顺利通过了一些就感觉好了。

三次实验,每次做完都很高兴很高兴,
第一次是因为调试了很多次终于能正常跑起来了,而且是一次过。这无疑起了个好头,我很想分享此次的喜悦,原本想写一个blog,帮同样迷茫的朋友度过第一关,条件就是如果我能两天拿下ch4我就写,然后……我半周多才拿下ch4,就没有后续了。
第二次是因为历尽千辛万苦终于过了ch4,相对于我一开始面对ch4的内容,毫无头绪,到大体上理解结构,非常感慨,而且离进入三阶段也非常近了。
第三次是因为能成功进入三阶段了,努力没有白费,而且自己也没有那么笨,至少及格了。

而且对于学习上,也懂了很多。一开始我总是逃避去细看代码,尤其是汇编,一但遇到难读的我就不喜欢去细看,导致我花了更多的时间,偷懒最终还是要付出代价的。
而看懂了源码,弄懂了结构,就不会出现没有头绪的感觉,而是感觉自己点子很多,实验根本不在话下的感觉。都是些很模块化的东西,而且还提供了很多现成的方法,可以去使用和效仿。
我还学会了要勇敢面对自己害怕的事情,不能逃避。这件事不光是对写OS这件事而言,其实我以前非常讨厌unsafe,关在unsafe里的东西,我不知为何就不喜欢去想它的作用,有些名字特别长的函数我就会自己觉得自己看不懂,就非常苦恼。以前我总是逃避他,越逃避它,它就越折磨你。我这次是深有体会的,就这个,unsafe,裸指针,内存分配,我硬着头皮把我只要有疑问的全搞明白了,虚拟内存这里的各种名词(我才知道好几个不同的名字指的是同一个东西),每个名词都认明白是什么意思。虽然我弄明白他们需要花时间,但是我真的弄懂之后,后面的工作会好做很多很多。

最后感谢各位老师同学举办了这次活动能让我收获这么多,同时也感谢在群里面交流和帮我解决问题的大佬小佬们。咱们国家的计算机行业有大家在,以后一定会发展的越来越好,越来越强大!!