一、前言
参加这次训练营时,我刚刚才进入大三,大三是个非常关键的时期,在之前的时间里已经积累了很多的基础,我在之前所学习到的知识将在这个时间段被检验被拷打,而翻过这座山后,学习的高度将建立在这个这个时期的过渡,所以不仅为了巩固与纠正之前的知识还为了能在这个过渡期将知识储备量进一步提高层次。我选择了这个训练营。我不仅能学到一门强大,前沿,更现代的技术,我还能对硬件与程序之间的桥梁——操作系统有更加深入的了解。能够得到丰富收获的同时也面临一系列的挑战。但是每当遇到困难与压力,我都够挺过来。难题来临,不得不顶上去,敢问路在何方,路就在脚下。
二、第一阶段
1.总述
第一阶段主要涉及的是Rust这门语言的学习,初次接触到Rust这门语言,可能最直观的感受是它的要求很多,比如可变引用于不可变引用、所有权、trait、mod等等。但是这也是Rust与其他语言不同的地方,它会在错误发生之前就尽力扼杀全部错误,这是其他语言所不具备的,这得益于Rust强大的编译器。另外,Rust编程的主要思想仍然是面向对象编程。这使得Rust在大型工程里会使代码更加模块化。
2.特殊机制
Rust的基础特性与其他语言类似这里不做描述,比如基本数据类型、结构体、枚举。这里只列举一些,有待补充。
(1) Option类型
这是一个枚举的变体(是基于枚举类型实现的),它在各种语义里发挥作用,其根本特别之处在于它的None和Some(),它与传统意义的Null不同,例如C语言里的Null表示为空值。它的设计能对Null做出处理而不简单停留在标识空值,他提供了更多选项。
(2) 生命周期
这是Rust强大的编译器带来的特性,它会检查在程序里每处被定义的内存的存在周期,以在编译阶段就避免悬垂指针的出现,这样的话内存的安全性将大大提高。
(3) 所有权机制
这是Rust独一档的特性,它会让Rust每处内存同一时间只会所属于一个变量,而其他变量只能引用这处内存,这样的定义使得Rust制定出了所有权规则,为内存提供了特殊的定义机制,这使得引用内存变得更加安全可靠。
(4) 模式匹配
针对模式匹配,Rust提供了match、if let、while let等来实现模式匹配,这使得我们在Rust可以对变量进行类型判断,增加了每条判断语句灵活性。
三、第二阶段
1.总述
第二阶段主要聚焦于基于risc-v架构的类Unix操作系统,我们采用了Rust语言来编写rcore内核,这是因为Rust在这方面有得天独厚的优势。操作系统在各领域都有所涉及,例如计算机体系架构、计算机组成、计算机网络、数据结构等。所以学习操作系统更够窥探更多计算机的秘密,因此我操作系统有非常浓厚的兴趣。
从裸机运行到单道批处理系统
- 没有任何操作系统,能够直接硬件的程序运行被称为裸机程序。这样做有一点历史原因,虽然这样做对程序员来说十分自由、没有限制。但这样会造成许多问题,例如安全问题、不具通用性难以移植以及对硬件资源的利用率很低等等。
- 于是有了单道批操作系统,在rcore表现为加入了初步的简陋的操作系统。为了安全性,借助硬件上划分的运行特权级将程序的执行环境人为与操作系统分隔开,同时也添加了系统调用,因此可以将多个程序按照一定的批次放在内存里约定的位置,以达到运行多个程序的目的。
- 此时会将所有的程序编译并链接后,生成bin文件,将bin文件先加载到内存里,然后操作系统执行时会将需要执行的程序加载到0x80400000这个约定的地址。
从单道批处理到进程调度
- 单道批处理系统已经明显的区别于裸机运行,但是这样的系统仍然还有进步空间,对硬件资源的利用率还是不高,比如说是cpu的处理能力没有充分利用以及内存资源的分配等等。
- 因此在单道批处理系统的基础上,我们不只是将程序加载进内存后让其滞留在那里而是每个程序都可以运行起来,于是提出了时间片的概念,让每个程序尽量都能被cpu执行,这样cpu的空闲时间会下降,相反性能将大幅提高,同时还让持有内存资源的程序能够及时得到执行,提高了内存利用率。
- 在rcore里,他是risc-v架构,使用内部的定时器来对运行时间进行切分,同时为了能成功切换程序,建立了新的数据结构TCB来维护与更新每个程序的关键信息。
内存地址空间的管理策略
- 以往所提到的地址或是所使用的地址都是物理地址,意思为所有数据都是直接承载在物理地址上的,且所存与所访问的地址是一一对应的。但是这样的话会带来很多问题,例如内存空间的不安全以及局限性等等。
- 因此提出了多种存储管理策略来使程序能够重定位,这极大地提高了内存地址空间的灵活性,同时使访问内存变得更加安全。实现这一算法需要硬件上的支持,因为如果在软件层面上让操作系统来翻译性能会大幅降低,对于硬件资源更适合去干这份工作。因此诞生了mmu。
- 这里的rcore使用的是SV39这种内存管理策略。依赖的是页机制,且使用的是多级页表。这我们对整个物理内存划分为一个很小的页面,这样我们对内存的分配更加灵活、安全、高效。
对外部存储空间的抽象与管理
- 内存虽说是直接与cpu交互,但内存的大小十分有限,我们不能够仅仅依靠内存来存储数据。我们使用计算机通常会产生、使用以及删除十分庞大数据,因此我们不得不依靠外部存储器件来存储数据,但这部分器件往往无法被cpu直接访问,因此我们需要操作系统来操作接口与设备来使数据放在内存里。
- 但是管理一个庞大的存储空间不是一件简单的事,并且内存与外存的存储结构通常是有很大差别的,所以我们需要实现对存储空间进行抽象然后再建立新的数据结构进行管理。这里引入的就是文件系统。
- rcore里的文件系统将每个文件(站在人的视角看叫做文件)抽象成了多个存储节点(站在存储器件的角度上),每个节点的数据结构里含有数据索引,名称,权限,文件关系等重要信息。这样我们访问文件时,操作系统会根据我们的需要去检索节点然后去访问数据。
- 在类Unix操作系统里文件系统采用了统一的框架“”fd(文件描述符)”去描述每个“文件”。由于许多外部设备的访问操作时相似的,例如“打开”、“读”、“写”等,因此对许多类似的设备重定向,建立适配该框架的数据结构,使得可以利用这个框架去访问外部设备。
线程与进程
- 之前我们通常将每个程序作为进程受操作系统管理然后运行,操作系统分配资源的对象为进程,cpu运行的单位也是进程。但是这样的设计仍然会带有一定的局限性,因此我们进程再度细分,将进程分为了线程,进程可以申请线程。
- 进程本身就是一个最大的主线程,它作为线程但又区别于子线程。每个线程都在在进程的环境里运行,所得到的资源也为该进程。因此操作系统分配资源的对象仍为进程,但cpu运行的单位变成了线程。这样提高了进程的运行响应,同时优化了操作系统的调度运行。
- 但这样也带来了一定的问题,会出现一些线程间的冲突,因此我们需要调和线程之间的运行。这里rcore就使用到了信号量和互斥。
四、总结
在这两个阶段里,我学到了很多,一门新的语言Rust、一种类Unix基于risc-v架构的操作系统。从9月29日开始到现在第二阶段11月10日的结束,无论是知识上的或是技术上的,我收获到了许多。我知道这只是基础,我渴望能在第三阶段学习到更成熟的操作系统,以此能加强我对Rust的学习、对risc-v架构的理解、对类Unix系统的理解以及更前沿的编程技术。感谢。