0%

起源

之前就看到了rcore训练营,但是报名后没有没加到群里,群满了无法再加,工作忙和后续的几次时间也没对上,所以就自己按照rcore的教程进行学习一步一步的写出了自己的一个os。

这次刚好新开营时间对的上,看到最后阶段有新东西,很兴奋所以加入了这次训练营

rust

其实个人学rust已经学了很久了,在工作里也在用rust写项目,也实现过网络框架,所以这次rust语言对我来说并不是什么问题,个人的技术站主要是java、go、rust和一些c(主要是一些ebpf和CRIU是c写的,所以需要看懂)

用了c和rust以后很明显的感觉就是内存管理rust编译器可以帮忙管理,而减少悬垂指针等等非法内存异常,rust也会对数组进行检测,当然有时候为了速度优化的话也可以用unsafe去除这些下标检测

整体来说就是rust提供了半自动化的内存管理和编译器的生命周期检测来帮助我们减少内存安全问题,让问题尽量暴露在编译器而不是运行期,外带cargo和rust的高级抽象能力,这些都是rust的生产力,也很适合多人协作大型项目。

二阶段

其实对于代码整体结构来说,已经看过一次了,并且跟着教程写了一个,所以代码结构对我来说并不是太大的问题,主要是时间的问题,对于上班党来说时间是真的很紧,基本下班以后就在做,而且会做到比较晚

  1. lab1

    其实是一个很简单的lab,taskinfo和系统调用的次数统计,对于了解代码结构的我来说不是什么特别难的事,很轻松就完成了
    问答题是很好的问题,也帮助我回忆和加深了risc-v的寄存器的作用,包括trap的流程,这个很重要,直接以代码展现出来,没学rcore的时候平时听到系统调用,其实是很抽象的,并不知道系统调用是怎么从用户态切换到内核态的,而rcore非常精彩的给我解答了这个问题,并且以代码展现,不再抽象。只能说感谢开源!

  2. lab2

    mmap 和 munmap 匿名映射,对我来说其实也不难,不过反而是问答题让我再次加深了SV39的结构,页表,页表项等等这些其实理解很抽象,包括用户态是怎么用到MMU的,MMU和操作系统存储的页表这些是怎么结合的,在这一张再结合linux的一些代码就理解了。其实是riscv 使用 SATP 寄存器来保存 MMU 映射表的根地址

  3. lab3

    spawn和stride 调度算法,这个其实也不算特别复杂,在给予fork和exec代码中只需要理解 spawn和他们的区别,就很容易写出来,而stride调度算法用一个小顶堆实现即可。因为之前看了linux的task_struct的实现,所以比较轻松就能理解。

  4. lab4

    这个要求实现linkatunlinkat 这个加深了我对硬连接的理解,并且文件系统的这章让我对linux的vfs也更加理解,以及操作系统是如何读取磁盘的数据的不过这个lab有一个坑,那就是在spawn以后再次加载文件就无法读出来文件,需要去修改TaskControlBlock 结构体上加#[repr(align(64)] 不知道具体原因还。整体来说明显会感受到磁盘读取和内存有异曲同工之妙。

  5. lab5

    死锁检测,其实一开始我看到那个要求我是懵的,看了一眼算法,大概理解想干啥,但是比较抽象,后来才知道这就是银行家算法,我觉得这里不如直接先说这是银行家算法,再给出后续的数据结构的定义,这个就非常考察细心了,主要就是资源的分配、分出、释放,需要格外注意,否则都无法通过,顺带这里也有一个坑,就是检测用了sleep,sleep用的是get_time,所以要实现这个api不然程序就会卡在那

总结

整体来说,rcore是很好的一个课程,让人对操作系统的实现理解更加深刻,包括和cpu的互动,trap、switch、磁盘、内存、甚至在问答题里还加入了扩展Kernel page-table isolation 这个让我又一次掌握了新姿势,也理解了为什么要创建跳板。

学完收获很大,怀着对底层知识的渴望打开rcore,理解底层知识,对我来说感到非常的开心,理论不再是抽象,而是具体的实现。

感谢开源!

Rust

Rust 是一种现代的、安全的系统编程语言,它提供了许多强大的工具,帮助我们编写高性能、可维护和安全的代码。以下是我从 rCore 学习中获得的 Rust 相关经验:

  • Ownership 和 Borrowing: Rust 强制实施的所有权和借用规则使代码更加健壮,防止了许多常见的内存错误。我学会了如何正确地管理内存,避免了悬垂指针和数据竞争等问题。
  • Pattern Matching: Rust 的模式匹配语法非常强大,使代码更加清晰和易于理解。在 rCore 项目中,我频繁地使用模式匹配来处理不同的系统调用和中断。
  • 并发编程: rCore 需要处理并发和多线程编程,Rust 提供了强大的并发工具,如 std::thread 和 std::sync,帮助我编写线程安全的代码。
  • Unsafe Rust: 尽管 Rust 鼓励安全编程实践,但在系统编程中,有时需要使用 unsafe 关键字来绕过 Rust 的安全检查。我学习了如何谨慎使用 unsafe,以确保代码的正确性。

RISC-V

rCore 项目选择了 RISC-V 架构作为操作系统的目标平台。学习 RISC-V 架构为我提供了对计算机硬件的深刻理解,并且在操作系统开发中也起到了关键作用。以下是我从 rCore 学习中获得的 RISC-V 相关经验:

  • 指令集架构: RISC-V 是一种精简的指令集架构,易于学习和理解。我学习了 RISC-V 汇编语言,包括指令集的基本操作和寄存器管理。
  • 中断处理: 在 rCore 中,我需要编写中断处理程序来处理时钟中断和外部中断。了解 RISC-V 中断处理的原理对于操作系统开发非常重要。
  • 内存管理: RISC-V 提供了虚拟内存管理的支持,我学习了如何配置页表和虚拟内存空间,以实现内存隔离和保护。
  • 系统调用: rCore 需要与硬件交互,执行系统调用。了解 RISC-V 中的系统调用约定和 ABI 对于操作系统的正确实现至关重要。

rCore-Tutorial

rCore-Tutorial 是一个非常有用的资源,提供了有关 rCore 项目的详细教程和示例代码。通过 rCore-Tutorial,我学到了很多关于操作系统开发的实际知识,包括以下内容:

  • 操作系统结构: rCore-Tutorial 教导了我操作系统的基本结构,包括内核、文件系统、进程管理和设备驱动等方面。
  • 实践项目: 通过跟随教程,我实际编写了操作系统的各个组成部分,这帮助我巩固了理论知识。
  • 社区支持: rCore-Tutorial 社区非常友好和支持,我在学习过程中得到了很多帮助和反馈。

总的来说,rCore 项目是一个令人兴奋的学习机会,我从中学到了大量有关 Rust、RISC-V 和操作系统开发的知识。通过 rCore-Tutorial,我不仅提高了编程技能,还加深了对计算机系统的理解。我鼓励其他学习者积极参与这个项目,探索更多有关操作系统和系统编程的奥秘。

本次训练营对我最大的帮助在于,让我深入了解了操作系统底层的工作原理,通过教程的辅助和源码的阅读,我了解到了操作系统在进程线程管理、任务调度、内存分配、文件系统管理等方面的知识,对其实现的机制有了初步的认识。

从操作系统环境的搭建开始,我不断的给系统增加功能,引入进程、页表、地址空间、文件系统、同步机制,让操作系统能够运行一个个进程与线程,处理并发场景的任务,这是一场具有挑战却又精彩的旅程。

从进程或线程的创建和调度角度来说,我了解到了操作系统如何把一个elf格式的文件转换为一个进程,为它分配地址空间、栈空间等,以及如何在进程中执行任务,以及创建新的线程。

elf文件中定义了程序的内存布局,而操作系统将为它创建地址空间,给它分配进程号并将各个逻辑段存放在合适的位置,之后将进程添加到调度队列中,由CPU执行。其中很重要的数据结构为TrapContext,它记录了进程或者线程的上下文信息,在切换任务时发挥着巨大的作用。而其中又涉及到了内存的分配,当进程需要分配内存时,由页表完成从虚拟地址到物理地址的映射,整个映射的过程也是我学习过程的一个重难点,从分配物理页号到创建多级页表进行映射,以及如何使用虚拟地址向物理内存中写入数据,通过思考、绘图去展示整个过程,我慢慢的理解了其中的所以然,感受到了虚拟内存系统的精妙的哲思。

文件系统部分,我学习到了一个简易文件系统的实现,对于文件系统如何管理磁盘文件、以及如何在内核中使用文件有了一个新的认识。首先最令我费解的是描述文件的一系列数据结构,难点在于DiskINode和INode的关系和作用。经过一遍遍理解和梳理,我认识磁盘上的第一个分块将会用于存储整个磁盘的元信息,包括各类块的数量,之后的块分别记录索引位图、索引、数据位图和数据。索引中记录了一个文件的类型、大小以及在磁盘上的块号,通过索引可以找到文件所在的位置,对文件进行读写,这个索引即是DiskINode,而INode用于记录DiskINode的位置,该数据结构将暴露给内核,方便在内核中对文件进行操作。

在并发的学习过程中,我对锁的实现机制有了更多的了解。当某个线程去访问被上锁的临界区时,线程将会从任务调度队列中移入到等待队列,由此陷入等待(休眠),在锁释放的时候再从等待队列中唤醒线程,以达到同步的效果,从而避免数据不一致问题。也试着去实现了一个简单的死锁检测机制,但对该机制的原理理解的还不够,无法证明为何该算法能够实现死锁检测。

教程和练习引导着我的学习与进步,让我对操作系统有了更多的理解,但它们无法为我解释所有的问题,也无法让我看懂所有的代码。当前的学习虽说已告一段落,但整个项目中还有很多细节值得我去细细品味。那些我未阅读或未理解的源码,以及关于os的设计原理–为什么要这样设计,是否有更加合适的设计方式,这对于我来说都是未完成的任务,也是将来需要探索的。

最后,我想感谢训练营,能提供这么好的机会和资料来帮助我学习os,今后我也希望能有更多的机会去深入理解系统级别的设计与开发,加油!

第二阶段总结

学习感受

  • 在参加rCore训练营之前,对于操作系统的相关知识我是比较模糊的,在平时的编程中往往只是简单用到一些比如协程、多线程、原子计数、互斥锁等等编程语言提供的编程模型,对于其内在实现基本一知半解,而在这次阅读了rCore相关教材之后,对这些函数背后所隐藏的机制有了更深的了解。在训练营的过程中,因为之前有用Rust经验,所以第一阶段Rustlings能够比较快速的解决,但是到了第二阶段,因为对于相关知识比较陌生所以做的比较慢一些,unsafe Rust、risc-v汇编这些平时接触非常少,所以在阅读相关代码的时候要查阅较多资料。目前虽然完成了第二阶段晋级的目标,但是感觉前方未知的领域更加宽广。
  • rCore依托于Github的授课方式也让我大开眼界,第一次知道Gihub Classroom这个服务,老师们围绕OS的学习建立的这一整套学习方式(Github Actions、排行榜、直播课网站),不仅仅局限于书本,把计算机专业和教学的完美结合到一起。同时rCore的社区感觉也已经很庞大了,已经有许多人做出了自己的贡献,虽然我目前还没有厉害到可以给rCore提PR,但是希望以后可以。

学习总结

  • 其实我学习Rust的开端比较早,就是看到各大视频网站有很多关于Rust如何安全的宣传视频,当时也没有适用场景和刚需,单纯出于好奇就开始了自学,在学完之后也就仅限于用来做一些比如Advent of Code的编程题,但是其间经常由于Rust严格的检查而被Rust折磨🤣。rCore把Rust用到了系统级的编程领域,难度更是加大了,因为要理解这些代码,首先要理解其背后隐含的OS设计思想,这些就是我对于Rust学习的感受。

Lab总结

Ch3 Lab

第一次做OS相关Lab,在起步时熟悉整个代码框架是比较重要的,这样在做题时才能找到相关的接口,Ch3 lab主要关于task info的获取,需要编写函数来获取每个task的相关信息。

  • 为TaskControlBlock增加field

    • start_time: usize
    • syscall_count: [u32: MAX_SYSCALL_NUM]
  • os/src/task/mod.rs

  1. 为TaskManager实现syscall_count_increment()
    主要逻辑:
    • 使用match来匹配syscall id,从而决定增加conut的对象。
    • 并在trap_handler中调用。
  2. 在run_first_task()中使用get_time_ms()获取当前时间
  • os/src/syscall/mod.rs
  1. 为TaskInfo实现reveal()
    主要逻辑:
    • 计算运行时间
    • 从Task Control Block中获取task info
      在sys_task_info()调用

Ch4 Lab

在初读关于内存空间的内容时经常一头雾水,各种术语经常忘了意思,需要经常翻速查表、文档和笔记。
因为地址空间的跳转,所以之前的trap的实现也要修改。

  • os/mm/memory_set.rs
    • mmap
      • 将vpn与ppn关联,从遍历整个VirtualPageNum,若已存在对应pte,则返回失败,若不存在则分配ppn
    • munmap
      • 将vpn从page table移除
  • os/syscall/process.rs
    • sys_get_time()
      • 用 translated_byte_buffer 将用户地址转换为对应的物理地址,然后读取TimeVal大小的内存
    • sys_task_info()
      • 同理先转化为对应物理地址,然后读取TaskInfo大小的内存,就可以获取task_info

Ch5 Lab

Lab 5实现spawn和stride调度算法,spawn一个子线程需要创建一个新的TaskControlBlock,然后再push到当前任务下

  • os/syscall/process.rs

    • sys_spawn()
      • 首先获取app data(get_app_data_by_name), 再根据data生成新的task
    • sys_get_priority()
  • os/task/manager.rs

    • 实现stride算法
      • 对TaskManager的ready_queue进行迭代,使用min_by_key()找到当前最优先的TaskControlBlock

首先得说,rCore-Tutorial-Guide-2023A教程真的太棒了,我敢说,这可能是我遇到的最清晰明了的操作系统教程!

回忆起第一次听到rcore训练营的经历,那是在2023年的Rust大会上,李明老师的精彩演讲为我打开了新世界的大门。尽管我主要在应用层开发上浸淫,但Rust的魅力让我难以抗拒,所以,当知道2023秋季的rcore开营消息后,我和好友毫不犹豫地跃跃欲试。

rustlings部分我觉得挺熟悉的,之前已经玩转了好几回。但真正进入第二阶段,我有点像被冻住的鹿在大灯下,感到前所未有的压力。工作和时间的双重压迫让我迟迟无法启动,但最后的决心驱使我开始啃这块硬骨头。哗啦哗啦地一页页翻,直到lab1的系统调用实现,我才有那种“原来是这样!”的豁然开朗。这也让我真切体会到,教程的设计对新手真的非常友好。

再往后,lab2就开始考验我的耐心了。我承认,我对sys_get_timesys_task_info的实现确实走了点弯路。原先,我采取了translated_byte_buffer来复制数据,试图仿照sys_write。但和友人交流后,我发现其实可以直接通过用户虚拟地址查找物理地址来进行数据复制,真是巧妙极了!同时,mmap和munmap的相关内容,虽然看似复杂,但仔细读一读,原来也不是那么的玄乎。

到了lab3,真的觉得有点上瘾了。这部分主要是spawn的实现。参考了exec和fork,再结合man posix_spawn的文档,整个流程其实很流畅。至于stride调度算法,哦,那真是让人爱不释手。一个简洁的lambda表达式,搞定!虽然说,我的实现可能有点粗糙,但它确实工作得很好。这也提醒了我,在软件开发中,有时候“简洁”和“完美”之间需要做出平衡。这也是我接下来要努力提升的方向。

虽然我对这三个实验的时间安排有点后悔,但回首这次训练营的经历,真的获益匪浅。对于未来的实验,我更加期待,希望能够带给我更多的启示和惊喜。

总之,这次短暂的OS训练营给我留下了深刻的印象。高质量的教程、热心的群友,真的是让人难以忘怀的体验!

前言

偶在某个群里听说了开源公开课的存在,讲OS就带你实现一个小型OS,讲计网就带你实现计网的5层架构。作为一个对底层感兴趣,又忍受不了学校课程的照本宣科的人,心想这样的课程也太有意思了吧,于是开启了我自学公开课的旅途。

关于开源操作系统训练营

说来很巧,在我将要把6.s081刷完的时候,在相关互助群里听说了这个开源操作系统训练营,于是自然地抱着进一步学习的想法就报了名。现在看来这门课确实符合我的期待:

  • 和学校课程最大的不同也是开源课程共同的优点,不会单讲理论,而是结合实践真正地实现一个能够使用的程序
  • 能够学习更加现代化的rust语言,其中的包管理器和所有权确实很实用
  • 由于xv6和rCore都是基于riscv的,很多功能的实现方式都是差不多的,所以学习难度很平缓,又能巩固之前所学
Read more »

契机

在学长介绍下看到了有操作系统训练营,想系统学一下Rust与操作系统,也想实操拓展一下自己在操作系统上的知识因此加入到了训练营中

第一阶段

之前使用Rust编写过一些程序,这个阶段过的稍微轻松一些,但也遇到了一些之前没有仔细阅读思考的地方:

  1. 智能指针之前只了解了一些常用的如Box,Arc。其他的指针暂时不清楚如内部可变的智能指针如RefCell之前都是靠Mutex或者SpinLock来解决的,在学习中也了解了这些指针以及学习了一下Rust智能指针的内存布局,有一个视频我觉得讲的还挺好的
  2. 生命周期。Rust对生命周期的隐藏太好了,基本上开发中都不会用到,因此我没怎么学习这方面知识,开发中比较排斥在结构体中使用引用,因为要涉及到生命周期标注。这一次也是好好学习了一下

第二阶段

之前看过rCore的文档和代码,这一次回顾的时候也发现了之前漏看或者以为自己看懂了的地方,最明显的是中断那一部分,之前看的都挺迷糊,也有现成代码就胡乱略过去了,实际上没怎么看懂具体流程以及中断的代码实现,现在再次看能更全面的了解RISCV上的中断机制。还有rCore上的文件系统也是接触较少的一部分

ch3

ch3只用在TCB里面加上一些数据即可,难度不高

ch4

涉及到内存管理了,这一方面难点主要集中在map和unmap的边界处理上,但多画图注意一下也是可以过

ch5

主要实现spawn系统调用,创建一个空白子进程然后加载对应程序即可,

ch6

需要对文件系统进行修改,原本想在unlink的时候缩减根目录所占大小,但试了一下感觉较难,采取了一个取巧的方式,把目标DirEntry直接清空了。实现获取文件信息时刚开始也把一些信息给硬编码了,但后来熟悉了一下文件系统后,又把这些给接上文件系统信息了,也算是一个自己不太好的地方,容易取巧。

ch8

涉及到死锁检查,难点主要是在信号量死锁上,需要预防资源循环依赖

总结

第二阶段将系统中的部分小模块拆分开,难度不会太高的前提下也能让人感觉到系统功能在一步步变多,正反馈还是挺多的,一个个test也能让人产生动力去添加功能,并找到代码的问题,这次编写的过程学习补充到了一些知识及代码实现,就是有一些功能不需要自己添加,比如直接跳过了中断,虽然中断代码一般固定后就不会修改了,但加上一些可能会让人印象更深刻一些。

Lab1

编程题

获取任务信息

思路:题目要求查询的信息都是全局持久化的,因此需要在TaskControlBlock中添加这些信息。

task_status已经存在,不需要添加

task_syscall_times需要在sys_call函数中拦截,根据系统调用的id进行桶计数即可

task_runtime比较麻烦,有两种思路:

  1. 保存任务第一次调度的时间,在sys_task_info调用时将当前时间和第一次的时间相减(我的做法)
  2. 分别记录任务在用户态和系统态下的时间,在sys_task_info调用时将两者相加。(此思路源于参考)
    1. 内核态时间.在 run_first_task 以及 mark_current_exited, mark_current_suspend 中更新信息, 另外需要在 task 退出时打印耗时。
    2. 用户态时间.用户态和内核态的分界处就是 trap, 因而在 trap_handler 的起始位置和末尾位置可分别作为 user time 的开始以及 user time 的结束。

Lab2

编程题

重写 sys_get_time 和 sys_task_info

由于内核和应用地址空间的隔离, 系统调用 不再能够直接访问位于应用空间中的数据,而需要手动查页表才能知道那些 数据被放置在哪些物理页帧上并进行访问。

可以参考文档中sys_write的重新实现。基本原理就是使用函数translated_byte_buffer可以将应用地址空间中一个缓冲区转化为在内核空间中能够直接访问的形式,然后再进行操作即可。

指针的copy操作可以使用core::ptr::copy_nonoverlapping

在taskinfo踩了坑,同样的代码可以过ch3的测试却没法过ch4的测试,因为使用了get_time_ms函数,有一些精度问题,后来改成get_time_us() / 1000 即可

mmap 和 munmap 匿名映射

mmap函数的作用和签名可参考
参考一
参考二

比较麻烦的是要弄清申请页和释放页边界,统一使用左闭右开的范围。检查范围和权限是否合法 使用 基本的数值运算或位运算即可;申请空间和释放空间要求 理解memset和pagetable的api作用,合理地使用即可

在样例ch4_umap卡了很久,因为当时mmap和munmap申请内存的范围边界没有理清,注意测试样例有一个地方申请了 PAGE_SIZE + 1的空间,这个申请应该向上取整,申请2页内存

Lab3

编程题

进程创建

实现spawn系统调用,spawn函数的作用简单来说就是创建一个子进程,并在子进程中加载执行传入的可执行文件。通过它的作用我们可以意识到它其实是fork和exec两个函数的结合,因此spawn的具体实现就可以在fork和exec函数里左借鉴右借鉴,这就是——拿来主义。

  1. spawn会根据传入的文件名,解析elf文件,获取elf文件的地址空间、用户栈等。和exec类似
  2. spawn会创建新的子进程,需要申请新的pid和内核栈。和fork类似
  3. 根据解析exec得到的信息以及父进程的信息初始化 子进程的TCB和Trap Context。和exec、fork类似
  4. 将新的进程加入队列

stride 调度算法

这题相对比较简单,根据题目要求为TCB加入pass、priority、stride字段即可,BIG_PASS随便设置一个比较大的整数就行。
需要修改的函数就是获取下一个执行任务的函数——fetch函数,之前采用的调度策略就是先进先出。stride算法的实现根据题目要求写一个for循环寻找stride值最小的task即可。

引言

我是在某 912 考研群里看到群友发了这次操作系统训练营的宣传图才进来的,事实证明,这是一次非常正确的决定,因为确实挺有趣的,一方面有了个在专业指导下实际上手折腾操作系统的机会,另一方面也确实见识到了挺多东西。

顺便,我还拉了几个朋友进来一起,可惜他们都比较忙,只是堪堪过了一阶段,在二阶段时正好与他们的考试周和大作业冲突了,于是在二阶段折戟沉沙,只能等下一次训练营时继承这次的存档继续打了(

感悟(正文)

操作系统训练营,那自然是以深入了解操作系统为目的的,若是要展示自己在这方面的感悟,那么我想,我可以通过使用伪代码来描述一个简单操作系统所做的事情,以此来反映我在这次训练营中所学到的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
struct SimpleSYSTEM{
pub hardware_resources:RefMut<HardWareResource>, //作为一种特殊的软件,操作系统同时对用户的软件与硬件进行操作,在大多数场景下,用户并不会直接的操作硬件,而是要通过操作系统来交互。
pub state:OSSTATE, //万物都是状态机啊状态机
}

enum OSSTATE{
//首先,很显然的,泛泛而谈的来说,操作系统要么在处理自己的数据,要么在处理程序的数据
OS_OPERATION(OS_OP_STATE),
RUNNING_TASK,
}

enum OS_OP_STATE{
//那么操作系统在不允许用户程序的时候会做什么事呢?
SELECT_AND_SWITCH_STATE, //这个状态表示控制权刚刚回到操作系统手上,这是需要操作系统来决定下一步做什么。
MANAGE_MEMORY,//由于虚拟内存的存在,一切的数据,不论在什么地方,最终都会映射到内存中,可以说操作系统做的只有两件事:操作内存与操作硬件,既然如此,操作系统也得负责管理内存
HANDLE_INT, //中断,是硬件给予操作系统反馈的桥梁,有了它我们才能更方便的建立硬件与软件的双向链接
}

impl UseAble for SimpleSYSTEM{
pub fn iter(&self){
loop{
match self.state {
OS_OP_STATE(operation) => match operation{
SELECT_AND_SWITCH_STATE => {
//首先,检查自己是如何进入基础状态的,也就是说如果发生了中断,我们需要第一时间处理,因此我们如此编排顺序:
if self.hardware_resources.interrupted(){
self.state = OS_OP_STATE(HANDLE_INT);
continue; // 进入下次循环,下次循环就会转到处理中断的操作。
}
if time_to_manage_mem(self.hardware_resources){ //除了被动的申请/释放内存,操作系统也需要时不时主动的去检查各个进程的内存空间以及整体的内存空间,以及时发现问题。
self.state = OS_OP_STATE(MANAGE_MEMORY);
continue;
}
self.state = RUNNING_TASK; //没有任何异常,那么是时候运行一会程序了!事实上这才是大多数时候的情况

}
MANAGE_MEMORY => {
manage(self.hardware_resources.managed_memory());//管理内存,检查错误并修正错误
self.state = OS_OP_STATE(SELECT_AND_SWITCH_STATE); //回到基础状态
}
HANDLE_INT => {
handle(self.hardware_resources.int_info()); // 根据硬件请求信号做出回应
self.state = OS_OP_STATE(SELECT_AND_SWITCH_STATE); //回到基础状态
}
},
RUNNING_TASK => {
(addition_attr,operations_collection:address) = generate_operation(self.hardware_resources.cache); //为什么是address?因为程序也存放在内存中。
actual_operation_stream:RefCell<dyn [operation]> = setTimer(self.hardware_resources.timers,operations_collection) // 这里不一定得是字面意思上的Timer,也可以是某种约束,比如我们所约定俗成的,放置在程序栈底部的返回地址,放置一连串操作在等,都可以算是对于程序运行时间的干涉。
self.state = OS_OP_STATE(SELECT_AND_SWITCH_STATE); //运行一段时间后操作系统提前在operations_collection/硬件计时器中埋下的操作会触发,将运行资源交还给系统,也就是说会重新进入这个大循环,因此我们可以在这里提前做好状态的设置。
//于是经过了处理后,我们得到了真正的一串精心调整过的操作序列;
self.hardware_resources.load_operations(actual_operation_stream); // 在这里,操作系统的工作暂时会停止,直到这段操作序列运行结束,或者运行时出现了情况,触发中断。
}
}
}
}
}

如上,这就是在我脑海中操作系统所做的事情,当然,这只是个泛泛而谈的框架,其中每个分支中所进行的操作都包含着值得深入学习的问题,比如 MANAGE_MEMORY 的情况下,我们可能需要进行进程资源的检查,以发现死锁/僵尸进程的情况等,学习这些内容就是参加训练营的目的;

日程

  • 第一阶段:rust 入门 & rustling: https://github.com/LearningOS/rust-rustlings-2023-autumn-dbydd

    由于我在此之前就自学过 rust,因此第一阶段对于我来说比较轻松,每天抽点时间刷一下 rustling 就能愉快的完成任务了,事实上我在第三天时就已经把 rustlings 刷完了。因此这个阶段实在是没什么好说的…

  • 第二阶段:rcore 实操训练:https://github.com/LearningOS/2023a-rcore-dbydd

    在我的印象中,rcore 是 ucore 的后辈,两个操作系统都是由清华大学的师生开发出来作为操作系统课的教学工具的;这不正巧,由于下定决心明年要冲 912,我在暑假的时候就先刷了一遍清华的网课,其中就包括了陈老师的操作系统课程,刷完后也瞄了两眼 ucore 的实验部分,由于脑中的知识很新,因此对于实验到还算是得心应手,反而大部分时间都是在与 rust 的语法做斗争.但是不巧的是也遇到了考试周,此外还要准备校内计算机社团的讲课工作与数学建模的备赛,就算是我也感到了分身乏术,进度也就这样被拖慢了下来,直到我写下这段文字时(11.1),也只是将实验推进到了 ch6,好在后续又有通知,这次的二阶段只要完成前三个实验就能进入三阶段,剩下的两个实验在三阶段补齐就行.这样一来或许我也有机会一命通关?整挺好.

    • 二阶段开始-2023.10.25 做完了 ch3

    由于是第一个实验,难度并不是很高,只需要给进程附加上一些信息记录,修改很少的代码就能完成。全过程思路如下:首先是要对进程做操作,因此应当去进程相关的代码下做修改。TaskInner 记录的是进程的运行时的程序部分,在这里添加自己的信息不是很合适,于是只剩下了一个可选项:在 TaskControlBlock 中塞东西。

    • 2023.10.25-2023.10.31,做完了 ch4

    ch4 主要是关于内存的管理,这部分可以说是一个操作系统的 1/2 个核心部分,在我看来最精简的操作系统,其实就是一个工作在内存上的输入输出机:输入当前的内存,输出特定长度的操作序列至某一片连续内存,并将当前 cpu 的 pc 指向哪个内存的首地址。当那一片连续内存执行完后就回到内核态(也就是继续由操作系统进行一次输出),如此反复…

    总之,我们只要实现一个虚拟内存的申请和释放就可以了,这部分主要的难点就是可能会一次申请/释放一大片内存,从而需要在多个页表项上进行操作,不过实际实现起来只需要稍加思索就能写出很自然的代码。

    • 2023.10.31-2023.11.1 做完了 ch5

    ch5 主要是对进程的管理,自很久以前时分复用的概念被提出开始,任何操作系统都会支持多进程的操作,这甚至不是一件需要特意提及的事情。问题在于:我们要如何设计一个合理的进程系统,并不出差错且用得顺手?

    实验部分有两个,一个是实现 spawn 方法,实验指导文档上给出了标准的 posix_spawn 方法的参考链接。一言以概括之:spawn 方法就是创建子进程用的,那么既然知道了要做什么,怎么做也就非常的显而易见了。

    另一个任务是实现一个简单的进程调度算法:stride 算法,这个算法的想法很自然:给进程加上动态变化的权值,如果一个进程刚刚被执行过,那么它应当不会在短期内被连续执行,除非他的优先值很高,能让他进行连续的执行。

    *未完待续