diff --git "a/source/_posts/2024-\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200-\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\345\206\260\350\275\251.md" "b/source/_posts/2024-\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200-\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\345\206\260\350\275\251.md" new file mode 100644 index 0000000000..fa04d7feb9 --- /dev/null +++ "b/source/_posts/2024-\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200-\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\345\206\260\350\275\251.md" @@ -0,0 +1,45 @@ +--- +title: 2024 秋冬季开源操作系统训练营第一 & 二阶段总结 - 冰轩 +date: 2024-11-10 16:19:54 +tags: + - author: BingXuanOwO + - repo: https://github.com/LearningOS/2024a-rcore-BingXuanOwO +--- + +## 前言 + +这次加入训练营的前因来自于两位朋友曾断断续续提到了 rCore,以及这个与 rCore 有关的 OS 训练营。 + +关于 Rust,我曾抱着将信将疑的态度。在社交媒体上关于此的风评给我的感受来讲,就好像万物皆可被该语言重写,这个语言能解决一切编程问题。而包括这种偏见在内等原因,我却没有对 Rust 有太多了解。 + +而至于 OS,我曾因观看过蒋炎岩的 OS 课程而对 OS 略知一二,但却一拖再拖,因各种原因没有完整的学完全部课程以及做完 labs。 + +我想,是时候改变这一切了。我最终报名加入了进来。 + +<--! more --> + +## PT1. 初次上手 Rust 与 Rustlings + +在报名之后,接下来的第一个任务便是完成 Rustlings 全部 110 道题目,以此形成对 Rust 初步的认识。Rust 的基础语法适应起来很快,但是最重要的还是适应 Rust 最核心的 borrow,所有权等一系列与 RAII 相关的设计。虽然碍于 Rustlings 篇幅受限,我没能完整掌握这方面,不过这部分知识还是在 rCore 的实验中补回来了的。 + +最终,我成功完成了全部的 rustlings 题目,并晋级至重要的下一阶段——rCore。 + +## PT2. rCore 之旅 + +接下来要完成的,便是相对而言更为重要的 rCore。一路上磕磕绊绊不少,但还是完成了挑战。 + +首先是 Lab1,Lab1 的任务主要是理清 rCore 原有的代码结构。我在刚看到 rCore 的框架代码的时候,老实讲的确会感觉无从下手。不过,在我花上一段时间了解原本的代码结构,理解调度部分的实现后,就没感觉问题很大了。这中间有个小插曲--由于我一开始看错了题目,我一开始以为 syscall_times 部分记录的是每次 syscall 调用时对应进程运行的时间,但是后来发现其实是每种 syscall 调用的次数。 + +接下来是 Lab2。因为 Lab2 的框架代码已经提前实现了多种用于内存管理的 struct,我最开始的想法是直接在内存的 PTE 上做文章。但我后续发现,在不对框架原有分配部分不做出大变动的情况下,这种想法似乎并不可行。我好像又有点手足无措,不过思考过后,我最终想到遍历 `MemorySet` 中全部 `MapArea` 区间计算并调整区间的方案,总算实现了 map 和 unmap 的 syscall。 + +Lab2 中重写 `sys_get_time` 和 `sys_task_info` 的部分就相对容易些,我实现的方案是按照对应函数所需返回值的大小,对于每个可能包含该变量的虚拟页,反查用户空间对应的物理页,并从内核 0x10000 虚拟页处开始分配虚拟页表然后 map 到对应的物理页。Lab2 原本这部分的实现实际上每次 map 后都不会 unmap 掉, 而是在接下来继续 map 的时候从上一次分配的末尾处 map。但在后面的 Lab 中,由于分配量超出预期,从而会导致其尝试 map 已经在别处分配了的 PTE。到问题发现之后,我添加了 unmap 部分,并使分配都从 0x10000 开始,以此解决了问题。 + +随后便是 Lab3,相对而言其实还算简单,stride 部分只需要参照题目描述实现。而至于 spawn 部分,可以参照已实现的 fork 与 exec 部分,这样照葫芦画瓢也同样能较为轻松的实现出来。 + +而接下来的 Lab4,由于项目结构又一次进行了变化,引入了一个新的 easy-fs 库,从而又感到了和刚进行 Lab1 时,没法理解代码结构时的头疼。而在进行 Lab4 的过程中,因为不知为何导致的死锁,我又一次头疼不已,甚至一度想放弃。好在最后因为 Rust RAII 的特性,通过封装一层函数的方式,将某个我并不知情的引用自动 drop 掉,才最终解决了问题。 + +最后,到了 Lab5,rCore 阶段要结束了。虽然这次我并没有选择将检测死锁的部分独立拆分为单独的 crate,但这部分仍然先在裸机上开发,完成后再移植进内核。由于是直接在裸机上开发,整体调试起来也会方便很多。不过话虽如此,在刚开始的时候,由于我开始进行 Lab5 的时间早于这次线程相关课程的上课时间,且题目没有详尽地描写检测死锁的算法,以及对算法的不熟悉,导致我在这里卡住了很久没有进展。不过最后,我确定了题目给出的算法为 Dijkstra 的银行家算法,在不断的了解这个算法后,最终完成了 Lab5。 + +_至此,一锤定音。_ + +_尘埃,已然落定。_ diff --git "a/source/_posts/2024-\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-wang-md.md" "b/source/_posts/2024-\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-wang-md.md" new file mode 100644 index 0000000000..18e781acb1 --- /dev/null +++ "b/source/_posts/2024-\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-wang-md.md" @@ -0,0 +1,21 @@ +--- +title: 2024-秋冬季开源操作系统训练营第一二阶段总结-wang.md +date: 2024-11-10 11:14:20 +tags: +--- + + +## 第一阶段总结 + +因为之前已经在学校里学习过Rust的内容,第一阶段更像是起到一个查缺补漏和复习的作用。通过完成Rustlings的100道练习题,对Rust生命周期、所有权机制、智能指针、移动语义等内容有了更深入的理解。 + + +## 第二阶段总结 +- lab3: 实现syscall: sys_get_time(), sys_task_info() +- lab4: 引入虚拟内存/分页机制,重新实现sys_get_time()和sys_task_info(), 实现mmap和munmap +- lab5: 将task抽象为process进程,实现sys_spawn()和stride调度算法 +- lab6: 新增文件系统,可以将原来在内存中的数据持久化到硬盘上。 实现sys_linkat(), sys_unlinkat(), sys_stat() +- lab8: 引入thread。实现死锁检测,实现sys_enable_deadlock_detect() +通过完成5个操作系统的实验,对操作系统的基本原理有了更深入的理解。在实验过程中,我遇到了很多问题,比如在实验四中,我在实现文件系统时,由于对文件系统Inode/OSInode/DiskInode/File之间的关系和概念理解不够透彻,一开始写lab的时候确实是完全不知道该怎么做,也导致了一些错误。于是我认真阅读了实验手册,通过画图和总结,最终完成了lab。 +lab1-3个人认为是相对简单的,几乎每一个的pattern都是先去某个struct添加一个新的field,然后在impl中对这个新的field进行一些需要的操作。这其中还可能涉及用户态与内核态之间的数据传递,比如在lab1中的sys_task_info()函数就涉及到内核态数据到用户态数据之间的传递。 +总之,通过这次训练营,我对操作系统的原理有了更深入的理解,也对Rust的应用有了更多的实践经验。 diff --git "a/source/_posts/2024-\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-DFP-HN.md" "b/source/_posts/2024-\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-DFP-HN.md" new file mode 100644 index 0000000000..17d2ec4750 --- /dev/null +++ "b/source/_posts/2024-\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-DFP-HN.md" @@ -0,0 +1,9 @@ +--- +title: 2024 秋冬季开源操作系统训练营第一阶段总结-DFP-HN +date: 2024-11-10 14:01:09 +tags: + - author: DFP-HN + - repo: https://github.com/LearningOS/rust-rustlings-2024-autumn-DFP-HN +--- +# 第一阶段总结 +第一阶段训练营通过写一些rust的基本语句,快速入门rust,掌握了rust的基本语法。第一阶段的题目设置非常合理,对于rust零基础的也能快速入门,只需要一步一步按照文档上的学习就能轻松完成题目。 diff --git "a/source/_posts/2024-\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-DFP-HN.md" "b/source/_posts/2024-\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-DFP-HN.md" new file mode 100644 index 0000000000..3d50d06261 --- /dev/null +++ "b/source/_posts/2024-\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-DFP-HN.md" @@ -0,0 +1,9 @@ +--- +title: 2024 秋冬季开源操作系统训练营第二阶段总结-DFP-HN +date: 2024-11-10 13:57:28 +tags: + - author: DFP-HN + - repo: https://github.com/LearningOS/2024a-rcore-DFP-HN +--- +# 第二阶段总结 +第二阶段的题目略有难度,我在这个阶段有了很大的收获。第三章的练习是获取当前任务的信息,通过这个练习,大致清楚了进程在操作系统中的运行流程以及组成结构,系统调用的实现和中断的实现。第四章的练习考察对页表的理解,在完成的过程中加深了我对虚拟地址和物理地址的理解。第五章的练习是实现一个子进程的创建,fork+exec也能生成一个子进程,但这个子进程的资源和空间与父进程完全一致,然而练习的要求需要子进程有独立的数据,如果采用fork+exec将浪费很多时间去重新加载数据。第六章的练习是关于文件系统,创建硬链接和取消硬链接,这一章节对我来说有点挑战,因为之前学操作系统对于文件系统这一块就没太搞懂,认真看了几个晚上的代码后才搞懂文件系统的结构,收获非常大。第八章练习比前面的轻松一些,只要搞清楚了代码中各个结构之间的关系,就能轻松解决。 \ No newline at end of file diff --git "a/source/_posts/2024-\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-ghost.md" "b/source/_posts/2024-\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-ghost.md" new file mode 100644 index 0000000000..ad97555b28 --- /dev/null +++ "b/source/_posts/2024-\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-ghost.md" @@ -0,0 +1,40 @@ +--- +title: 2024_第二阶段总结_ghost +date: 2024-11-10 20:14:22 +tags: + - author: + - repo: +--- + +# 一、前言 + +毕业之后一直工作都是在做c嵌入式驱动开发,是和同事闲聊的时候了解这个项目的就开始学下去了 从2024春季就已经开始学了但是因为一些事情耽误了就没有完成 +对我个人来说主要是好奇想看看经常看到的所谓写操作系统是做什么,以及我在工作中也会看到rust的一些内容对这个语言也很好奇 +# 二、基础阶段 + +![错误记录](1-1.png) + +在基础学习的时候我认为这样的错误记录是很有必要的 +因为我基础并不好经常对于书上看到的方法看到就忘了 实际写起来我最大的感受就是 +1、如果有多个数据结构嵌套我很容易就搞不清哪里要复制哪里不复制 +2、闭包按照我理解就是一个函数指针 在传递闭包的时候一开始没理解总是搞不清楚到底是怎么回事 + +**以及在完成一阶段之后我才安装了rust-analyer也算是踩坑了把 有这个插件才发现可以很快就发现错误不用在一次次编译** + +# 三、OS专业阶段 + +专业阶段在整个学习的过程中对我而言最困难的是第四章和第八章 +在第四章 我觉得内存的边界条件卡住了很久 主要还是理解不够 明白了 地址空间 应用空间 需要转换 +也看明白了虚拟地址的寻址方法 但是真的感觉新的文档没有图我一开始没理解的就是 他三级页表怎么寻址的过程 +后面才真的理解上一级页表的值代表的是下一级要去找的page 再再下一级page上找到对应页表项 才实现了多级页表的寻址 +![三级页表](3-1.png) + +整体来说我觉得这个专业阶段 首先文档需要图 +并且我发现只看一遍效果很差 需要边看边记 并且对于代码讲解最好是能够记录并且依次添加注释 明白每一步再做什么不是只看文档中的讲解就过一遍就结束 +![结构体关系](3-2.jpg) +以及我觉得再实验过程中最重要的是搞清楚各个结构体所代表的实际意义 +并且可以集中在图上展示 不然我在做ch6一开始总是错就是 没梳理清楚 disk_inode inode OS_inode还有目录项分别都是些什么 导致ch6经常找错 +于是我就在草稿纸上根据理解写了我对于这些数据结构的理解和关联很快就解决了 + +实验的时候还有一点 有时候我总喜欢通过结构体之间的关系来找对应的数据 但是在rust里这样做我觉的很复杂 +最好的方式就是在容易得到的结构体中保存需要的数据 \ No newline at end of file diff --git "a/source/_posts/2024-\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-ghost/1-1.png" "b/source/_posts/2024-\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-ghost/1-1.png" new file mode 100644 index 0000000000..759cde617b Binary files /dev/null and "b/source/_posts/2024-\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-ghost/1-1.png" differ diff --git "a/source/_posts/2024-\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-ghost/3-1.png" "b/source/_posts/2024-\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-ghost/3-1.png" new file mode 100644 index 0000000000..4cd3aedb7f Binary files /dev/null and "b/source/_posts/2024-\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-ghost/3-1.png" differ diff --git "a/source/_posts/2024-\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-ghost/3-2.jpg" "b/source/_posts/2024-\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-ghost/3-2.jpg" new file mode 100644 index 0000000000..11f180afc8 Binary files /dev/null and "b/source/_posts/2024-\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-ghost/3-2.jpg" differ diff --git a/source/_posts/2024A-OS-I-Summary.md b/source/_posts/2024A-OS-I-Summary.md new file mode 100644 index 0000000000..ea90ae057f --- /dev/null +++ b/source/_posts/2024A-OS-I-Summary.md @@ -0,0 +1,100 @@ +--- +title: 2024A-OS-I-Summary +date: 2024-11-10 12:34:18 +tags: + - author:Whth + - repo:https://github.com/LearningOS/rust-rustlings-2024-autumn-Whth + - RustBasics +--- + + + + + +# 引言 + +---- + +9月的时候在`Rust语言中文社区`的微信公众号了解到了这个训练营, +当时想想正好可以获取一些操作系统的设计与实践的经验和感受感受`Rust`编程的实际运用,所以就报名了. + + +# 第一阶段概述 + + +--- +## 个人情况 + +- 非科班 +- 大四备战考研ing +- 有过一些`C++`和`Python`的编程基础 +- 系统学习过linux的架构和原理 +- 了解过`Rust`的编程范式和语言特性(不过没怎么实际用过,只是简单的配过环境搓过些小玩意儿) + + + + +## Rustling + +说是100道题,不过实际上是80道题. +其中70道语言特性和语法相关的基础题目,而剩下的10道题都是算法相关的. + +因为`Rustling`没啥平台依赖所有就直接在win本把仓库克隆下来然后`cargo install&&rustling watch`开干 + +语法基础题花了一个下午给整完,中途卡了两天然后又花了两天把剩下的这10道题也做了.不过有一说一最后几个算法题确实有那么点上强度的感觉了Xb + +![img.png](2024A-OS-I-Summary/img.png) + +![img_1.png](2024A-OS-I-Summary/img_1.png) + + + +## 总结 + + + +唉唉,给我的感觉两类题目的难度曲线会稍微有点陡峭了,简单和困难的过渡不太平缓,中途如果没有`Jetbrain RustRover`的built-in linter(好用,推荐😋)可能还得多花些时间 +![img_2.png](2024A-OS-I-Summary/img_2.png) + +个人感觉最难的几个部分有生命周期,借用和作用域 + +1. **生命周期(Lifetimes)**:确保引用在其所指向的数据有效期内始终有效,防止悬空指针。虽说如此,但是实际上没有了`'a`和IDE的检查马上就歇菜了Xb. +2. **借用(Borrowing)**:允许临时使用数据而不转移所有权,提高代码的灵活性和安全性。这个借用和cpp的借用机制类似,不过在rust中 borrowing 的时候会自动释放,不用像cpp那样手动释放,挺好. +3. **作用域(Scopes)**:定义变量和引用的可见性和生命周期,确保资源在不再需要时被及时释放。最后我的理解感觉实际上也就是`{}`的范围. + +不过难归难,实际上rust里面挺多编程范式确实体验蛮不错的,我挺喜欢模式匹配,建造者模式,鼓励链式调用(手动点赞). + +最后在榜上捞了个Rank 39. + +![img_3.png](2024A-OS-I-Summary/img_3.png) + + +# 推荐阅读 + + + +1. [Rust官方文档](https://doc.rust-lang.org/book/title-page.html) - Rust的官方文档是最权威的学习资源之一,提供了详尽的语言特性和标准库介绍,适合所有级别的学习者。 + +2. [Rust精选](https://rustcc.cn/) - 提供了一系列聚焦于学习Rust的网络资源,包括但不限于使用Rust处理信号的文章,这些资源对于希望深入了解特定主题的学习者非常有帮助。 + +3. [《Rust 程序设计》](https://kaisery.github.io/trpl-zh-cn/) - 一本全面介绍Rust编程的书籍,适合希望系统学习Rust的新手。 + +4. [《Rust 权威指南》](https://rust-book.cs.dbappsecurity.com/) - 这本书深入讲解了Rust的核心概念和高级特性,适合已经有一定Rust基础的学习者。 + +5. [《Rust 实战》](https://rust-lang-nursery.github.io/rust-cookbook/intro.html) - 通过实战项目帮助读者掌握Rust编程的实际应用,适合希望通过实践加深理解的学习者。 + +6. [《深入理解 rust 并发编程》](https://rust-lang-nursery.github.io/rust-cookbook/concurrency.html) - 针对Rust的并发编程特性进行了详细的讲解,适合对并发编程感兴趣的开发者。 + +7. [《Command-Line Rust》](https://rust-cli.github.io/book/) - 专注于使用Rust开发命令行应用程序,适合对此类应用感兴趣的学习者。 + +8. [《Rust Atomics and Locks》](https://docs.rs/crossbeam/latest/crossbeam/atomic/index.html) - 深入讲解了Rust中的原子操作和锁机制,适合希望深入了解并发控制的学习者。 + +9. [《System Programing with Rust》](https://azerupi.github.io/mdBook/rust-system-programming/) - 介绍了如何使用Rust进行系统级编程,适合希望探索这一领域的开发者。 + +10. [《Rust 编程之道》](https://rustwiki.org/zh-CN/rust-by-example/) - 通过具体的例子和最佳实践,帮助读者更好地掌握Rust编程。 + + + + + + diff --git a/source/_posts/2024A-OS-I-Summary/img.png b/source/_posts/2024A-OS-I-Summary/img.png new file mode 100644 index 0000000000..0508f0d53b Binary files /dev/null and b/source/_posts/2024A-OS-I-Summary/img.png differ diff --git a/source/_posts/2024A-OS-I-Summary/img_1.png b/source/_posts/2024A-OS-I-Summary/img_1.png new file mode 100644 index 0000000000..7d5ed9c99b Binary files /dev/null and b/source/_posts/2024A-OS-I-Summary/img_1.png differ diff --git a/source/_posts/2024A-OS-I-Summary/img_2.png b/source/_posts/2024A-OS-I-Summary/img_2.png new file mode 100644 index 0000000000..131b0ec9b9 Binary files /dev/null and b/source/_posts/2024A-OS-I-Summary/img_2.png differ diff --git a/source/_posts/2024A-OS-I-Summary/img_3.png b/source/_posts/2024A-OS-I-Summary/img_3.png new file mode 100644 index 0000000000..c2cf8c5a43 Binary files /dev/null and b/source/_posts/2024A-OS-I-Summary/img_3.png differ diff --git a/source/_posts/2024A-OS-II-Summary.md b/source/_posts/2024A-OS-II-Summary.md new file mode 100644 index 0000000000..3e7ee47c21 --- /dev/null +++ b/source/_posts/2024A-OS-II-Summary.md @@ -0,0 +1,684 @@ +--- +title: 2024A-OS-II-Summary +date: 2024-11-10 12:34:26 +tags: + - auther:Whth + - repo:https://github.com/LearningOS/2024a-rcore-Whth + - OsBasics + - RustPractice +--- + + +* [引言](#引言) +* [在此之前](#在此之前) +* [第二阶段概述](#第二阶段概述) + * [环境配置](#环境配置) + * [战斗开始](#战斗开始) + * [ch1](#ch1) + * [问题与对应解决操作如下:](#问题与对应解决操作如下) + * [练习解题思路](#练习解题思路) + * [ch2](#ch2-) + * [问题与对应解决操作如下:](#问题与对应解决操作如下-1) + * [练习解题思路](#练习解题思路-1) + * [ch3](#ch3) + * [问题与对应解决操作如下:](#问题与对应解决操作如下-2) + * [练习解题思路](#练习解题思路-2) + * [ch4](#ch4) + * [问题与对应解决操作如下:](#问题与对应解决操作如下-3) + * [练习解题思路](#练习解题思路-3) + * [ch5](#ch5) + * [问题与对应解决操作如下:](#问题与对应解决操作如下-4) + * [练习解题思路](#练习解题思路-4) + * [ch6](#ch6) + * [问题与对应解决操作如下:](#问题与对应解决操作如下-5) + * [练习解题思路](#练习解题思路-5) + * [ch7](#ch7) + * [问题与对应解决操作如下:](#问题与对应解决操作如下-6) + * [练习解题思路](#练习解题思路-6) + * [ch8](#ch8) + * [问题与对应解决操作如下:](#问题与对应解决操作如下-7) + * [练习解题思路](#练习解题思路-7) +* [总结](#总结) + + + +---- + +# 引言 + +到了第二阶段,强度又上了一个等级,这次直接开始学习手搓操作系统了,中途也算是被折磨了不少,但是也收获良多. + +切实的把学到的理论知识给使出来了,也提升了一些语言能力. + + +---- + +# 在此之前 +- 读`Rcore-book`的时候切记不要一开始就直接先按着它给全部啃完,然后再写代码,最好是代码和文档一起走,看着文档的内容介绍,然后再根据它来分析具体的实现. +- 多打`trace`|`log`|`assert`来调试,不要直接panic, 用`gdb`+`tmux`着实有点麻烦. + + +---- + +# 第二阶段概述 + +## 环境配置 + +文档里面指定了使用`Ubuntu 22`作为开发环境,而我的宿主机是`Windows 11`,所以最后就用`WSL2`了,装了一个轻量点的`Debian for WSL` + +使用字节跳动的 `rsproxy` 安装`rustup` + +```fish +# 修改配置 ~/.config/fish/config.fish + +echo "export RUSTUP_DIST_SERVER "https://rsproxy.cn"" >> ~/.config/fish/config.fish +echo "export RUSTUP_UPDATE_ROOT "https://rsproxy.cn/rustup"" >> ~/.config/fish/config.fish + +``` + +安装 `rustup` +```fish +curl --proto '=https' --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | sh + +``` + +修改 `~/.cargo/config.toml` 文件,配置`rsproxy`为默认源 +``` +[source.crates-io] +replace-with = 'rsproxy-sparse' +[source.rsproxy] +registry = "https://rsproxy.cn/crates.io-index" +[source.rsproxy-sparse] +registry = "sparse+https://rsproxy.cn/index/" +[registries.rsproxy] +index = "https://rsproxy.cn/crates.io-index" +[net] +git-fetch-with-cli = true +``` + +安装 `qemu-system-riscv64` 和 `qemu-riscv` + +```fish +# 安装依赖 +sudo apt install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \ + gawk build-essential bison flex texinfo gperf libtool patchutils bc \ + zlib1g-dev libexpat-dev pkg-config libglib2.0-dev libpixman-1-dev git tmux python3 +# 下载并编译安装 qemu +wget https://download.qemu.org/qemu-7.0.0.tar.xz &&\ +tar xf qemu-7.0.0.tar.xz &&\ +cd qemu-7.0.0 &&\ +./configure --target-list=riscv64-softmmu,riscv64-linux-user && \ +make -j$(nproc) && sudo make install + +``` + +克隆`rcore-os`仓库,并且克隆测试用例仓库 +```fish +git clone https://github.com/LearningOS/2024a-rcore-Whth.git && \ +cd 2024a-rcore-Whth && \ +git clone https://github.com/LearningOS/rCore-Tutorial-Test-2024A user --depth 1 &&\ +git clone https://github.com/LearningOS/rCore-Tutorial-Checker-2024A.git ci-user --depth 1 &&\ +cp -r user ci-user/user +``` + + +运行第一个例子 +```fish +git checkout ch1 &&\ +cd os &&\ +make run +``` + +Success! +```fish +[rustsbi] RustSBI version 0.3.0-alpha.4, adapting to RISC-V SBI v1.0.0 +.______ __ __ _______.___________. _______..______ __ +| _ \ | | | | / | | / || _ \ | | +| |_) | | | | | | (----`---| |----`| (----`| |_) || | +| / | | | | \ \ | | \ \ | _ < | | +| |\ \----.| `--' |.----) | | | .----) | | |_) || | +| _| `._____| \______/ |_______/ |__| |_______/ |______/ |__| +[rustsbi] Implementation : RustSBI-QEMU Version 0.2.0-alpha.2 +[rustsbi] Platform Name : riscv-virtio,qemu +[rustsbi] Platform SMP : 1 +[rustsbi] Platform Memory : 0x80000000..0x88000000 +[rustsbi] Boot HART : 0 +[rustsbi] Device Tree Region : 0x87000000..0x87000ef2 +[rustsbi] Firmware Address : 0x80000000 +[rustsbi] Supervisor Address : 0x80200000 +[rustsbi] pmp01: 0x00000000..0x80000000 (-wr) +[rustsbi] pmp02: 0x80000000..0x80200000 (---) +[rustsbi] pmp03: 0x80200000..0x88000000 (xwr) +[rustsbi] pmp04: 0x88000000..0x00000000 (-wr) +[kernel] Hello, world! +``` + +## 战斗开始 + +> 注: 为了快速了解主要功能,使用了LLM来提取要点,仅作为参考, 不保证100%正确 + +### ch1 + +>本章主要内容是构建一个用户态和裸机的最小执行环境,包括理解执行环境、修改目标平台、移除标准库依赖、实现基本功能(如输出字符串和关机),并正确配置内存布局和栈空间,最终实现一个能够运行简单应用程序的环境。 + +#### 问题与对应解决操作如下: + + +| 问题 | 原因 | 解决方案 | 最终效果 | +|-----------------------------------------------------|-----------------------------------------------------------------------|----------------------------------------------------|--------------------------| +| 无法成功将`Hello World!` 移植到`riscv64gc-unknown-none-elf` | 报错的原因是目标平台上确实没有 Rust 标准库 std,也不存在任何受 OS 支持的系统调用,也就是裸机平台 (bare-metal)。 | 将标准库 `std` 替换为无平台依赖的核心库 `core`,以满足裸机平台的编译需求。 | 可以通过编译啦! | +| 通过编译后的程序是空程序,怎么让它变得可以执行用户代码? | 没有定义入口函数 | 添加`_start`作为入口函数 | 可以编译并且可以执行空的`loop` | +| `_start`编译得到的程序并不能正常退出,报段错误了,为什么? | U模式下尝试越权执行S指令导致的 | 根据`riscv`的指令标准,使用ecall完成权限的正常跳转,添加一个`syscall_exit` | 可以编译并且正常退出啦! | +| 没有显示很不好调试啊,怎么加一个? | `core`库里面并没有实现`println!` | 添加一个`syscall_write`和`stdout`用作实现`println!` | 可以正常显示输出啦! | | | | +| 现在的程序仍然是在用户态上运行的,而操作系统需要在内核态上运行,怎么解决? | 程序默认在用户态运行,没有切换到内核态。 | 修改程序入口点和初始化代码,确保程序在内核态运行。 | 程序在内核态运行,能够访问内核态资源。 | +| 内存布局不符合预期,直接退出了,怎么办? | 缺少或错误的链接脚本配置。 | 使用链接脚本 (Linker Script) 调整内存布局,使入口地址为 0x80200000。 | 内存布局符合预期,程序能够在指定地址开始执行。 | +| 未正确设置栈空间 | 没有为操作系统预留足够的栈空间,或者没有正确初始化栈指针。 | 在汇编文件中预留栈空间,并在启动时设置栈指针。 | 栈空间正确设置,程序可以安全地使用栈。 | +| .bss 段未清零 | .bss 段没有被初始化为零,可能导致未定义行为。 | 在 `rust_main` 函数中调用 `clear_bss` 函数清零 .bss 段。 | .bss 段被正确清零,避免了潜在的未定义行为。 | +| 缺少打印功能 | 没有实现或适配内核态的打印功能。 | 修改用户态的 `println` 宏,实现内核态的打印功能。 | 可以在内核态打印信息,便于调试。 | +| 异常处理时未打印错误位置 | 异常处理函数 `panic` 没有实现打印错误位置的功能。 | 重写 `panic` 函数,使其在异常时打印错误发生的位置。 | 异常时能够打印错误位置,方便定位问题。 | +| 日志功能缺失 | 没有集成日志库,或日志库未正确配置。 | 在项目中引入日志库 `log` 并实现日志模块。 | 可以记录不同级别的日志信息,增强调试能力。 | + +至此,我们完成了一个可以执行用户态代码的裸机程序😋。 +```fish +>>> cd os&&make run LOG=DEBUG + +[rustsbi] RustSBI version 0.3.0-alpha.4, adapting to RISC-V SBI v1.0.0 +.______ __ __ _______.___________. _______..______ __ +| _ \ | | | | / | | / || _ \ | | +| |_) | | | | | | (----`---| |----`| (----`| |_) || | +| / | | | | \ \ | | \ \ | _ < | | +| |\ \----.| `--' |.----) | | | .----) | | |_) || | +| _| `._____| \______/ |_______/ |__| |_______/ |______/ |__| +[rustsbi] Implementation : RustSBI-QEMU Version 0.2.0-alpha.2 +[rustsbi] Platform Name : riscv-virtio,qemu +[rustsbi] Platform SMP : 1 +[rustsbi] Platform Memory : 0x80000000..0x88000000 +[rustsbi] Boot HART : 0 +[rustsbi] Device Tree Region : 0x87000000..0x87000ef2 +[rustsbi] Firmware Address : 0x80000000 +[rustsbi] Supervisor Address : 0x80200000 +[rustsbi] pmp01: 0x00000000..0x80000000 (-wr) +[rustsbi] pmp02: 0x80000000..0x80200000 (---) +[rustsbi] pmp03: 0x80200000..0x88000000 (xwr) +[rustsbi] pmp04: 0x88000000..0x00000000 (-wr) +[kernel] Hello, world! +[DEBUG] [kernel] .rodata [0x80202000, 0x80203000) +[ INFO] [kernel] .data [0x80203000, 0x80204000) +[ WARN] [kernel] boot_stack top=bottom=0x80214000, lower_bound=0x80204000 +[ERROR] [kernel] .bss [0x80214000, 0x80215000) +``` + + +#### 练习解题思路 +- undefined + +---- + +### ch2 + +>本章主要内容是实现一个简单的批处理系统,包括设计和实现应用程序、管理内存布局、实现系统调用、编译生成应用程序二进制码、将应用程序链接到内核、实现特权级切换和 Trap 管理,最终实现内核加载并执行用户态应用程序的功能。 + + +#### 问题与对应解决操作如下: + + + +| 问题 | 原因 | 解决方案 | 最终效果 | +|----------------------|-----------------------------------------|---------------------------------------------------------------------|----------------------------------| +| 应用程序设计看起来好复杂啊! | 用户库预留了很多未来才会用到的系统调用接口,还有一些高级功能。 | 只关注本节提到的部分,忽略那些未来的神秘东西。 | 简化了开发过程,让你能快速上手。 | +| 找不到 main 函数,编译器报错了! | 默认情况下,Rust 程序需要一个 main 函数作为入口。 | 移除 main 函数,使用自定义的 _start 函数。 | 程序不再依赖标准库的 main 函数,自由度更高。 | +| 怎么输出个字符串这么难? | 标准库的 println! 宏依赖于标准 I/O,裸机环境下没有这些支持。 | 自己实现一个输出字符串的函数。 | 可以在裸机环境下愉快地打印字符串了! | +| 程序跑起来,但是一直不退出,卡在那里了! | 缺少一个优雅的退出机制。 | 实现一个关机功能,让程序能够正常退出。 | 程序运行完后,QEMU 会优雅地退出,不再卡在那里。 | +| 内存布局怎么这么乱? | 缺少或错误的链接脚本配置。 | 使用链接脚本 (Linker Script) 调整内存布局。 | 内存布局变得整洁有序,程序在指定地址开始执行。 | +| 栈空间怎么总是出问题? | 没有为操作系统预留足够的栈空间,或者没有正确初始化栈指针。 | 在汇编文件中预留栈空间,并在启动时设置栈指针。 | 栈空间正确设置,程序可以安全地使用栈。 | +| .bss 段里的垃圾数据怎么清理? | .bss 段没有被初始化为零,可能导致未定义行为。 | 在 `rust_main` 函数中调用 `clear_bss` 函数清零 .bss 段。 | .bss 段被正确清零,避免了潜在的未定义行为。 | +| 怎么和内核通信? | 应用程序需要调用内核提供的系统调用。 | 实现系统调用接口,使用 ecall 指令。 | 应用程序可以通过系统调用与内核通信,完成各种任务。 | +| 怎么编译生成应用程序二进制码? | 缺少编译和链接的步骤。 | 使用 Cargo 和 objcopy 工具编译生成二进制文件。 | 生成了可以在裸机上运行的应用程序二进制文件。 | +| 怎么把应用程序链接到内核? | 应用程序的二进制镜像文件需要作为数据段链接到内核里,内核需要知道它们的位置。 | 使用汇编文件 `link_app.S` 将应用程序的二进制镜像链接到内核。 | 应用程序的二进制镜像被正确链接到内核中。 | +| 找不到应用程序的起始和结束位置? | 内核需要知道应用程序的数量和它们的起始和结束位置。 | 在 `link_app.S` 中定义全局符号,记录应用程序的数量和位置。 | 内核能够准确找到每个应用程序的起始和结束位置。 | +| 如何初始化应用管理器? | 需要一个全局的应用管理器来管理应用程序的加载和运行。 | 使用 `lazy_static!` 宏创建全局的 `AppManager` 实例。 | 应用管理器在第一次使用时自动初始化。 | +| 如何加载应用程序? | 需要将应用程序的二进制镜像加载到指定的内存地址。 | 实现 `load_app` 方法,将应用程序加载到 0x80400000 起始的内存地址。 | 应用程序被正确加载到内存中,准备运行。 | +| i-cache 不一致怎么办? | 修改内存中的代码段时,i-cache 中的内容可能与内存不一致。 | 在加载应用程序前,使用 `fence.i` 指令清空 i-cache。 | 保证程序执行的正确性,避免 i-cache 与内存不一致的问题。 | +| 怎么管理多个应用程序? | 需要一个机制来管理多个应用程序的加载和运行。 | 在 `AppManager` 中维护应用程序的数量和当前运行的应用程序索引。 | 能够按顺序加载和运行多个应用程序。 | +| 如何实现批处理操作系统的接口? | 需要提供接口来初始化和运行下一个应用程序。 | 实现 `init` 和 `run_next_app` 方法,分别用于初始化和运行下一个应用程序。 | 批处理操作系统能够初始化并按顺序运行多个应用程序。 | +| 怎么实现特权级切换? | 批处理操作系统需要在用户态和内核态之间切换,以便初始化、监控和处理应用程序。 | 使用 RISC-V 的特权级切换机制,通过控制状态寄存器(CSR)和硬件支持。 | 实现了用户态和内核态之间的平滑切换。 | +| 特权级切换的起因是什么? | 需要在启动应用程序、处理系统调用、处理错误和应用程序结束时进行特权级切换。 | 确保在这些关键时刻能够正确切换特权级。 | 确保了批处理操作系统的稳定性和安全性。 | +| 特权级切换涉及哪些控制状态寄存器? | 需要管理特权级切换时的各种状态信息。 | 使用 sstatus、sepc、scause、stval 和 stvec 等 CSR。 | 确保了 Trap 处理的正确性和可靠性。 | +| 如何处理 Trap 进入 S 特权级? | CPU 需要从用户态切换到内核态处理 Trap。 | 硬件自动修改 sstatus、sepc、scause 和 stval 寄存器,并跳转到 stvec 指定的入口地址。 | 确保了 Trap 处理的高效性和准确性。 | +| 如何处理 Trap 返回用户态? | Trap 处理完成后需要返回用户态继续执行。 | 使用 sret 指令恢复 sstatus 和 sepc 寄存器,并跳转回用户态。 | 确保了 Trap 处理后的正确返回。 | +| 用户栈和内核栈有什么区别? | Trap 发生时需要切换栈,以保存和恢复寄存器状态。 | 定义了 `KernelStack` 和 `UserStack` 类型,并实现 `get_sp` 方法获取栈顶地址。 | 确保了 Trap 处理时栈的正确管理和切换。 | +| 如何保存和恢复 Trap 上下文? | Trap 发生时需要保存当前的寄存器状态,Trap 处理后需要恢复这些状态。 | 定义 `TrapContext` 结构体,包含所有通用寄存器和必要的 CSR。 | 确保了 Trap 处理前后寄存器状态的一致性。 | +| 具体如何保存 Trap 上下文? | Trap 发生时需要保存当前的寄存器状态,以便 Trap 处理后恢复。 | 在 `__alltraps` 汇编函数中保存所有通用寄存器和必要的 CSR。 | 确保了 Trap 处理前寄存器状态的正确保存。 | +| 具体如何设置 Trap 处理入口? | 需要指定 Trap 处理的入口地址。 | 在 `trap::init` 中设置 `stvec` 寄存器指向 `__alltraps`。 | 确保了 Trap 处理的入口地址正确设置。 | +| 具体如何恢复 Trap 上下文? | Trap 处理完成后需要恢复寄存器状态,继续执行应用程序。 | 在 `__restore` 汇编函数中恢复所有通用寄存器和必要的 CSR。 | 确保了 Trap 处理后寄存器状态的正确恢复。 | +| 如何处理系统调用? | 应用程序通过 ecall 指令触发系统调用,需要在内核态处理。 | 在 `trap_handler` 中识别 ecall 并调用 `syscall` 函数。 | 正确处理了系统调用请求。 | +| 如何处理访存错误和非法指令? | 应用程序可能出现访存错误或非法指令,需要妥善处理。 | 在 `trap_handler` 中捕获并处理这些错误,调用 `run_next_app`。 | 确保了应用程序出错时能够正确处理并切换到下一个应用程序。 | +| 如何实现具体的系统调用? | 需要根据 syscall ID 调用相应的处理函数。 | 在 `syscall` 模块中实现 `sys_write` 和 `sys_exit`。 | 提供了基本的系统调用功能。 | +| 如何打印系统调用的结果? | 需要将系统调用的结果输出到控制台。 | 在 `sys_write` 中将缓冲区内容转换为字符串并打印。 | 确保了系统调用结果的正确输出。 | +| 如何处理应用程序退出? | 应用程序退出时需要通知内核并切换到下一个应用程序。 | 在 `sys_exit` 中打印退出信息并调用 `run_next_app`。 | 确保了应用程序退出后的正确处理和切换。 | +| 如何执行下一个应用程序? | 当批处理操作系统初始化完成或某个应用程序运行结束时,需要切换到下一个应用程序。 | 在 `run_next_app` 函数中加载并运行下一个应用程序。 | 确保了应用程序的连续运行。 | +| 如何跳转到应用程序入口点? | 需要将 CPU 的执行跳转到应用程序的入口点 0x80400000。 | 在 `TrapContext::app_init_context` 中设置 `sepc` 为 0x80400000。 | 确保了 CPU 跳转到应用程序的入口点。 | +| 如何切换到用户栈? | 需要将使用的栈从内核栈切换到用户栈。 | 在 `TrapContext::app_init_context` 中设置 `sp` 为用户栈的栈顶地址。 | 确保了 CPU 使用用户栈。 | +| 如何设置 sscratch 指向内核栈? | 在 Trap 处理时需要确保 sscratch 指向内核栈。 | 在 `__restore` 函数中设置 `sscratch` 为内核栈的栈顶地址。 | 确保了 sscratch 指向内核栈。 | +| 如何从 S 特权级切换到 U 特权级? | 需要从 S 特权级切换到 U 特权级以运行应用程序。 | 在 `TrapContext::app_init_context` 中设置 `sstatus` 的 `SPP` 字段为 `User`。 | 确保了特权级从 S 切换到 U。 | +| 如何复用 __restore 代码? | 需要复用 `__restore` 代码来简化启动应用程序的过程。 | 在 `run_next_app` 中调用 `__restore` 并传递一个特殊构造的 Trap 上下文。 | 确保了启动应用程序的简洁性和一致性。 | + +至此我们的OS可以承载多个用户态程序,并且可以切换用户态程序,并且可以返回用户态程序。 + +#### 练习解题思路 +- undefined +- +---- + +### ch3 + +>主要介绍了多道程序与分时多任务的概念,包括多道程序的放置与加载、任务切换的实现、任务管理机制、系统调用如yield和exit的实现、以及在RISC-V架构下处理嵌套中断和实现抢占式调度的方法。 + + +#### 问题与对应解决操作如下: + +| 问题 | 原因 | 解决方案 | 最终效果 | +|-----------------------------|------------------------------------|------------------------------------------------------------------|--------------------------| +| 如何放置多个应用程序? | 内核需要让每个应用程序加载到不同的内存地址,以支持多道程序。 | 为每个应用程序指定不同的起始地址。 | 确保了多个应用程序可以同时驻留在内存中。 | +| 如何为每个应用程序指定不同的起始地址? | 每个应用程序需要有自己的起始地址,以避免冲突。 | 使用 `user/build.py` 脚本为每个应用程序指定起始地址。 | 确保了每个应用程序的起始地址不同。 | +| 如何加载多个应用程序? | 需要在内核初始化时将所有应用程序加载到内存中。 | 在 `loader` 模块的 `load_apps` 函数中加载所有应用程序。 | 确保了所有应用程序在内核初始化时被加载到内存中。 | +| 如何清空应用程序区域? | 在加载新应用程序之前,需要清空内存区域以避免残留数据。 | 在 `load_apps` 函数中清空内存区域。 | 确保了内存区域在加载新应用程序前被清空。 | +| 如何加载应用程序到指定地址? | 需要将应用程序的二进制数据加载到指定的内存地址。 | 在 `load_apps` 函数中将应用程序数据复制到指定地址。 | 确保了应用程序被正确加载到指定的内存地址。 | +| 如何计算应用程序的基地址? | 需要为每个应用程序计算一个唯一的基地址。 | 在 `get_base_i` 函数中计算应用程序的基地址。 | 确保了每个应用程序的基地址唯一。 | +| 如何实现任务切换? | 任务切换是操作系统的核心机制,需要在应用运行中交出 CPU 使用权。 | 设计和实现任务切换机制,包括保存和恢复任务上下文。 | 确保了多个任务之间的平滑切换。 | +| 任务切换与 Trap 切换有何不同? | 任务切换和 Trap 切换在某些方面有相似之处,但也有一些关键差异。 | 任务切换不涉及特权级切换,部分由编译器完成。 | 确保了任务切换的高效性和透明性。 | +| 如何保存任务上下文? | 任务切换时需要保存当前任务的寄存器状态。 | 在 `__switch` 汇编函数中保存 `ra`、`sp` 和 `s0~s11`。 | 确保了任务上下文的正确保存。 | +| 如何恢复任务上下文? | 任务切换时需要恢复目标任务的寄存器状态。 | 在 `__switch` 汇编函数中恢复 `ra`、`sp` 和 `s0~s11`。 | 确保了任务上下文的正确恢复。 | +| 如何实现 `__switch` 函数? | 需要一个函数来完成任务上下文的保存和恢复。 | 在 `switch.S` 中实现 `__switch` 汇编函数。 | 确保了任务切换的高效性和可靠性。 | +| 如何定义 `TaskContext` 结构体? | 需要一个结构体来存储任务上下文。 | 在 `task/context.rs` 中定义 `TaskContext` 结构体。 | 确保了任务上下文的正确表示。 | +| 如何在 Rust 中调用 `__switch` 函数? | 需要在 Rust 代码中调用 `__switch` 函数。 | 在 `task/switch.rs` 中声明 `__switch` 外部函数。 | 确保了 `__switch` 函数的正确调用。 | +| 如何管理任务? | 内核需要管理多个任务,包括任务的状态和上下文。 | 维护任务控制块和任务管理器。 | 确保了任务的有效管理和调度。 | +| 任务运行状态有哪些? | 任务需要有不同的运行状态来管理其生命周期。 | 定义 `TaskStatus` 枚举,包括 `UnInit`、`Ready`、`Running` 和 `Exited`。 | 确保了任务状态的清晰表示。 | +| 任务控制块包含哪些信息? | 任务控制块需要存储任务的状态和上下文。 | 在 `TaskControlBlock` 结构体中包含 `task_status` 和 `task_cx`。 | 确保了任务控制块的完整性和一致性。 | +| 如何实现任务管理器? | 需要一个全局的任务管理器来管理任务控制块。 | 创建 `TaskManager` 结构体,包含任务控制块数组和当前任务索引。 | 确保了任务管理器的全局可见性和管理能力。 | +| 如何初始化任务管理器? | 任务管理器需要在内核初始化时进行初始化。 | 在 `TASK_MANAGER` 的 `lazy_static!` 块中初始化任务管理器。 | 确保了任务管理器的正确初始化。 | +| 如何实现 `sys_yield` 系统调用? | 应用需要主动交出 CPU 使用权。 | 实现 `sys_yield` 函数,调用 `suspend_current_and_run_next`。 | 确保了应用可以主动交出 CPU 使用权。 | +| 如何实现 `sys_exit` 系统调用? | 应用需要主动退出。 | 实现 `sys_exit` 函数,调用 `exit_current_and_run_next`。 | 确保了应用可以主动退出。 | +| 如何实现任务切换? | 任务切换需要保存和恢复任务上下文。 | 实现 `__switch` 汇编函数,保存和恢复 `ra`、`sp` 和 `s0~s11`。 | 确保了任务切换的高效性和可靠性。 | +| 如何初始化任务上下文? | 任务控制块需要初始化任务上下文。 | 在 `init_app_cx` 中初始化 Trap 上下文,并在 `goto_restore` 中设置 `ra` 和 `sp`。 | 确保了任务上下文的正确初始化。 | +| 如何执行第一个任务? | 需要从任务管理器中选择并执行第一个任务。 | 在 `run_first_task` 中调用 `__switch` 切换到第一个任务的上下文。 | 确保了第一个任务的正确执行。 | +| 如何实现分时多任务系统? | 现代任务调度算法需要抢占式调度,每个应用只能连续执行一段时间。 | 使用时间片轮转算法 (RR) 对应用进行调度。 | 确保了任务的公平调度。 | +| 如何实现时钟中断与计时器? | 实现调度算法需要计时。 | 使用 RISC-V 的 mtime 和 mtimecmp 寄存器。 | 确保了精确的计时和时钟中断。 | +| 如何获取当前时间? | 应用需要获取当前时间。 | 实现 `get_time` 和 `get_time_us` 函数。 | 确保了应用可以获取当前时间。 | +| 如何设置时钟中断? | 需要设置时钟中断来触发任务调度。 | 实现 `set_timer` 和 `set_next_trigger` 函数。 | 确保了时钟中断的正确设置。 | +| 如何处理时钟中断? | 时钟中断触发时需要进行任务切换。 | 在 `trap_handler` 中处理 S 特权级时钟中断。 | 确保了时钟中断触发后的任务切换。 | +| 如何启用时钟中断? | 需要启用 S 特权级时钟中断以避免被屏蔽。 | 在 `rust_main` 中调用 `enable_timer_interrupt`。 | 确保了 S 特权级时钟中断的启用。 | + + +至此,我们完成了一个简单的内核,它已经并行执行了多个应用 + +#### 练习解题思路 + +- 直接为每一个task打上数据标签保存所有的`taskinfo` 相关信息. +- 额外的为了正常统计syscall计数需要为syscall添加一个 `hook`用作每次`syscall`被调用时可以更新当前`TaskControlBlock`的syscall计数 +- 最后实现 `syscall_get_task_info`函数本体,从`TaskControlBlock`哪里取出数据返回即可. + +---- + +### ch4 + +>本章深入探讨了RISC-V架构下的SV39多级页表机制的实现,包括虚拟地址和物理地址的管理、内存控制相关的CSR寄存器、地址格式与组成等,并详细介绍了如何实现地址空间抽象、内核与应用的地址空间管理以及基于地址空间的分时多任务处理。 + + +#### 问题与对应解决操作如下: + +| 问题 | 原因 | 解决方案 | 最终效果 | +|---------------------------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------| +| 如何实现 SV39 多级页表机制? | 就像给房子建楼层一样,我们需要在内核中搭建 RV64 架构的 SV39 分页系统。 | 分步骤来,先打地基(准备环境),再一层一层往上建。 | 我们就有了一个稳固的多层建筑,即实现了 SV39 分页机制。 | +| 虚拟地址和物理地址的区别是什么? | 访问内存时,就像找人,得知道是在虚拟世界还是现实世界找。 | 修改 satp 这个特殊的开关,告诉系统我们要在哪个世界里玩。 | 系统就能正确地把虚拟世界的地址翻译成现实世界的地址了。 | +| 如何启用分页模式? | 默认情况下,MMU(内存管理单元)这个大管家没上班,我们需要叫它起来工作。 | 把 satp 的 MODE 字段设置为 8,就像是给大管家发了个开工通知。 | 这样,大管家就正式上岗,开始处理分页事务了。 | +| 如何定义地址和页号的类型? | 在编程的世界里,给东西起名字很重要,尤其是地址和页号这种关键角色。 | 用 Rust 语言定义 `PhysAddr`、`VirtAddr`、`PhysPageNum` 和 `VirtPageNum` 四个小伙伴。 | 这样每个小伙伴都有了自己的身份证明,方便管理。 | +| 如何实现地址和页号之间的转换? | 地址和页号之间需要相互认识,好比是不同语言的人交流。 | 为它们实现 `From` 和 `Into` 特性,还有 `floor` 和 `ceil` 方法,就像教它们一门通用语言。 | 这样,地址和页号就可以无障碍沟通了。 | +| 如何定义页表项的数据结构? | 页表项是页表里的小兵,要有自己的样子。 | 使用 `bitflags` 工具箱来定义 `PTEFlags` 和 `PageTableEntry`,给小兵们穿上制服。 | 这样,每个小兵都有了独特的身份标识。 | +| 如何生成页表项? | 小兵们需要有出生证,证明它们是合法的。 | 为 `PageTableEntry` 添加 `new` 和 `empty` 方法,就像是给小兵们颁发出生证。 | 这样,小兵们就有了合法的身份。 | +| 如何检查页表项的标志位? | 小兵们的任务和能力需要定期检查。 | 为 `PageTableEntry` 添加 `is_valid` 方法,就像是对小兵们的能力进行审查。 | 这样,我们就能确保每个小兵都在正确地执行任务。 | +| 如何管理物理页帧? | 物理页帧就像是仓库里的货物,需要有人管理。 | 实现 `StackFrameAllocator`,就像请了一个仓库管理员。 | 这样,货物的进出就有条不紊了。 | +| 如何确定可用物理内存范围? | 仓库有多大,哪些地方可以存放货物,这些都是需要事先规划好的。 | 在链接脚本 `os/src/linker.ld` 里用 `ekernel` 符号标出内核数据的终点。 | 这样,我们就明确了哪些地方是可以用来存放货物的。 | +| 如何定义物理页帧管理器的 Trait? | 为了规范仓库管理员的行为,需要有一套标准。 | 定义 `FrameAllocator` Trait,就像是制定了仓库管理的标准流程。 | 这样,不管谁来当仓库管理员,都能按规矩办事。 | +| 如何实现 `StackFrameAllocator`? | 有了标准,还需要有一个具体的实施者。 | 实现 `StackFrameAllocator` 结构体及相关方法,就像是选定了一个具体的仓库管理员。 | 这样,货物的管理就有了实际的操作者。 | +| 如何初始化 `StackFrameAllocator`? | 仓库管理员上任前,需要了解仓库的情况。 | 为 `StackFrameAllocator` 实现 `new` 和 `init` 方法,就像是管理员做入职培训。 | 这样,管理员就能快速进入状态,开始工作。 | +| 如何实现物理页帧的分配? | 仓库管理员要懂得如何合理分配货物。 | 为 `StackFrameAllocator` 实现 `alloc` 方法,就像是训练管理员如何发放货物。 | 这样,货物的分配就更加合理有效了。 | +| 如何实现物理页帧的回收? | 当货物不再需要时,要能够及时回收,以便再次利用。 | 为 `StackFrameAllocator` 实现 `dealloc` 方法,就像是教管理员如何回收货物。 | 这样,仓库的空间利用率就提高了。 | +| 如何创建物理页帧的全局实例? | 仓库需要有一个总管,随时调用资源。 | 创建 `FRAME_ALLOCATOR` 全局实例,并完成初始化。 | 这样,任何地方都可以调用总管来获取或归还资源了。 | +| 如何实现物理页帧分配/回收的接口? | 总管需要提供简单直接的服务窗口。 | 实现 `frame_alloc` 和 `frame_dealloc` 函数,就像是开设了服务窗口。 | 这样,用户就能很方便地请求或归还资源了。 | +| 如何实现 `FrameTracker`? | 为了确保货物的安全,需要有个追踪系统。 | 实现 `FrameTracker` 结构体及相关方法,就像是建立了一套货物追踪系统。 | 这样,货物的状态就能实时监控了。 | +| 如何实现 `FrameTracker` 的自动回收? | 当货物不再使用时,应该能够自动归还仓库。 | 为 `FrameTracker` 实现 `Drop` 特性,就像是给货物装上了自动归还装置。 | 这样,货物就能自动回到仓库,减少了人为操作。 | +| 如何实现多级页表的基本数据结构? | 多级页表就像是一栋多层楼的大厦,需要设计好每一层的布局。 | 定义 `PageTable` 结构体及相关方法,就像是设计好了大厦的蓝图。 | 这样,大厦的建设就有了依据。 | +| 如何初始化多级页表? | 大厦建设前,需要打好地基。 | 为 `PageTable` 实现 `new` 方法,就像是为大厦打好地基。 | 这样,大厦的建设就有了坚实的基础。 | +| 如何实现多级页表的映射和解除映射? | 大厦里的房间需要能够灵活地分配和收回。 | 为 `PageTable` 实现 `map` 和 `unmap` 方法,就像是制定了房间分配和收回的规则。 | 这样,房间的管理就更加灵活高效了。 | +| 如何在多级页表中查找页表项? | 在多层楼的大厦里找到特定的房间,需要有一张详细的楼层图。 | 为 `PageTable` 实现 `find_pte_create` 方法,就像是制作了一份详细的楼层指南。 | 这样,就能快速准确地找到目标房间了。 | +| 如何访问物理页帧? | 要想进入仓库取货,需要有专门的通道。 | 为 `PhysPageNum` 实现 `get_pte_array`、`get_bytes_array` 和 `get_mut` 方法,就像是设置了专用的取货通道。 | 这样,取货就更加便捷了。 | +| 如何实现恒等映射? | 有时候,最简单的办法就是最好的。 | 使用恒等映射,就像是让虚拟页号和物理页号一一对应。 | 这样,系统就能以最简单的方式工作了。 | +| 如何实现手动查页表的方法? | 有时候,我们需要绕过系统,直接从底层获取信息。 | 为 `PageTable` 实现 `from_token`、`find_pte` 和 `translate` 方法,就像是掌握了直达底层的密道。 | 这样,即使没有系统的帮助,也能快速获取所需的信息。 | +| 如何实现地址空间的抽象? | 地址空间就像是一个大容器,里面装着很多小容器。 | 定义 `MapArea` 和 `MemorySet` 结构体及方法,就像是设计了一个可以装很多小容器的大箱子。 | 这样,地址空间的管理就更加有序了。 | +| 如何描述逻辑段? | 每个小容器都有自己的用途,需要清楚地标记出来。 | 定义 `MapArea` 结构体,就像是给每个小容器贴上了标签。 | 这样,每个小容器的用途就一目了然了。 | +| 如何实现逻辑段的映射方式? | 不同的小容器可能有不同的打开方式。 | 定义 `MapType` 枚举,就像是给每种小容器设定了打开方式。 | 这样,无论哪种小容器,都能轻松打开。 | +| 如何实现逻辑段的权限控制? | 每个小容器都有自己的门锁,需要控制谁能开锁。 | 定义 `MapPermission` 结构体,就像是给每个小容器安装了智能门锁。 | 这样,只有被授权的人才能打开小容器。 | +| 如何实现地址空间的管理? | 管理一个大容器,需要有一个好的计划。 | 定义 `MemorySet` 结构体及其方法,就像是制定了一份管理大容器的计划书。 | 这样,大容器内的所有小容器就能得到有效的管理了。 | +| 如何初始化地址空间? | 需要初始化地址空间。 | 实现 `MemorySet::new_bare` 方法。 | 确保了地址空间的正确初始化。 | +| 如何在地址空间中插入逻辑段? | 需要在地址空间中插入新的逻辑段。 | 实现 `MemorySet::push` 方法。 | 确保了逻辑段的正确插入。 | +| 如何插入 Framed 方式的逻辑段? | 需要在地址空间中插入 Framed 方式的逻辑段。 | 实现 `MemorySet::insert_framed_area` 方法。 | 确保了 Framed 方式逻辑段的正确插入。 | +| 如何生成内核的地址空间? | 需要生成内核的地址空间。 | 实现 `MemorySet::new_kernel` 方法。 | 确保了内核地址空间的生成。 | +| 如何从 ELF 文件生成应用的地址空间? | 需要从 ELF 文件生成应用的地址空间。 | 实现 `MemorySet::from_elf` 方法。 | 确保了应用地址空间的生成。 | +| 如何实现逻辑段的映射? | 需要实现逻辑段的映射。 | 实现 `MapArea::map` 方法。 | 确保了逻辑段的正确映射。 | +| 如何实现逻辑段的解映射? | 需要实现逻辑段的解映射。 | 实现 `MapArea::unmap` 方法。 | 确保了逻辑段的正确解映射。 | +| 如何实现逻辑段的数据拷贝? | 需要实现逻辑段的数据拷贝。 | 实现 `MapArea::copy_data` 方法。 | 确保了逻辑段的数据拷贝。 | +| 如何实现单个虚拟页面的映射? | 需要实现单个虚拟页面的映射。 | 实现 `MapArea::map_one` 方法。 | 确保了单个虚拟页面的正确映射。 | +| 如何实现单个虚拟页面的解映射? | 需要实现单个虚拟页面的解映射。 | 实现 `MapArea::unmap_one` 方法。 | 确保了单个虚拟页面的正确解映射。 | +| 如何实现内核地址空间? | 需要实现内核地址空间。 | 定义 `MemorySet::new_kernel` 方法。 | 确保了内核地址空间的创建。 | +| 如何映射内核的跳板? | 需要映射内核的跳板。 | 实现 `MemorySet::map_trampoline` 方法。 | 确保了跳板的正确映射。 | +| 如何映射内核的各个逻辑段? | 需要映射内核的各个逻辑段。 | 在 `MemorySet::new_kernel` 方法中调用 `push` 方法。 | 确保了各个逻辑段的正确映射。 | +| 如何处理内核栈? | 需要处理内核栈。 | 在 `MemorySet::new_kernel` 方法中设置内核栈。 | 确保了内核栈的正确设置。 | +| 如何设置保护页面? | 需要设置保护页面。 | 在内核栈之间预留一个保护页面。 | 确保了内核栈的安全性。 | +| 如何实现恒等映射? | 需要实现恒等映射。 | 使用 `MapType::Identical` 映射方式。 | 确保了内核数据段的正确访问。 | +| 如何设置逻辑段的权限? | 需要设置逻辑段的权限。 | 使用 `MapPermission` 设置权限。 | 确保了逻辑段的权限控制。 | +| 如何创建应用地址空间? | 需要创建应用地址空间。 | 实现 `MemorySet::from_elf` 方法。 | 确保了应用地址空间的创建。 | +| 如何解析 ELF 格式数据? | 需要解析 ELF 格式数据。 | 使用 `xmas_elf` crate 解析 ELF 数据。 | 确保了 ELF 数据的正确解析。 | +| 如何映射跳板? | 需要映射跳板。 | 在 `MemorySet::from_elf` 方法中调用 `map_trampoline` 方法。 | 确保了跳板的正确映射。 | +| 如何映射应用的各个逻辑段? | 需要映射应用的各个逻辑段。 | 在 `MemorySet::from_elf` 方法中调用 `push` 方法。 | 确保了各个逻辑段的正确映射。 | +| 如何处理用户栈? | 需要处理用户栈。 | 在 `MemorySet::from_elf` 方法中设置用户栈。 | 确保了用户栈的正确设置。 | +| 如何设置保护页面? | 需要设置保护页面。 | 在用户栈下方预留一个保护页面。 | 确保了用户栈的安全性。 | +| 如何映射 Trap 上下文? | 需要映射 Trap 上下文。 | 在 `MemorySet::from_elf` 方法中映射 Trap 上下文。 | 确保了 Trap 上下文的正确映射。 | +| 如何返回用户栈顶地址和入口点地址? | 需要返回用户栈顶地址和入口点地址。 | 在 `MemorySet::from_elf` 方法中返回这些地址。 | 确保了这些地址的正确返回。 | +| 如何平滑地从物理地址直接访问过渡到分页模式下的虚拟地址访问 | 在开启分页模式后,CPU 访问内存的方式发生了变化,需要确保切换前后地址空间的连续性 | 在切换satp的指令附近保持地址空间映射的连续性,如保持恒等映射 | 成功实现了从物理地址直接访问到虚拟地址访问的平滑过渡,确保了CPU指令的连续执行不受影响 | +| 如何处理多级页表带来的性能下降问题 | 多级页表增加了MMU的访存次数,加大了多级缓存的压力,导致性能开销增大 | 引入快表(TLB)存储虚拟页号到页表项的映射,减少MMU的访存次数;在切换地址空间时清空快表以保证映射关系的正确性 | 有效缓解了多级页表带来的性能问题,提高了系统的整体效率 | +| 在启用分页机制后,如何高效地在Trap处理时完成地址空间的切换 | 启用分页机制后,Trap处理不仅需要切换栈,还需要切换地址空间,这增加了操作的复杂性 | 使用跳板机制,将应用的Trap上下文存放在应用地址空间的次高页面,避免了在保存Trap上下文前切换到内核地址空间 | 实现了Trap处理时地址空间的高效切换,简化了操作步骤,同时保证了地址空间的安全隔离 | +| 如何解决每个应用地址空间都映射内核段带来的内存占用和安全问题 | 每个应用地址空间都映射内核段会导致额外的内存占用,并且存在安全风险,如熔断漏洞 | 采用内核与应用地址空间隔离的设计,每个应用有自己的地址空间,内核有独立的地址空间 | 显著减少了内存占用,提升了系统的安全性,同时也能更好地支持任务的并发执行 | +| 如何高效地在 Trap 处理时保存和恢复上下文 | Trap 处理不仅需要保存和恢复寄存器状态,还需要切换地址空间,传统的 Trap 处理方法在启用分页机制后变得复杂 | 在 Trap 上下文中增加额外的信息:`kernel_satp`(内核地址空间的 token)、`kernel_sp`(应用内核栈顶的虚拟地址)、`trap_handler`(trap handler 入口点的虚拟地址)。这些信息在应用初始化时由内核写入,之后不再修改 | 提高了 Trap 处理的效率,简化了地址空间切换的过程,确保了系统的稳定性和安全性 | +| 如何确保 Trap 处理时能够平滑地切换地址空间 | 在 Trap 处理过程中,需要从用户地址空间切换到内核地址空间,确保指令能够连续执行 | 使用跳板页面(Trampoline Page),将汇编代码放置在 `.text.trampoline` 段,并对齐到代码段的一个页面中。跳板页面在内核和应用地址空间中均映射到同一物理页帧 | 实现了平滑的地址空间切换,确保了 Trap 处理时指令的连续执行,提高了系统的整体性能 | +| 为什么在 __alltraps 中需要使用 `jr` 而不是 `call` | 在内存布局中,跳转指令和 trap_handler 都在代码段内,但实际执行时的虚拟地址与编译时设置的地址不同 | 使用 `jr` 指令通过寄存器跳转到 trap_handler 入口点,避免了地址偏移量计算错误的问题 | 确保了跳转指令的正确性,避免了因地址偏移量计算错误导致的程序崩溃 | +| 如何管理任务以确保应用的安全隔离 | 为了使应用在运行时有一个安全隔离且符合编译器给应用设定的地址空间布局,操作系统需要对任务进行更多的管理 | 扩展任务控制块(Task Control Block),包含应用的地址空间 `memory_set`、Trap 上下文的物理页号 `trap_cx_ppn` 和应用数据的大小 `base_size` | 提高了任务管理的灵活性和安全性,确保了应用在运行时的安全隔离,支持了更多复杂的应用场景 | +| 应用程序的地址空间与内核地址空间分离,导致 Trap 处理变得复杂 | 内核和应用程序运行在不同的地址空间中,这使得 Trap 上下文的处理需要特别的机制来确保正确性 | 通过在 `task_control_block` 中为每个应用分配独立的内核栈,并在 `trap_handler` 中动态获取当前应用的 Trap 上下文 | 实现了更灵活的任务管理和更安全的 Trap 处理机制,每个应用都能在自己的地址空间中独立运行 | +| 应用程序的 Trap 上下文不在内核地址空间中,无法直接访问 | 应用程序的 Trap 上下文存储在用户空间,而 Trap 处理发生在内核空间 | 使用 `current_trap_cx` 函数获取当前应用的 Trap 上下文的可变引用 | 保证了 Trap 处理的高效性和安全性,同时简化了代码逻辑 | +| 从 S 模式到 S 模式的 Trap 没有合适的处理方式 | 当前系统设计主要关注 U 模式到 S 模式的 Trap,对于 S 模式到 S 模式的 Trap 缺乏考虑 | 在 `trap_handler` 开头调用 `set_kernel_trap_entry` 函数,将 `stvec` 设置为 `trap_from_kernel` 的地址 | 对于 S 模式到 S 模式的 Trap,采取直接 panic 的策略,避免了复杂的上下文保存和恢复过程 | +| 应用程序在从 Trap 返回用户态时需要正确设置 `stvec` | 为了确保应用程序能够正确地 Trap 回到内核,需要在返回用户态前设置正确的 `stvec` 值 | 在 `trap_return` 函数中调用 `set_user_trap_entry` 函数,将 `stvec` 设置为 `TRAMPOLINE` 地址 | 保证了应用程序能够通过 `__alltraps` 正确地 Trap 回到内核,提高了系统的健壮性 | +| 指令缓存可能包含已过时的代码或数据,影响应用程序的正确执行 | 内核中的一些操作可能导致物理页帧内容的变化,而指令缓存在这些变化后可能仍然保留旧的快照 | 在 `trap_return` 函数中使用 `fence.i` 指令清空指令缓存 | 确保了每次从 Trap 返回用户态时,应用程序能够基于最新的代码和数据执行,增强了系统的稳定性和可靠性 | +| 内核无法直接访问应用空间的数据 | 内核与应用地址空间隔离导致直接访问失败 | 使用 `translated_byte_buffer` 函数将应用空间的缓冲区转换为内核可直接访问的形式 | 实现了安全有效的数据访问,避免了内存访问违规 | +| `sys_write` 需要支持不同文件描述符 | 当前实现仅支持标准输出,限制了功能扩展性 | 在 `sys_write` 中增加对其他文件描述符的支持,例如通过查找文件描述符对应的文件对象来确定写入目标 | 扩展了系统调用的功能,使其能适应更多使用场景,如文件写入等 | +| 字节数组切片转字符串可能失败 | 非UTF-8编码的字节序列可能导致转换错误 | 使用 `.unwrap()` 强制转换(虽然不推荐,但在本例中假设输入总是合法的),或更安全地处理转换错误 | 确保了输出的正确性和程序的健壮性,即使遇到非UTF-8编码的数据也能优雅地处理 | + + +#### 练习解题思路 +- 实现 `mmap` + - 根据文档可以描述应用态的虚拟内存管理主要是由 `memory_set`来完成 + - 由此可以直接将在 `TaskControlBlock` 中保存的 `memory_set` 对象进行修改,添加一个 `TaskControlBlock::mmap` 方法 + - 注意需要检查对齐到页,权限校验,与冲突校验 +- 实现 `munmap` + - 和`mmap`类似实现类似,只需要修改 `TaskControlBlock` 中的 `memory_set`对象 + - 注意清除前需要检查虚拟页是否有效 +- 迁移 `sys_get_time` + - 在之前 `sys_get_time` 在用户态和内核态都在同一个大栈内,得到结果后可以直接写入用户传递进来的指针哪里 + - 而现在由于内核和用户的地址空间被用跳板分离,并且每一个应用都使用虚拟内存,传入函数的也是虚拟内存地址,因此需要将虚拟地址转换为物理地址,再写入用户空间 + - 使用 `translated_byte_buffer` 实现一个 `copy_to_user` 函数用作用户态和内核态的转换 +- 迁移 `sys_get_task_info` + - 和`sys_get_time`一样,需要将虚拟地址转换为物理地址,再写入用户空间 + +---- + +### ch5 + + +#### 问题与对应解决操作如下: + +>本章深入探讨了操作系统中进程及进程管理的核心概念,包括关键系统调用(如fork, exec, waitpid)、进程管理的数据结构、进程调度机制以及进程资源回收等,并通过编程作业加深理解。 + + +| 问题 | 原因 | 解决方案 | 最终效果 | +|----------------------------------|------------------------------------------------|-------------------------------------------------------------------------------------|----------------------------------------------------| +| 子进程如何知道自己是新创建的? | 当使用 `fork()` 系统调用创建子进程时,子进程需要知道自己的身份以便执行相应的操作。 | `fork()` 在子进程中返回 0,而在父进程中返回子进程的 PID。 | 子进程可以根据返回值 0 来识别自己是新创建的,而父进程可以使用返回的 PID 来跟踪和管理子进程。 | +| 如何让子进程执行新的程序? | 子进程创建后,默认只是父进程的一个副本,可能需要执行完全不同的程序。 | 使用 `exec()` 系统调用,它可以替换当前进程的地址空间,加载并运行指定的新程序。 | 子进程能够执行全新的程序,实现了进程间的程序切换。 | +| 父进程如何得知子进程的状态变化? | 父进程需要知道子进程何时结束或停止,以便进行资源回收或采取其他行动。 | 通过 `waitpid()` 系统调用,父进程可以等待某个特定的子进程或任何子进程结束,并获取其退出状态。 | 父进程能够有效地管理子进程的生命周期,确保系统资源得到合理利用。 | +| 用户如何与 shell 交互输入命令? | Shell 程序需要能够接收用户的键盘输入,以解析和执行命令。 | 使用 `sys_read()` 系统调用来从标准输入读取用户输入,通常通过封装函数如 `getchar()` 来简化操作。 | 用户可以通过键盘与 shell 交互,输入命令来启动程序或执行其他操作。 | +| 如何优雅地处理用户输入的退格和删除操作? | 用户在输入命令时可能会犯错,需要能够方便地修改已输入的内容。 | Shell 程序检测到退格或删除键时,清除屏幕上的最后一个字符,并从内存中的输入缓存中移除该字符。 | 提供了更好的用户体验,让用户能够更加灵活地编辑他们的命令行输入。 | +| 应用程序只能通过编号加载 | 开发者希望根据应用名称而不是编号来加载应用程序,以提高灵活性和用户体验 | 在链接器脚本中添加应用名称,并在加载器中实现通过名称查找ELF数据的功能 | 用户可以通过应用名称启动应用,提高了系统的易用性和灵活性 | +| 进程标识符分配不高效 | 随着系统中进程数量的增加,简单的线性分配方式可能导致PID耗尽或浪费 | 引入了PidAllocator类,采用栈式分配策略,支持PID的回收和重用 | 系统能够更高效地管理和复用进程标识符,避免了资源浪费 | +| 内核栈管理缺乏自动化机制 | 手动管理内核栈容易出错,且增加了内核开发的复杂度 | 使用RAII模式,通过Drop特性自动管理内核栈的生命周期 | 内核栈的管理变得更加安全可靠,减少了开发者的工作负担 | +| 任务控制块的设计不满足多任务管理的需求 | 原有的任务控制块设计过于简单,无法支持复杂的进程管理功能 | 扩展任务控制块,引入父子进程关系管理,增强进程间通信能力 | 系统能够更好地支持多任务处理,增强了进程间的交互性和稳定性 | +| 任务管理器职责过重 | 任务管理器不仅负责任务调度,还承担了CPU状态的监控,导致模块间耦合度过高 | 将CPU监控功能分离至Processor结构中,专注于任务队列的管理 | 任务管理器变得更加轻量化,模块间的职责划分更加清晰,提高了系统的可维护性和扩展性 | +| 无法准确追踪当前执行的任务 | 在多任务环境中,内核需要随时知道当前哪个任务正在执行,以便进行有效的调度和管理 | 引入 `Processor` 结构,包含 `current` 字段来跟踪当前执行的任务 | 内核能够实时了解当前执行的任务,提高了任务调度的效率和准确性 | +| 缺乏有效的任务切换机制 | 当前任务需要交出CPU使用权时,缺少一种平滑过渡到下一个任务的方法 | 实现 `schedule` 函数,通过 `__switch` 进行任务上下文切换,确保任务间的平滑过渡 | 任务切换更加流畅,减少了上下文切换带来的性能损失 | +| 缺少空闲控制流来处理无任务执行的情况 | 当所有任务都处于阻塞状态时,CPU需要有一个默认的行为来避免空转 | 在 `Processor` 中引入 `idle_task_cx`,并在 `run_tasks` 中实现空闲控制流 | CPU在无任务执行时不会空转,提高了系统的整体效率和稳定性 | +| 初始进程无法启动 | 没有正确的进程控制块 | 使用 `lazy_static` 在运行时动态创建 `INITPROC` 控制块,并通过 `add_task` 将其加入任务管理器 | 成功创建并启动了第一个进程,为后续进程管理打下了基础 | +| 进程调度不准确 | 缺少有效的调度机制 | 实现 `suspend_current_and_run_next` 函数,用于暂停当前任务并选择下一个任务执行 | 系统能够根据需要合理地调度进程,提高了系统的响应性和效率 | +| 子进程地址空间与父进程不同步 | 直接复制父进程的地址空间可能导致数据不一致 | 使用 `MemorySet::from_existed_user` 方法深拷贝父进程的地址空间,确保子进程拥有独立但相同的数据副本 | 子进程能够拥有与父进程相同的应用环境,支持真正的 fork 功能 | +| 父子进程无法正确区分 | 系统调用返回值相同导致混淆 | 修改子进程的 `trap_cx.x[10]` 为 0,表示子进程的 PID,而父进程则返回新创建的子进程的 PID | 确保每次调用 `sys_fork` 时,父进程和子进程都能正确识别自己的身份 | +| 进程退出后资源未回收 | 系统未能及时回收已退出进程的资源 | 实现 `sys_exit` 和 `sys_waitpid` 系统调用,允许父进程等待并收集子进程的退出信息 | 系统资源得到有效管理和利用,避免了僵尸进程的产生 | +| 用户程序如何执行新的程序? | 用户程序想要执行另一个程序时,需要一个方法来加载新程序并开始执行。 | 通过 `sys_exec` 系统调用,内核接收新程序的路径,加载相应的 ELF 文件,创建新的内存布局,并更新 Trap 上下文以指向新程序的入口点。 | 用户程序能够无缝地加载并执行新程序,就像从未离开过一样。 | +| 如何处理 `sys_exec` 调用后 Trap 上下文的变化? | 在 `sys_exec` 调用后,由于地址空间的变化,原有的 Trap 上下文不再有效。 | 在 `trap_handler` 中,在处理完 `sys_exec` 系统调用后再次调用 `current_trap_cx()` 来获取最新的 Trap 上下文。 | 确保 Trap 上下文始终是最新的,从而保证系统调用的正确执行。 | +| 应用程序如何从标准输入读取数据? | 应用程序需要一种方式来读取用户通过键盘输入的数据。 | 实现 `sys_read` 系统调用,使用 SBI 接口 `console_getchar` 读取单个字符,并将其写入用户提供的缓冲区中。 | 应用程序现在可以接收来自用户的直接输入,增强了交互性。 | +| 当应用程序退出或崩溃时,如何妥善处理? | 应用程序可能会主动退出或因错误而崩溃,需要一个机制来清理资源并确保系统的稳定性。 | 在 `sys_exit` 和 `trap_handler` 中调用 `exit_current_and_run_next` 函数,将进程状态设置为僵尸,并清理其资源。 | 退出的应用程序不会留下未清理的资源,系统保持稳定,资源得到有效管理。 | +| 父进程如何回收已退出子进程的资源? | 父进程可能希望知道其子进程何时退出,并回收子进程的资源。 | 实现 `sys_waitpid` 系统调用,允许父进程检查子进程的状态,并在子进程成为僵尸时回收其资源。 | 父进程可以有效地管理其子进程的生命周期,确保资源及时回收,提高系统效率。 | + + + +至此,我们实现了一个基本的进程调度系统,能够支持进程的创建、调度、销毁等基本功能。 + +#### 练习解题思路 +- 实现 `sys_spawn` + 1. **获取 ELF 文件数据**: + - 从系统调用参数中获取 ELF 文件的数据。这通常是一个指向 ELF 文件数据的指针和数据的长度。 + + 2. **创建新的内存集**: + - 使用 `MemorySet::from_elf` 方法从 ELF 文件数据创建一个新的内存集。这将解析 ELF 文件并设置相应的内存段。 + + 3. **分配 PID 和内核栈**: + - 为新进程分配一个新的 PID。 + - 分配一个新的内核栈,并获取其顶部地址。 + + 4. **初始化任务控制块 (TCB)**: + - 创建一个新的 `TaskControlBlock` 结构体,初始化其各个字段,包括内存集、陷阱上下文、任务上下文等。 + - 设置陷阱上下文的内核栈指针和用户栈指针。 + - 设置新进程的入口点。 + + 5. **添加子进程到父进程的子进程列表**: + - 将新创建的子进程添加到父进程的子进程列表中。 + + 6. **返回子进程的 PID**: + - 返回新创建的子进程的 PID 给调用者。 + + +- 实现 `sys_set_priority` 并且实现一个优先级调度算法,例如 `Stride` 调度算法 + - 实现 `sys_set_priority` + + 1. **系统调用接口**: + - 定义 `sys_set_priority` 系统调用,接收两个参数:进程的 PID 和新的优先级值。 + + 2. **查找目标进程**: + - 根据传入的 PID 查找对应的 `TaskControlBlock`(TCB)实例。 + + 3. **更新优先级**: + - 更新找到的 TCB 实例中的优先级字段。 + + 4. **更新 Stride 值**: + - 调用 `update_stride` 方法更新该 TCB 的 Stride 值,以反映新的优先级。 + + 5. **返回结果**: + - 如果成功更新优先级,返回 0;否则返回错误码。 + + - 实现 `Stride` 调度算法 + + 1. **定义 Stride 结构**: + - 使用 `Stride` 结构体来表示每个进程的 Stride 值,包含一个 `u64` 类型的字段。 + + 2. **更新 Stride 值**: + - 在 `TaskControlBlock` 中定义 `update_stride` 方法,用于更新 Stride 值。该方法使用公式 `(self.stride.0 + INIT_STRIDE * self.priority as u64) % (BIG_STRIDE + 1)` 来计算新的 Stride 值,防止溢出。 + + 3. **实现比较器**: + - 为 `Stride` 结构体实现 `Ord`、`Eq`、`PartialOrd` 和 `PartialEq` trait,以便在调度器中进行排序和比较。 + - 比较器的逻辑是:计算两个 Stride 值的差值,并根据差值的大小决定顺序。这样可以确保 Stride 值在循环范围内正确比较。 + + 4. **任务管理器**: + - 在任务管理器中维护一个优先级队列(如 `BinaryHeap`),用于存储所有就绪态的 TCB。 + - 每次调度时,从优先级队列中取出 Stride 值最小的 TCB 进行执行。 + - 执行完后,更新该 TCB 的 Stride 值,并重新插入优先级队列。 + +- 迁移 `mmap`, `munmap`, `sys_get_time`, `sys_get_task_info` + - 基本都可以无损实现迁移 + +---- + +### ch6 + +>本章深入探讨了文件系统与I/O重定向的概念,通过介绍文件描述符、文件I/O操作、简易文件系统easy-fs的设计与实现,以及文件系统在内核中的集成,旨在加深读者对操作系统文件处理机制的理解。 + +#### 问题与对应解决操作如下: + + +| 问题 | 原因 | 解决方案 | 最终效果 | +|------------------------------------|-----------------------------------------------------|----------------------------------------------------------------------------------------|--------------------------------------------------| +| 标准输入输出接口不够通用 | 初始实现仅针对标准输入输出,限制了程序的灵活性 | 为标准输入输出实现 `File` Trait,使它们能够像普通文件一样被处理 | 程序可以更灵活地处理各种输入输出流,不再局限于标准输入输出 | +| 文件描述符表固定大小限制了文件数量 | 使用固定大小的文件描述符表,无法适应进程需求的变化 | 引入动态长度的 `Vec` 来存储文件描述符,移除了文件数量的硬性限制 | 进程可以根据实际需要打开任意数量的文件,提升了系统的灵活性和可扩展性 | +| 文件描述符状态管理复杂 | 每个文件描述符都需要单独管理其打开和关闭状态,增加了系统负担 | 使用 `Option` 包装文件描述符,通过 `None` 和 `Some` 区分空闲和占用状态 | 简化了文件描述符的状态管理,降低了实现复杂度,提高了代码的可维护性 | +| 文件共享困难 | 不同进程间难以共享同一文件,限制了多进程协作 | 使用 `Arc` 实现文件的共享引用,允许多个进程同时读写同一文件 | 提升了多进程间文件共享的能力,促进了进程间的协作,增强了系统的交互性 | +| 系统调用仅限于标准输入输出 | 早期的 `sys_read` 和 `sys_write` 系统调用只能用于标准输入输出,限制了功能 | 重构系统调用,使其能够根据文件描述符调用相应的 `File` Trait 方法 | 系统调用变得更加通用,可以应用于任何实现了 `File` Trait 的文件对象 | +| 文件打开失败 | 尝试打开的文件不存在或路径错误 | 使用 `CREATE` 标志尝试打开文件,若文件不存在则创建之 | 成功打开或创建了文件,程序继续执行 | +| 写入数据失败 | 文件描述符无效或磁盘空间不足 | 检查文件描述符是否正确,确保有足够的磁盘空间 | 数据成功写入文件,文件内容更新 | +| 读取数据失败 | 文件为空或读取操作超出文件末尾 | 确认文件中有数据,检查读取长度是否正确 | 正确读取到了文件中的数据,内容显示无误 | +| 关闭文件失败 | 文件描述符已关闭或从未打开 | 在每次操作文件前后检查文件描述符状态 | 文件被正确关闭,资源得以释放 | +| 多次读取时未能正确读取所有数据 | 缓冲区大小不足以一次性读取全部数据,或读取逻辑有误 | 实现循环读取机制,直到读取到文件末尾 | 所有数据都被完整地读取出来,没有遗漏 | +| 文件系统与内核紧密耦合 | 随着内核功能增加,代码量增大,导致文件系统与内核之间的耦合度越来越高,难以单独开发和测试 | 将 easy-fs 分离成独立的 crate,并且通过抽象接口 BlockDevice 连接底层设备驱动 | 实现了文件系统的模块化,提高了代码的可维护性和可测试性 | +| 磁盘IO操作频繁导致性能下降 | 每次读写文件都需要访问磁盘,增加了系统的I/O开销,影响了整体性能 | 引入块缓存层,利用内存作为磁盘的缓存,减少直接磁盘访问 | 显著提高了文件系统的响应速度和整体效率 | +| 缓存替换策略不合理可能导致常用数据被误删 | 当内存中的缓存满了,如果替换策略不当,可能会把频繁使用的数据替换出去,影响性能 | 使用类FIFO的缓存替换算法,优先替换掉最少使用的数据 | 优化了缓存机制,确保了常用数据能够留在内存中,进一步提升了性能 | +| 并发访问同一块缓存可能导致数据不一致 | 在多线程环境中,多个线程同时访问同一块缓存,如果没有适当的同步机制,会导致数据不一致 | 采用 Arc> 结构,提供了共享引用和互斥访问的能力 | 有效防止了并发访问带来的数据竞争问题,保证了数据的一致性和安全性 | +| 缓存数据未及时同步到磁盘可能导致数据丢失 | 如果缓存中的数据被修改后没有及时写回到磁盘,一旦系统崩溃,可能会导致数据丢失 | 在 BlockCache 的 Drop 实现中检查 modified 标记,必要时将数据写回磁盘 | 确保了即使在系统异常退出的情况下,也能尽量减少数据丢失的风险 | +| 文件系统启动时找不到超级块 | 超级块是文件系统的重要组成部分,如果找不到或者损坏了,整个文件系统就无法正常工作 | 设计了一个超级块 `SuperBlock`,包含魔数 `magic` 用于验证文件系统的合法性 | 确保了文件系统启动时可以快速准确地定位超级块,增强了系统的健壮性 | +| 无法高效地管理大量的索引节点和数据块 | 随着文件数量的增加,索引节点和数据块的数量也随之增加,传统的线性查找方式效率低下 | 引入了位图 `Bitmap`,通过位图管理索引节点和数据块的分配状态 | 大幅提升了文件系统管理大量索引节点和数据块的效率,减少了查找时间 | +| 数据块分配过程中可能出现浪费 | 在分配数据块时,如果每次只分配一个块,可能会导致大量的小块分散在磁盘上,造成空间浪费 | 位图 `Bitmap` 通过逐块扫描和按组分配的方式,确保了高效利用磁盘空间 | 减少了磁盘碎片,提高了磁盘空间利用率,使得文件系统更加紧凑 | +| 并发情况下位图操作可能导致数据不一致 | 在多线程或多进程环境下,多个进程同时操作位图可能会导致数据不一致,例如同一个数据块被多次分配 | 使用 `Arc>` 结构,确保了位图操作的原子性和一致性 | 有效防止了并发操作带来的数据竞争问题,保证了文件系统的稳定性和可靠性 | +| 文件读取超出范围时返回0 | 当尝试从文件的某个偏移位置读取数据时,如果该位置已经超过了文件的实际大小,函数将无法读取到任何数据 | 在`read_at`方法中加入边界检查逻辑,确保读取操作不会超出文件的实际大小 | 用户尝试读取不存在的数据时,程序能优雅地处理这种情况,避免了潜在的错误 | +| 目录项名称长度限制 | 为了保证目录项的紧凑性和高效性,每个目录项的文件名长度被限制在27个字符以内 | 通过定义`NAME_LENGTH_LIMIT`常量来控制文件名的最大长度,并在创建目录项时进行长度校验 | 保证了文件系统的稳定性和效率,同时也能适应大多数文件命名的需求 | +| 文件扩容时需要额外分配数据块 | 当文件需要增长时,可能需要额外的数据块来存储新增加的内容 | 实现`increase_size`方法,根据新旧文件大小计算所需的额外数据块数量,并分配这些块 | 文件可以动态增长,用户不必担心文件大小的限制,提高了用户体验 | +| 清空文件时需要回收所有数据块 | 当用户删除文件或清空文件内容时,为了释放磁盘空间,需要回收所有与该文件关联的数据块 | 实现`clear_size`方法,该方法负责追踪并回收所有相关的数据块 | 有效管理了磁盘空间,防止了空间浪费,保持了文件系统的健康状态 | +| 目录项转换为字节序列 | 为了使用`read_at`和`write_at`方法处理目录项,需要将目录项对象转换为字节序列 | 为`DirEntry`结构体实现了`as_bytes`和`as_bytes_mut`方法,允许对象与字节序列之间的转换 | 使得目录项可以方便地被读写,简化了目录操作的实现 | +| 创建 easy-fs 文件系统时,如何确保所有磁盘块都被正确初始化? | 初始状态下,磁盘可能含有旧数据或随机值,这可能导致新创建的文件系统不稳定。 | 在 `create` 方法中,遍历所有磁盘块,并将它们清零。 | 新创建的 easy-fs 文件系统在磁盘上拥有干净的状态,没有残留数据干扰。 | +| 如何从已存在的块设备上加载 easy-fs 文件系统? | 直接读取块设备上的数据无法保证读到的是有效的 easy-fs 文件系统,可能会遇到损坏或不兼容的情况。 | 使用 `open` 方法读取超级块(SuperBlock),验证其有效性后,根据超级块信息构造 EasyFileSystem 实例。 | 成功加载并验证了文件系统,用户可以安全地与之交互。 | +| 如何确定一个 inode 在磁盘上的确切位置? | 文件系统需要快速准确地定位到特定的 inode,以便进行读写等操作。 | 通过 `get_disk_inode_pos` 方法,基于 inode 编号计算出其所在的磁盘块编号及块内的偏移量。 | 提供了一种高效的方法来定位磁盘上的 inode,加速了文件操作。 | +| 文件系统如何管理数据块的分配与回收? | 数据块的有效管理是文件系统性能的关键,错误的分配或未能及时回收会导致空间浪费或文件损坏。 | 实现了 `alloc_data` 和 `dealloc_data` 方法,前者用于分配新的数据块,后者则负责回收不再使用的数据块。 | 确保了数据块资源的合理利用,提高了文件系统的稳定性和效率。 | +| 如何获取根目录的 Inode? | 根目录是文件系统中所有其他文件和目录的起点,获取其 Inode 是进行任何文件操作的前提。 | 通过 `root_inode` 方法,根据已知的根目录 inode 编号(总是0)来创建对应的 Inode 结构。 | 用户能够顺利地从根目录开始浏览和操作文件系统。 | +| 文件索引查找效率低下 | 因为所有的文件都在根目录下,所以在查找文件时需要遍历整个根目录的目录项,导致效率不高 | 实现了一个 `find` 方法,该方法直接在根目录的 `DiskInode` 中根据文件名查找对应的 `inode` 编号,并利用该编号生成新的 `Inode` 对象 | 提高了文件索引的查找速度,用户可以更快地访问到所需文件 | +| 多核环境下文件系统操作冲突 | 当多个核心同时访问文件系统时,可能会出现数据竞争或不一致的问题 | 在所有暴露给用户的文件系统操作中加入了对 `EasyFileSystem` 的互斥锁,确保同一时间只有一个核心可以进行文件系统操作 | 保证了文件系统在多核环境下的稳定性和数据一致性 | +| 文件创建时重复文件检测不足 | 在创建新文件前,如果没有正确检查文件是否已经存在,可能导致重复文件的创建 | 在 `create` 方法中添加了检查逻辑,通过调用 `find_inode_id` 来判断文件是否已存在,如果存在则返回 `None` | 避免了重复文件的创建,提高了文件系统的可靠性 | +| 文件清空时资源回收不彻底 | 清空文件时如果只删除了文件的 `inode` 信息而没有释放其占用的数据块,会导致磁盘空间的浪费 | 在 `clear` 方法中,不仅清除了 `inode` 的大小信息,还回收了之前分配给该文件的所有数据块 | 优化了磁盘空间的使用,避免了不必要的资源浪费 | +| 应用程序打包至文件系统时路径处理不当 | 在将应用程序从主机文件系统复制到 `easy-fs` 时,如果路径处理不当,可能导致文件复制失败或错误 | 使用 `clap` crate 解析命令行参数,明确指定了源文件夹和目标文件夹路径,并在复制文件时进行了正确的路径拼接 | 成功将应用程序打包到 `easy-fs` 中,为后续的应用程序执行提供了便利 | +| 内核如何与块设备通信? | 在操作系统中,直接与硬件通信通常需要特殊的接口,对于块设备也是如此。 | 使用 `virtio-drivers` crate 并在内核中创建一个全局的 `BLOCK_DEVICE` 实例,通过 VirtIO 协议与 QEMU 虚拟化的块设备通信。 | 内核能够高效地读取和写入虚拟硬盘,就像操作真实硬件一样。 | +| 如何在 QEMU 中添加虚拟硬盘? | 默认情况下,QEMU 虚拟机没有附加任何存储设备,需要手动配置。 | 在启动 QEMU 时,通过命令行参数 `-drive` 和 `-device` 添加一个 VirtIO 块设备,并指定其镜像文件和总线连接方式。 | 成功为虚拟机添加了一个可读写的虚拟硬盘,可以用于存放文件系统。 | +| 文件系统如何初始化? | 文件系统需要知道从哪里开始读取数据,以及如何管理文件和目录。 | 在内核启动时,通过 `EasyFileSystem::open` 从块设备打开文件系统,并获取根目录的 `inode`。 | 文件系统准备就绪,可以进行文件的查找、读取和写入等操作。 | +| 如何实现文件的打开功能? | 文件的打开涉及到权限检查、文件是否存在以及是否需要创建新文件等逻辑。 | 定义 `OpenFlags` 枚举来表示不同的打开模式,并实现 `open_file` 函数处理各种情况。 | 用户程序可以通过系统调用 `sys_open` 以不同的模式打开文件,满足多样化的文件操作需求。 | +| 应用程序如何加载和执行? | 程序需要从磁盘加载到内存中,并正确设置进程环境才能执行。 | 修改 `sys_exec` 系统调用,使其从文件系统中读取应用程序的 ELF 数据,并将其加载到新的任务控制块中。 | 应用程序可以从文件系统中加载并成功执行,为用户提供了丰富的应用程序运行环境。 | + + +至此,我们实现了一个简单的文件系统,能够支持文件创建、删除、打开、读取、写入等基本操作。 + + +#### 练习解题思路 + + +- 实现 `linkat` + - 本章中`OsInode`用作权限管理,`Inode`用作操作控制, `DiskInode`则可视为作为内存中的文件镜像,与`BlockCache`一起可以根据需求来选择同步读或者写策略 + - 对于硬链接需要维护一个引用计数,才能决定是否需要删除文件,方便起见直接把引用计数放在`DiskInode`中 + - 此外拥有了引用计数还不足以实现硬链接,因为`DiskInode`中并没有用作身份表示的数据段,所以用户是无法直接分辨引用的哪一个`DiskInode` + - 作为唯一标识符 `inode_id` 在 `Inode` 中被维护,而 `Inode` 会创建 `DiskInode` 所以可以在`DiskInode`的创建阶段中添加一个 `inode_id` 字段,来标识身份 + - 由此便可以实现 `linkat`了,首先获取目标`Inode`,然后将目标`DiskInode`的引用计数加一,同时创建一个指向目标`DiskInode`的 `DirEntry` +- 实现 `unlinkat` + - 获取待删除的`Inode`,将其引用计数减一,若为0则删除,并且同时移除 `DirEntry` +- 实现 `fstat` + - 获取直接对应的`Inode`,返回其元数据 +- 迁移 `Stride`&&`sys_set_priority`, `sys_spawn` , `mmap`, `munmap`, `sys_get_time`, `sys_get_task_info` + - 基本无损迁移 + +---- + +### ch7 + +>本章详细介绍了进程间通信的基本概念、管道的使用方法及其系统调用、命令行参数处理以及标准I/O重定向等核心内容。 + + +#### 问题与对应解决操作如下: + + +| 问题 | 原因 | 解决方案 | 最终效果 | +|--------------------|---------------------------------------------------|---------------------------------------------------------------------------------------------------------|-----------------------------------------| +| 如何创建管道? | 需要在进程间建立一种简单的单向通信机制,以传递数据。 | 使用 `sys_pipe` 系统调用,为当前进程创建一个管道,其中包括一个只读文件描述符和一个只写文件描述符。 | 成功创建了管道,为进程间的通信提供了基础。 | +| 管道如何处理读写操作? | 当数据从一个进程写入管道后,另一个进程需要能够正确读取这些数据,且读写操作不应导致数据丢失或混乱。 | 通过实现 `File` Trait 的 `read` 和 `write` 方法,确保管道能够安全地进行读写操作,同时利用循环队列管理数据流,保证数据的完整性和顺序。 | 管道实现了可靠的进程间数据传输,避免了数据乱序或丢失。 | +| 如何关闭管道? | 当不再需要管道时,应当释放相关资源,避免内存泄漏。 | 使用 `sys_close` 系统调用关闭不再使用的管道端口。当一个管道的所有读端或写端都被关闭后,管道会自动回收资源。 | 管道资源得到了有效管理,避免了不必要的资源浪费。 | +| 如何检测管道的写端是否全部关闭? | 在读取管道时,如果所有写端都已经关闭,应该能够立即得知,而不是无限期等待。 | 在 `PipeRingBuffer` 中实现 `all_write_ends_closed` 方法,通过检查写端的弱引用计数是否能升级为强引用计数来判断所有写端是否已关闭。 | 读端可以及时了解写端的状态,避免无效等待,提高了程序的响应性。 | +| 如何处理管道中的阻塞读取? | 读取管道时,如果当前没有数据可读,读操作可能会阻塞,直到有新的数据写入。 | 在 `Pipe` 的 `read` 方法中,如果当前管道中没有可读数据且所有写端未关闭,则通过 `suspend_current_and_run_next` 暂停当前任务,等待其他任务写入数据后再继续读取。 | 读操作能够正确地等待新数据的到来,而不会浪费CPU资源,提高了系统的整体效率。 | +| 命令行参数无法正确传递给应用 | 在早期版本中,`sys_exec` 系统调用仅接受路径参数,缺少传递命令行参数的机制 | 修改 `sys_exec` 接口,增加一个 `args` 参数来传递命令行参数数组 | 开发者可以轻松地在自己的应用中利用命令行参数,增强了应用的灵活性 | +| 用户栈上的命令行参数格式不正确 | 当命令行参数被压入用户栈时,没有正确处理字符串的终止符 `\0` | 在压入每个命令行参数时,确保在其末尾添加 `\0` 符号 | 应用在启动时能够正确解析命令行参数,避免了因字符串未正确终止导致的问题 | +| 标准输入输出重定向功能缺失 | 应用默认使用标准输入输出,缺乏对文件重定向的支持 | 引入 `sys_dup` 系统调用,允许文件描述符的复制与替换;在 shell 程序中处理 `<` 和 `>` 符号,实现输入输出重定向 | 用户可以通过简单的命令行语法,灵活地将文件作为应用的输入或输出,提升了用户体验 | +| 输入输出重定向时文件描述符管理不当 | 在执行输入输出重定向时,直接关闭标准输入输出文件描述符,可能导致资源泄露 | 使用 `close` 关闭不再需要的文件描述符,使用 `dup` 进行文件描述符的复制,确保资源正确回收 | 文件描述符得到有效管理,避免了资源泄露,提高了系统的稳定性和安全性 | +| 命令行参数在用户态应用中无法正确恢复 | 应用在启动时接收到了命令行参数的个数和指针数组的基地址,但没有有效的方法将其转换为可使用的格式 | 在用户态应用的 `_start` 函数中,根据命令行参数的个数和指针数组的基地址,逐个恢复命令行参数为 `&str` 类型 | 应用开发者可以方便地在主函数中直接使用命令行参数,提高了开发效率 | + +#### 练习解题思路 +- undefined + +---- + + +### ch8 + +>主要探讨了并发程序设计,包括线程的概念、管理、同步互斥机制(如锁、信号量和条件变量),以及相关系统调用的实现,旨在通过理论与实践结合的方式,帮助读者深入理解并掌握多线程编程及其在操作系统层面的支持。 + + +#### 问题与对应解决操作如下: + +| 问题 | 原因 | 解决方案 | 最终效果 | +|-------------------------|-------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|--------------------------------------------------------------| +| 线程创建后的等待机制不够高效 | 当主线程调用 `sys_waittid` 检查子线程是否已经退出时,如果子线程还没有退出,主线程就会不断地尝试检查,这会导致CPU资源的浪费。 | 引入 `yield_()` 函数,在检测到目标线程未退出时让出CPU使用权,等到下一次调度时再进行检查。 | 这样做不仅节省了CPU资源,还提高了系统的整体效率,就像学会了“休息也是战斗力”的道理一样。 | +| 线程与进程的数据结构分离导致的复杂性增加 | 随着线程概念的引入,原本属于进程的一些属性需要被独立出来,形成新的线程控制块,这增加了系统的复杂度。 | 将线程特有的信息封装进 `TaskControlBlock` 结构中,并在 `ProcessControlBlock` 中添加了一个线程控制块向量,用于管理同一个进程下的多个线程。 | 通过这种巧妙的设计,不仅解决了线程管理的问题,还保持了系统的清晰和模块化,就像给混乱的书架找到了整理的方法。 | +| 多线程程序中线程退出后的清理工作 | 在多线程环境中,当某个线程完成任务并退出后,需要有一个机制来清理该线程占用的资源,确保不会造成内存泄漏。 | 在线程控制块中加入了 `exit_code` 字段来记录线程退出的状态,并且在主线程中通过 `waittid` 来回收已退出线程的资源。 | 这个解决方案保证了资源的有效管理和回收,就像给花园定期除草一样,让系统环境更加健康。 | +| 线程调度的公平性和效率 | 在多线程环境下,如何确保各个线程能够公平地获得CPU时间片,同时又不失调度的效率,是一个重要的问题。 | 利用 `Processor` 结构来管理线程的调度,通过合理的算法确保每个线程都有机会执行,同时也考虑到了高优先级线程的需求。 | 这样的调度策略既保证了公平性,也提高了系统的响应速度和吞吐量,就像是找到了团队合作的最佳模式,每个人都能发挥自己的长处。 | +| 如何创建一个新线程? | 需要扩展进程的能力,使其能够同时执行多个任务。 | 使用 `sys_thread_create` 系统调用,创建新的 `TaskControlBlock`,并将其添加到调度队列中。 | 成功地在现有进程中启动了一个新线程,增加了系统的并发处理能力。 | +| 线程退出时如何不影响其他线程? | 非主线程退出时,不应该影响到同一进程内的其他线程。 | 实现 `sys_exit` 系统调用,只回收当前线程的资源,不影响其他线程。如果退出的是主线程,则整个进程及所有线程一起退出。 | 保证了线程间相互独立,提高了系统的稳定性和灵活性。 | +| 如何优雅地等待线程结束? | 主线程可能需要等待某个子线程完成特定的任务。 | 提供 `sys_waittid` 系统调用,允许主线程检查子线程是否已退出,并获取其退出状态。 | 使主线程能够有效地管理子线程,确保任务按预期顺序完成。 | +| 线程之间的资源如何有效管理? | 创建和销毁线程时,需要确保资源的合理分配和回收。 | 在创建线程时分配必要的资源(如栈空间),在线程退出时回收这些资源。 | 资源得到了高效利用,减少了内存泄漏的风险。 | +| 如何实现线程间的特权级切换? | 线程在执行过程中可能需要从用户态切换到内核态,以执行某些特权操作。 | 利用硬件支持的特权级机制,以及在创建线程时准备好的跳板页,确保线程可以安全地进行用户态与内核态的切换。 | 保障了系统的安全性,同时实现了高效的线程执行。 | +| 多线程同时访问共享数据会导致错误结果 | 操作系统在调度时可能会在关键操作中途切换线程,导致数据状态不一致。 | 引入锁机制,确保同一时间只有一个线程可以访问共享数据。 | 保证了共享数据的一致性和完整性,避免了竞态条件。 | +| 线程在执行临界区代码时被中断 | 线程在执行多条汇编指令时,可能被操作系统中断,导致数据不一致。 | 使用锁来保护临界区代码,确保线程在执行完所有相关指令前不会被中断。 | 确保了临界区代码的原子性,避免了数据竞争。 | +| 如何选择合适的锁机制 | 忙等锁和睡眠锁各有优缺点,选择不当会影响性能或公平性。 | 根据应用场景选择合适的锁机制,例如使用睡眠锁减少CPU浪费。 | 提高了系统的整体性能和响应速度,同时保证了公平性。 | +| 如何实现可睡眠锁 | 需要操作系统支持,让等待锁的线程睡眠,释放锁的线程唤醒等待的线程。 | 使用 `mutex_lock` 和 `mutex_unlock` 系统调用来管理锁的状态和等待队列。 | 实现了高效的锁管理,减少了不必要的CPU消耗。 | +| 如何处理多个线程竞争锁的情况 | 当多个线程同时竞争同一个锁时,需要确保公平性和性能。 | 使用等待队列来管理等待锁的线程,确保公平性,并在释放锁时唤醒最早等待的线程。 | 保证了锁的竞争公平性,提高了系统的并发处理能力。 | +| 互斥锁不能处理多个线程同时访问资源的问题 | 互斥锁只能让一个线程独占资源,无法满足多线程并发的需求 | 引入信号量机制,允许设定最大并发线程数N | 实现了更加灵活的资源访问控制,提高了并发效率 | +| 信号量操作需要保证原子性以防止竞态条件 | 在进行信号量的增减操作时,如果不保证操作的原子性,可能会导致数据不一致 | 使用原子指令或互斥锁保护信号量的读写操作 | 确保了信号量操作的安全性,避免了竞态条件的发生 | +| 信号量的初始值设置不当可能导致死锁 | 如果信号量的初始值设得过小,可能会导致线程因为无法获得资源而长时间等待 | 根据实际情况合理设置信号量的初始值 | 避免了不必要的线程等待,减少了死锁的风险 | +| 信号量用于同步时,线程唤醒顺序不确定 | 在多个线程等待同一信号量时,V操作可能随机唤醒其中一个线程,导致执行顺序混乱 | 通过FIFO或其他策略管理等待队列,确保按顺序唤醒线程 | 保证了线程间的有序执行,增强了程序的可预测性和稳定性 | +| 信号量资源未及时释放可能引起资源泄露 | 如果线程在完成任务后忘记释放持有的信号量,会导致其他线程永远无法获取到资源 | 在线程退出前确保调用V操作释放信号量 | 防止了资源泄露,保证了系统资源的有效利用 | +| 线程 second 会忙等检查,浪费处理器时间 | 使用互斥锁和简单的循环检查条件会导致 CPU 时间浪费 | 引入条件变量,让线程 second 在条件不满足时休眠 | 提高了 CPU 利用率,减少了不必要的等待 | +| 线程 second 休眠时持有互斥锁,导致死锁 | 线程 second 在等待条件时没有释放互斥锁,使得其他线程无法获得锁来改变条件 | 在条件变量的 wait 操作中先释放互斥锁,再将线程挂起 | 避免了死锁的发生,确保了线程间正确的同步 | +| 线程之间的唤醒和等待缺乏有效的通信机制 | 单纯的互斥锁和信号量不足以表达复杂的线程间通信需求 | 实现条件变量的 wait 和 signal 操作,提供线程间等待和唤醒的机制 | 增强了线程间的协作能力,使并发编程更加灵活 | +| 条件变量的唤醒操作可能没有线程在等待 | 有时唤醒操作发生在没有线程等待的情况下,这会导致资源浪费 | 优化条件变量的实现,确保只有在有线程等待时才执行唤醒操作 | 提升了系统的响应性和资源利用率 | +| 不同的唤醒行为语义可能导致程序行为不一致 | Hoare、Hansen 和 Mesa 三种语义的选择会影响线程的唤醒和执行顺序 | 选择合适的语义(例如 Mesa 语义)并明确文档化,确保开发者理解行为 | 减少了由于语义不清导致的编程错误,提高了代码的可维护性 | + + +至此,我们又有了一些并发支持 + + +#### 练习解题思路 + +- 实现 `sys_enable_deadlock_detect` + - 检测`Mutex`死锁 + - 死锁发生是由于存在等待环,可以通过检测是否存在等待环来确定是否会发生死锁 + - 而检测环的时候,如果 `Mutex` 为一个节点,那么我们就需要知道它的入读和出度,由此可向 `Mutex` 额外添加`wait_queue`表示入度,`holder`表示出度 + - 如果存在环,那么就存在死锁 + - 检测Semaphore死锁 + - 类似于`Mutex`,同样可以检测是否存在等待环,不过这里`holders`表示出度 +- 无迁移要求XD + + +---- + +# 总结 + +断断续续写了一个月,途中可谓受尽苦难,最后才把所有东西都提交上去,希望自己能坚持下去,不过收获良多,这里感谢提供这么棒的平台,也感谢老师! + +ch3的题目 +![img.png](2024A-OS-II-Summary/img.png) + + +ch8的题目 +![img_1.png](2024A-OS-II-Summary/img_1.png) diff --git a/source/_posts/2024A-OS-II-Summary/img.png b/source/_posts/2024A-OS-II-Summary/img.png new file mode 100644 index 0000000000..2e675c0885 Binary files /dev/null and b/source/_posts/2024A-OS-II-Summary/img.png differ diff --git a/source/_posts/2024A-OS-II-Summary/img_1.png b/source/_posts/2024A-OS-II-Summary/img_1.png new file mode 100644 index 0000000000..3fdf4b7875 Binary files /dev/null and b/source/_posts/2024A-OS-II-Summary/img_1.png differ diff --git a/source/_posts/2024A-rcore-camp-stag2-moyigeek.md b/source/_posts/2024A-rcore-camp-stag2-moyigeek.md new file mode 100644 index 0000000000..71cef29adf --- /dev/null +++ b/source/_posts/2024A-rcore-camp-stag2-moyigeek.md @@ -0,0 +1,61 @@ +--- +layout: npm +title: 2024A-rcore-camp-stag2-moyigeek +date: 2024-11-11 15:18:24 +mathjax: true +tags: + - author:moyigeek + - repo:https://github.com/LearningOS/2024a-rcore-moyihust +--- + +## moyigeek 的2024秋冬rcore训练营阶段二笔记 + + +## lab1 +在TCB中加入start_time和syscall_times字段,并在trap中更新syscall_times,在taskManager的run_first_task和run_next_task中维护start_time字段,最后在syscall中通过get_current_task_control_block实现返回task_info + +## lab2 +使用translated_byte_buffer来实现结构体完整性以完成sys_get_time 和 sys_task_info.然后完成申请内存的检查以及申请的释放。 + + +## lab3 +在本次实验中,我们将当前运行程序的处理从 `taskmanager` 中分离出来,交由 `processor` 处理,并使用修改后的接口完成 `ch3-4` 的系统调用。我们仿照 `exec` 和 `fork` 实现了 `spawn` 流程,并创建了一个新的地址空间。此外,我们在 PCB 中添加了 `priority` 和 `stride` 字段,并在选择时遍历选择 `stride` 最小的进程。 + +在这个实验中,认识了完整的PCB结构,了解了进程的创建和调度,了解了进程的调度算法,了解了进程的调度流程,了解了进程的调度接口。 + +{% asset_img PCB.png PCB %} + +关于其中stride属性的溢出问题 +可以看到由于溢出的出现,进程间stride的理论比较和实际比较结果出现了偏差。我们首先在理论上分析这个问题:令PASS_MAX为当前所有进程里最大的步进值。则我们可以证明如下结论:对每次Stride调度器的调度步骤中,有其最大的步进值STRIDE_MAX和最小的步进值STRIDE_MIN之差: + +$STRIDE_{MAX} – STRIDE_{MIN} <= PASS_{MAX}$ + +在溢出情况下,P.pass_B 可能会变得很小,而 P.pass_A 仍然很大。 +由于模运算的性质,(P.pass_B + MAX_VALUE) - P.pass_A 仍然是一个有效的差值。 +因此,即使发生溢出,STRIDE_{MAX} - STRIDE_{MIN} <= PASS_{MAX} 仍然成立,因为 PASS_{MAX} 是所有进程中最大的步进值。 + + +## lab4 +在easy-fs中添加硬链接的功能,在inode中添加全局的nlink bitmap来保存链接的个数,由此实现sys_stat.通过在easy_fs的功能,在os中实现创建链接和删除链接的系统调用。 + +这章实验的复杂度较高,额外挂载了一个文件系统easy-file-system以及新增了虚拟文件系统的接口。实现这一章的重点在于理解磁盘管理的操作以及文件系统的架构,同时也要理解Rust如何实现以极低的耦合实现虚拟文件系统。 +其中文件系统的结构如图: + +{% asset_img file.jpg file_system %} + +1. 磁盘块设备接口层:以块为单位对磁盘块设备进行读写的 trait 接口 +`BlockDevice` +2. 块缓存层:在内存中缓存磁盘块的数据,避免频繁读写磁盘 +`BlockCache` +3. 磁盘数据结构层:磁盘上的超级块、位图、索引节点、数据块、目录项等核心数据结构和相关处理 +磁盘数据结构层的代码在 layout.rs 和 bitmap.rs 中。 +4. 磁盘块管理器层:合并了上述核心数据结构和磁盘布局所形成的磁盘文件系统数据结构 +5. 索引节点层:管理索引节点,实现了文件创建/文件打开/文件读写等成员函数 + + +## lab5 +实现了死锁检测,创建了 `DeadlockDetect` 结构体,并实现了银行家算法。在系统调用中初始化系统资源和进程资源,实现了资源的申请和释放,并实现了死锁的检测。在是实现中使用渐近的银行家算法在创建资源的时候初始化资源的数量,并在申请的时候检查是否会造成死锁。 + + +## 总结 +在这次的训练营中,我学到了很多关于操作系统的知识,包括进程管理,文件系统,内存管理,死锁检测等等。在这个过程中,我也学到了很多关于Rust的知识,包括Rust的内存管理,Rust的并发模型等等。这次训练营让我对操作系统有了更深的理解,也让我对Rust有了更深的理解。 \ No newline at end of file diff --git a/source/_posts/2024A-rcore-camp-stag2-moyigeek/PCB.png b/source/_posts/2024A-rcore-camp-stag2-moyigeek/PCB.png new file mode 100644 index 0000000000..25607fdcb3 Binary files /dev/null and b/source/_posts/2024A-rcore-camp-stag2-moyigeek/PCB.png differ diff --git a/source/_posts/2024A-rcore-camp-stag2-moyigeek/file.jpg b/source/_posts/2024A-rcore-camp-stag2-moyigeek/file.jpg new file mode 100644 index 0000000000..a1692bfe71 Binary files /dev/null and b/source/_posts/2024A-rcore-camp-stag2-moyigeek/file.jpg differ diff --git a/source/_posts/2024a-rcore-srcres258-stage1.md b/source/_posts/2024a-rcore-srcres258-stage1.md new file mode 100644 index 0000000000..b5085c0213 --- /dev/null +++ b/source/_posts/2024a-rcore-srcres258-stage1.md @@ -0,0 +1,22 @@ +--- +title: 2024 秋冬季开源操作系统训练营 第1阶段(基础阶段 - Rust编程) +date: 2024-11-12 21:54:11 +category: srcres258 +tags: srcres258 +--- + +# 阶段目标与晋级条件 + +- Rust编程语言为学习操作系统设计与实现打下坚实基础 + +- 通过完成100道Rustling编程题,强化训练Rust编程技能 + +- 该阶段排行榜达到满分可晋级,进入专业阶段学习 + +# 阶段个人总结 + +Rust 语言是参加本训练营必须掌握的一门语言,因为本训练营的所有项目均基于本语言编写。由于本人已学习过 Rust 编程语言,故本阶段无太大压力,跟着晋级要求完成 rustlings习题后即达成目标。 + +而重点是后续专业阶段 rCore 操作系统的学习与代码理解,因操作系统相关知识早已忘记(虽然曾跟着网络教程阅读过 Linux 内核源码,但年代久远早已忘记细节),故需要一定时间重拾相关知识,并跟着实验在旧知识基础上提升自己的理解水平。 + +附:本人学习 Rust 语言的主要参考[视频教程](https://www.bilibili.com/video/BV1hp4y1k7SV/)。 \ No newline at end of file diff --git a/source/_posts/2024a-rcore-srcres258-stage2.md b/source/_posts/2024a-rcore-srcres258-stage2.md new file mode 100644 index 0000000000..cb647c47d1 --- /dev/null +++ b/source/_posts/2024a-rcore-srcres258-stage2.md @@ -0,0 +1,20 @@ +--- +title: 2024 秋冬季开源操作系统训练营 第2阶段(专业阶段 - OS设计实现) +date: 2024-11-12 22:04:16 +category: srcres258 +tags: srcres258 +--- + +# 阶段目标与晋级条件 + +- 从零开始构建操作系统的各个模块,不断完善操作系统核心功能 + +- 完成5道rCore操作系统大实验编程题,深入领会OS重要概念,掌握必备技能 + +- 排行榜积分达到500分,方可进入项目阶段学习,参与团队合作完成关键任务 + +# 阶段个人总结 + +从本阶段开始本人感到明显的学习吃力,各种新概念在教学过程中如泉涌般浮现在脑海,难以在短时间内完全消化完毕。 故专门抽出一整个周末以及课余时间反复研读与理解教学文档,深入洞悉各种新概念的内涵,在彻底理解相关知识点后再结合视频直播中老师的讲解理解每一行代码的实现逻辑(5W1H原则:‌Why(何因)‌、‌What(何事)‌、‌Where(何地)‌、‌When(何时)‌、‌Who(何物)‌、‌How(何法))。在确保完全弄懂文档知识点与代码的实现逻辑之后,方才着手完成每个实验的习题。 + +通过本阶段的学习,我也认识到学习操作系统相较于其他编程实践项目需要投入更多的精力,对于陌生的技术栈需要花更多的心思去理解其技术细节与应用方式,不能向以前一样盲目自信而心不在焉地学习。同时这几次实验也建立起我实战大型编程项目的经验,为以后参与更多更繁杂的编程项目打下基础。 \ No newline at end of file diff --git "a/source/_posts/2024a-rcore-\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-cereal.md" "b/source/_posts/2024a-rcore-\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-cereal.md" new file mode 100644 index 0000000000..c613520963 --- /dev/null +++ "b/source/_posts/2024a-rcore-\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-cereal.md" @@ -0,0 +1,14 @@ +--- +title: 2024a-rcore-一阶段总结-cereal +date: 2024-11-10 15:04:04 +tags: +--- + +### 前言 + +基本上从零开始通过rustlings学习rust,rust作为一门编程语言,通过其合理的设计,保证了其内存安全和高性能,非常值得学习。 + +### 个人感悟 + +通过rustlings学习rust的基础语法、数据结构、所有权、引用等相关知识,初步了解了rust程序的编写,在通过之后rcore的学习,通过实践更加加深了对于rust编程的理解,这种学习方式还是非常高效,之后还需要更多项目的累计,不断提高rust编程能力,和对操作系统的理解。 +对于rust来说,最难理解的还是所有权部分的内容,变量所有权之前的转移,引用、Copy、Clone等相关使用的分析,都需要很多代码的累计,才能写起来rust时得心应手。 diff --git "a/source/_posts/2024a-rcore-\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-cereal.md" "b/source/_posts/2024a-rcore-\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-cereal.md" new file mode 100644 index 0000000000..573776b346 --- /dev/null +++ "b/source/_posts/2024a-rcore-\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-cereal.md" @@ -0,0 +1,15 @@ +--- +title: 2024a-rcore-二阶段总结-cereal +date: 2024-11-09 13:52:35 +tags: +--- + +### 前言 + +我自己之前参加过ysyx,有一点操作系统的相关基础知识和riscv指令集基础,~~pa4后面还没做完~~,对于很多操作系统基础概念理解还是不够深刻,通过rcore的学习也是补上了这一部分。操作系统作为最和硬件联系紧密的软件,特权、异常、中断等概念的引入,让cpu具有了安全的连续不停运行各种各样程序的能力,这非常美妙😄,这也是我学习操作系统的目的。 + +### 个人感悟 + +通过近一个月的学习,对于操作系统的相关概念和rust这门编程语言有了更深入的了解,之前对于操作系统也只是停留在很基础的部分,对于操作系统的基础概念的认识也是非常模糊,也没有上过操作系统的相关课程,通过这次的学习,也是对操作系统的基础概念(进程管理,地址空间,文件系统,并发等等)有了基础的理解。 +在rcore的实验课程中,最让我印象深刻的就是虚拟地址部分,由于之前接触的操作系统全部都是nommu类型,直接访问物理地址的概念非常不容易被打破,并且ch4也是一个门槛,需要非常深入的理解rcore的整体代码框架,对虚拟地址的映射,软硬件协同操作,多级页表,地址空间的概念理解了很久,在不断查找资料,不断理解的情况下,才对虚拟地址的概念有了很浅显的理解,把任务完成。 +在完成任务的过程中,其实rcore的整体框架非常完善,作业的部分也就是一些扩展功能,整体还是对rcore的实现的理解,在完成任务时,也是对已有实现进行模仿,理解整体框架,之后调用已有函数或自己根据数据结构实现函数,实现功能。 diff --git "a/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-focksor.md" "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-focksor.md" new file mode 100644 index 0000000000..760599e740 --- /dev/null +++ "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-focksor.md" @@ -0,0 +1,30 @@ +--- +title: 2024年开源操作系统训练营总结-focksor +date: 2024-11-10 14:36:48 +categories: + - 2024秋冬季开源操作系统训练营 +tags: + - author:focksor + - repo:https://github.com/LearningOS/2024a-rcore-focksor +--- + +# 第一阶段 + +做为一个几乎没接触过 Rust 的新手,第一阶段的 Rustlings 给了我很深的印象。通过不断地练习,我了解了 Rust 一些比较基础的概念,并对其所有权概念印象深刻。 + +做为一个常用 C 和 Python 的开发者,我时常会被 C 的内存问题及 Python 的性能问题困扰,通过学习 Rust 和其相关概念,我逐渐意识到,Rust 或许可以兼顾 C 的性能和 Python 的开发速度,给我带来更好的编程体验。 + +在后续的日常开发过程中,我应该会尝试使用 Rust 做为我的首选语言,并在其中逐步更加熟悉这门语言,感受 Rust 的魅力。 + + +# 第二阶段 + +从我开始接触计算机以来,我就不怎么深入了解过操作系统底层,只是在日常学习中耳濡目染地学习了一些相关概念,有个一知半解。 + +还记得当年面试的时候,面试官问我“程序从用户态进入内核态是什么过程”,我当时只能尴尬地说不会。 + +当真正开始学习操作系统的时候,我才发现,操作系统比我想象地更简单,也比我想象中更困难。 + +觉得简单是因为当以前一知半解的知识被层层剥离,通过简单直白的语言袅袅道来的时候,会感觉“原来这个只要这样就可以实现了”。但是只要稍微深入理解,又会被背后的思路震撼,让人耳目一新,而当理解“为什么需要这样做,有什么考虑”的时候,就会感觉非常困难了。 + +通过第二阶段,现在我觉得我已经脱离了一知半解的状态,应该可以说是“新手入门”了,希望可以在后续的学习中,能得到更多的成长,更深刻地感受到操作系统的魅力。 diff --git "a/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil.md" "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil.md" new file mode 100644 index 0000000000..4c5a5101aa --- /dev/null +++ "b/source/_posts/2024\345\271\264\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-winddevil.md" @@ -0,0 +1,78 @@ + +# 阶段一-Rustlings + +这个阶段主要是需要进行rust相关的学习. + +其实早就有接触过rust,但是语法,尤其是所有权和生命周期这块,一直还比较生疏. + +有关于所有权相关的内容主要是看了这个视频:[如何一步一步推导出Rust所有权、借用规则、引用类型以及秒懂生命周期标注_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV1hjh1emEAY/?spm_id_from=333.337.search-card.all.click) + +其中关于rust作为无gc语言,大部分内存都是保存在栈上的,只有少部分是保存在栈里边. + +**'a指出返回的引用与输入参数x、y之间有关联**. + +这个非常非常重要. + +# 阶段二-OS内核实验 + +## ch0-2 + +可以参见我之前复现[rCore-Tutorial-Book-v3 3.6.0-alpha.1 文档](https://rcore-os.cn/rCore-Tutorial-Book-v3/)的笔记,被公开在博客园上. + +[[winddevil的笔记 - 博客园](https://www.cnblogs.com/chenhan-winddevil/p/18292624) + +这次学习主要是从`ch3~ch8`的学习,重点换做了解决通过测例,和一些自己的问题. + +## ch3 + +主要是讲的一个任务切换的流程,有了任务切换之后又通过定时器中断实现了抢占式调度. + +![替代文本|800](https://img2024.cnblogs.com/blog/3071041/202409/3071041-20240914172749474-2031592744.png "可选标题") + +## ch4 + +这一章主要解决的是一个虚拟地址转换为物理地址的过程,说是虚拟地址,我原来以为真的改变了地址,实际上每一次调用资源都还是使用了物理地址的,利用地址空间对所有的需要访问具体物理地址的对象进行操作. + +![替代文本|800](https://img2024.cnblogs.com/blog/3071041/202410/3071041-20241027024955160-396645957.png "可选标题") + +## ch5 + +这一章同样是承接了上一章的知识,讲的主要是一个进程的概念,加入了很多新的结构体,后边我应该会有时间的时候更新一下图片. + +进程中最巧妙的就是使用了`fork`这个复制一个任务的操作,有了进程,那么就可以实现编程的简洁性,倭只需要编写一个小任务,然后再进行组合,而不是调用`fn`,然后自己设计各种分支结构. + +有了进程,相当于把调度的工作委托给了`os`. + +## ch6 + +在上一章的基础上,引入了块和文件系统. + +这一部分的知识学的非常的不牢靠. + +但是让我印象深刻的地方是,这一章基本实现了我对之前学习的时候发现windows是可以直接"点开"应用这个操作的好奇. + +那么应用保存在哪里,为什么我用U盘拷贝了还是可以继续运行. + +之前学习单片机的时候很少想到我可以通过什么东西对"可执行"的东西进行操作. + +通过二进制文件进行加载然后运行的操作属实惊艳到了我. + +## ch7 + +这个和`ch4`更加相关.之前运行`rtos`的时候总是想着,那么这个变量可以直接以全局变量的方式进行传输为什么我要使用各种比如信号量比如邮箱的方式,现在就一目了然了. + +因为地址空间的不同所以进程之间的通信需要通过管道,也就是需要经由操作系统这一层. + +## ch8 + +这一部分让我想起了之前进行单片机编程的时候的`临界段保护`操作,那时候是通过非常暴力的方法关掉了所有的中断以保证这次读取不会出现问题. + +或者使用原子操作保证中断无法打断单一时钟下的操作. + +这里并没有和硬件和中断打交道,而是选用了三种方式,加锁\条件变量\信号量的方式. + +使用银行家算法进行了调度,算法不难,但是调用本身很麻烦,需要在每一次加锁的时候对题中的变量进行操作.并且每一次上锁的时候都需要`detect`,那么对上锁的程序也必须进行改造. + +## 本次体验 + +越到后边越忙,如果有幸进入下一个阶段一定不能好高骛远多线程操作,一定要留足时间给自己. diff --git "a/source/_posts/2024\345\271\264\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-guoraymon.md" "b/source/_posts/2024\345\271\264\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-guoraymon.md" new file mode 100644 index 0000000000..d50aceb86e --- /dev/null +++ "b/source/_posts/2024\345\271\264\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-guoraymon.md" @@ -0,0 +1,20 @@ +--- +title: 2024年秋冬季开源操作系统训练营一、二阶段总结报告-guoraymon +date: 2024-11-11 16:05:18 +categories: + - 2024秋冬季开源操作系统训练营 +tags: + - author:guoraymon + - repo:https://github.com/LearningOS/2024a-rcore-guoraymon +--- + +## 第一阶段 +好几年前就被 Rust 的性能和并发功能吸引,无奈日常没有运用,只能学而时习之。不过 Rusting 还是没有什么难度的,多看看文档,轻松拿下不是问题。 + +## 第二阶段 +第二阶段就需要了解操作系统原理了,各类教科书都停留在原理阶段,学完了也不知道自己学会了没有。 +lab1 比较简单,原理是需要了解特权级和系统调用,实践上懂得在哪里记录数据和返回即可。 +lab2 难度提升不小,原理上需要了解页表机制,多级页表极其抽象,算是一个难点。实践上要注意内核态拿到的是用户态的虚拟地址,要注意转换而不是直接访问。 +lab3 难度不大,原理上就是进程那些事,不算难。实践上因为基础代码都已经提供了,依样画葫芦很容易就能通过。 +lab4 感觉也是不算难的,原理上就是各种数据结构的管理。实践上因为现在的文件系统比较简易,很多可以不过多考虑直接取巧实现。最大的问题是我的机子性能太差,跑全部测试必定超时,我误以为是自己代码问题。 +lab5 感觉是个巨坑,原理上实验说明写的模模糊糊,完全看不懂。实践上也是感觉测例不完善,虽然顺利通过了,但是没把握实现是正确的。 \ No newline at end of file diff --git "a/source/_posts/2024\345\271\264\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-jrk.md" "b/source/_posts/2024\345\271\264\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-jrk.md" new file mode 100644 index 0000000000..9fb429c407 --- /dev/null +++ "b/source/_posts/2024\345\271\264\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-jrk.md" @@ -0,0 +1,36 @@ +--- +title: 2024秋冬季开源操作系统训练营第一、二阶段总结报告-jrk +date: 2024-11-10 12:10:17 +categories: + - 2024秋冬季开源操作系统训练营 +tags: + - author:ayepei + - repo:https://github.com/LearningOS/2024a-rcore-ayepei-1 + +--- + +# 第一阶段:Rustlings + +第一次接触rust,对于里面的借用和引用,智能指针,生命周期等概念接触起来还是有点难的。在第二阶段的rcore便体现出来了,在很多地方对于这些的理解不够,导致花了很多的时间。第一阶段的Rustlings的难度还是不大的,我通过rust圣经来学习rust。后面还是要多花时间对rust进行学习。 + +# 第二阶段:rcore + +## Lab 1 + +lab1的sys_task_info还是不难的,通过写这个对系统调用的理解更深了。 + +## Lab 2 + +lab2重写sys_get_time和sys_task_info,主要是采用虚拟地址后怎样将其转化。sys_mmap可以调用insert_framed_area来进行。在这其中我遇到的问题就是怎么校验合法性,通过不断的调试也便通过了。munmap主要是来删除掉映射区域,也是要判断当前区域是否被映射。 + +## Lab 3 + +lab3是实现sys_spawn和stride调度算法, 这个是比较简单的,更改了调度算法便完成了。 + +## Lab 4 + +lab4实现了硬链接和获取文件信息。这个部分是我觉得最难的部分。因为对文件系统的了解不够,只有通过反复阅读实验指导书和rcore的代码来理解。理解了文件系统的不同层次后也便完成了。 + +## Lab 5 + +lab5是实现死锁检测, 主要是理解死锁检测这个概念,我开始没能理解,导致想了很久都没有思路,最后通过反复看测试用例死锁检测的内容理解了。在写代码的时候对rust的借用不熟悉,导致了很多错误。最后修改后完成了死锁检测。 \ No newline at end of file diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\344\270\223\344\270\232\351\230\266\346\256\265\346\200\273\347\273\223-qstommyshu.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\344\270\223\344\270\232\351\230\266\346\256\265\346\200\273\347\273\223-qstommyshu.md" new file mode 100644 index 0000000000..9f6b845a6c --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\344\270\223\344\270\232\351\230\266\346\256\265\346\200\273\347\273\223-qstommyshu.md" @@ -0,0 +1,22 @@ +--- +title: 2024秋冬季专业阶段总结-qstommyshu +date: 2024-11-10 10:15:22 +tags: + - author: + - repo:<[rcore-os-repo_you_worked_on](https://github.com/LearningOS/2024a-rcore-qstommyshu/tree/main)> +--- + +# 第一阶段 + +我之前就做过rustlings了,前100道题还是比较容易的,但是在专业阶段开始之前再refresh一下知识挺好的。最后10道题我其实很喜欢,之前做rustlings 都是在非常零散的填空,最后十道题实现算法让我真正开始有了我可以用rust 做些什么的感觉(虽然写的时候也挺痛苦就是了,尤其是linked list)。现在回看其实有些地方可以用更好的方式实现,等放假了再去练一练! + +# 第二阶段 + +在rcore的实验课中,我学到了很多OS相关的知识。每一章的实验难度循序渐进,其中ch6, ch4, ch8 是我认为最难的,也是学到了最多东西的部分。 + +这3周走来我收获良多,也理解了很多以前就知道,但是懵懵懂懂的知识。从开始的获取`TaskInfo`中和群友的激烈讨论,到理解计算机系统数据都是一串二进制数据。再到开始理解地址空间,学会实现跨页机制和申请/释放内存。再到实现进程的创建,公平的`Stride`调度算法。再到文件系统,实现了系统文件的数据以及硬链接和删除文件。再到实现了线程级别的死锁检测。现在回看我都理解我这么短时间内怎么学到这么多知识的。 + +说实话我觉得最重要的还是跟群友一起讨论,加深自己对知识的理解。如果是我自己一个人学的话可能刚开始就放弃了。而且,在追求技术的路上见到这么多志同道合的人也是非常开心,在讨论中也学到了很多超越课程本身的东西。 + +当然,训练营时间还是挺紧凑的,做完了实验题目并不代表我真正把所有知识都掌握了,我还需要自己花时间巩固吸收指导书的内容。希望有一天我能总结出一本自己的指导书,把原来指导书中晦涩难懂的部分以更简洁明了的方式写出来帮助未来训练营的同学们。 + diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack.md" new file mode 100644 index 0000000000..33ad5d9ffe --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack.md" @@ -0,0 +1,224 @@ +--- +title: 2024秋冬季开源操作系统训练营-一二阶段总结-Zack +date: 2024-11-11 11:51:17 +categories: + - report +tags: + - author: Zack + - repo: https://github.com/LearningOS/2024a-rcore-lzcers +--- + +# 感想 + +在接触这门课程之前,我正处于第 N 次入门 Rust 的途中,在之前其实就一直有过多次学习 Rust 的经历,我博客上关于 Rust 的学习笔记最早甚至是 18 年的,回顾我最早学习 Rust 的理由是我希望掌握不同的编程语言范式拓展自己的思维模型。 + +> 「学习过程更多的是知识迁移,如果是学习新的编程范式则会稍有难度,这不仅仅是学习新的概念,还需要在思维模式上做出改变,对于编程语言的学习,应当多去学习不同范式的语言,尽管现代编程语言都支持多范式,但各语言擅长的范式还是不一样的。」 + +但前几次入门 Rust 多因为工作没能用上而浅尝辄止,最近一次我是深刻意识到不实践是无法学会的,所以我一边用 Anki 制作学习笔记卡片,一边尝试用 Rust 写一些小工具,又在 Rust 微信群里看到了 Rust 操作系统的课程,这一下死去的记忆突然开始攻击我了,这不正是我想要的实践吗? +回想起大学自己自学过王爽的《汇编语言》还有于渊《一个操作系统的实现》,但止于汇编编写内核引导后输出 Hello World 的阶段,我被开篇的复杂 C 语言应用劝退了,上面两本书的作者和名字我时隔十年居然都能清晰回忆起来,这或许与我当时的热情有关吧,毕竟操作系统是三大浪漫之一。 +于是我立了个 FLAG 想要弥补过去没学好的内容,另外也是为了真正知道自己在做什么,当我拿起自己习以为常且熟练的编程语言和计算机工具进行各种创作的时候,真正发生了什么,学完二阶段或许我不能说掌握了多少底层的细节,但是我有了一种清晰地感知,当我写下的代码在计算机中流动的时候发生了什么。 + +# 控制流 + +由代码编写的指令在编译成汇编代码后会形成一个执行序列,这个序列就是一个控制流,在正常没有中断和异常情况下,整个控制流都由代码生成的汇编控制,这个控制流是**普通控制流。** +但是一旦进行系统调用,程序就会脱离原本的控制流,进入到操作系统内核代码的控制流中了,比如接受 IO 中断响应,处理用户输入等。这种突变的控制流称之**异常控制流**。(处理中断、异常、陷入) +这个过程中伴随着执行环境的变化,以及上下文的切换,在应用程序基于操作系统抽象(进程、线程等)的执行环境中,突然切换到操作系统内核基于硬件的执行环境中,同时上下文发生了变化(执行环境的相关参数)。 +操作系统和应用程序需要协同硬件一起来保存和恢复这些上下文,使得程序能够正常运行。 + +异常控制流的保存和恢复由操作系统和 CPU 负责。(手动编写在栈上保存与恢复寄存器的指令) + +对于函数转移这内控制流转移,由编译器负责。(编译器会自动生成栈上保存与恢复上下文的指令) + +# 异常控制流 + +外设中断(Device Interrput):由外部设备引起的外部 I/O 设备事件。 +异常(Exception):程序发生除零错误,内存访问越界等。 +陷入(Trap):系统调用进入操作系统工作流。 + +三种东西都是一回事,都是应用程序的工作流被中断了,跑去执行别的地方的代码了,不过对三种中断的方式做了命名区分。 + +# 目标平台与目标三元组 + +通常一个 C 程序编译器的工作流程如下: + +1. 源代码 -> 预处理器进行宏展开 +2. 宏展开的源代码 -> 编译器编译生成汇编代码 +3. 汇编程序 -> 汇编器编译为目标机器代码 +4. 目标代码 -> 链接器链接为可执行文件 + +Rust 通过一个三元组来描述软件运行的目标平台,即 CPU、操作系统、运行时库等。 + +`$ rustc --version --verbose + +``` +rustc 1.82.0 (f6e511eec 2024-10-15) +binary: rustc +commit-hash: f6e511eec7342f59a25f7c0534f1dbea00d01b14 +commit-date: 2024-10-15 +host: aarch64-apple-darwin +release: 1.82.0 +LLVM version: 19.1.1 +``` + +从 host 可以看出我们的 CPU 是 aarch64, apple 系统, darwin 运行时。 + +# Rust std 库和 core 库区别? + +std 库依赖操作系统,提供线程等能力,core 库不依赖操作系统。 + +elf 格式分析 +`rust-readobj -h target/riscv64gc-unknown-none-elf/debug/os` + +# 实现第一个裸机程序我们做了什么? + +Rust 编程:剥离标准库,使用 core 库提供的 trait 实现 panic_handle + +## 1. 去掉 Rust 的标准库依赖,使得程序能够编译成功。 + +1. 在 Rust 项目中使用 `#[no_std]` 标识不使用标准库,这样一来我们 `println!()` 也将无法使用需要自己实现。 +2. panic 处理是必须的,但是 panic!宏也标准库实现,因此需要导入 core 库手动实现 `panic_handle` 方法 +3. 但程序需要一个的启动需要一个`_start` 语义项,语言的标准库作为程序的执行环境,在启动前也会做一些初始化工作才会跳转到由用户编写的程序入口处,但此时我们已经没有标准库依赖了,因此`#[no_main]`声明没有通常意义的入口函数。 + +完成上面三项我们就可以编译通过了,但此时编译出来的是一个空壳应用,我们希望执行点什么。 + +## 2. 编译出能对接 Qemu 与 RustSBI 的应用。 + +![](./2024秋冬季开源操作系统训练营-一二阶段总结-Zack/20241101143428.png) + +源代码编译出的来程序字节码可以分为两部分即数据与代码,实际上我们还可以对这两部分进一步划分为更小的单位段(Section),整个这部分就构成了程序内存布局。 + +.bss: Block Started By Symbol 存储未初始化的全局变量和静态变量。 +.data: 已经初始化的全局变量与静态变量。 +.rodata: 存储只读的常量与字符串等。 + +1. Qemu 在加电启动后会从 0x80200000 开始执行,因此我们需要调整编译出的程序的内存布局,这将通过为编译器指定 `linker.ld` 配置文件来实现,但提交给 Qemu 的文件还要剥离元数据才能被 Qemu 正确寻址,这通过 `rust-objcopy --strip-all` 来实现。 + +1. 使用`#[no_mangle]`自定义的入口函数 +1. 编写 linker.ld 定义内存布局 +1. 编写汇编代码配置栈空间布局,设置栈顶指针然后 call 入口函数 + +```asm + # os/src/entry.asm + .section .text.entry + .globl _start + _start: + la sp, boot_stack_top + call rust_main + + .section .bss.stack + .globl boot_stack +boot_stack: + .space 4096 * 16 + .globl boot_stack_top +boot_stack_top: +``` + +## 3. 实现关键系统调用,实现 Println! 宏等。 + +通过系统调用实现退出机制,通过系统调用实现 print 方法以及宏。 + +# 如何实现批处理操作系统? + +批处理操作系统主要解决几个问题: + +1. 加载所有应用程序 +2. 在执行应用程序前初始化,比如用户态栈的初始化,特权级别切换 +3. 处理应用程序的系统调用 +4. 处理应用程序的错误,在出错时执行下一个程序 + +因此首先要实现用户态的程序,剥离元数据转为 bin 格式,以静态绑定的方式载入内存,在使用时以动态加载的方式来加载。 +具体实现方法是通过代码生成汇编码,将应用程序写入数据段,并标识每个程序的起始结束地址。 + +```asm +# 例子 + .section .data + .global app_4_start + .global app_4_end +app_4_start: + .incbin "../user/target/riscv64gc-unknown-none-elf/release/04priv_csr.bin" +app_4_end: +``` + +在内核中,实现应用程序的加载器、实现 Trap 的处理和特权级别切换,以便于在用户态程序进行系统调用时从用户态切换到内核态,并且在这种切换过程中实现上下文的保存和恢复。 + +## CSR 是什么? + +在计算机体系结构中,CSR 通常指的是 "Control and Status Register"(控制和状态寄存器)。这些寄存器用于存储处理器的控制信息和状态信息,允许操作系统和应用程序控制处理器的行为和获取处理器的状态。 + +# 多道任务与分时多任务 + +在这里我们主要为了隔离 CPU 资源的使用,让每个应用在特定时间内都能获得 CPU 的全部使用权,同时也可以主动让出 CPU 的使用。 +为了让内核同时调度多个应用程序的执行,我们需要实现应用程序的加载机制和任务管理系统,实现任务的切换、暂停管理等能力。 + +## 协作式多任务执行 + +通过实现 **yield 系统调用** 我们可以让任务主动让出 CPU 使用权,从而让内核进行任务切换执行别的任务,这种由任务自己让出 CPU 使用权的系统叫做**协作式**操作系统,但是任务下次获得 CPU 使用权的时间与内核调度策略以及其它任务相关,所以缺点也很明显,对于需要及时得到响应的任务而言这样协作式的方式会严重影响使用体验,协作式操作系统适用于所有应用都是可信的情况下。 + +## 分时多任务执行 + +通过时钟中断机制,我们可以实现时间片轮转调度机制,让 CPU 在每个时间片段内周而复始的轮转执行任务,这样可以确保每个任务都能公平的得到 CPU 的使用时间,这种机制叫做**抢占式**调度策略。 + +# 地址空间 - 计算机空间的抽象 + +现在,我们学会了如何将如何将应用运行在裸机上所必要的知识与概念,即特权级别的切换与任务栈的切换,切换过程中伴随的寄存器的存储与恢复,接下来是操作系统最重要的事物,即构造各种抽象,提供环境约束与管理。 +第一要构造的抽象是「**地址空间**」,它给应用程序创造一个拥有全部内存的幻觉,隔离应用以及操作系统之间的内存访问,保证安全。 + +## SV39 多级页表机制 + +RISC-V 中通过将 Satp 寄存器的高 8 位 Mode 设置为 8 则开启 SV39 页表机制,此时 U/S 模式下的地址访问都会被视作 39 位虚拟地址需要经过 MMU 转换为 56 位的物理地址,SV39 即特权模式下的 39 位虚拟地址,即在 SV39 模式下,64 位宽只有低 39 位是有意义的。 + +**地址格式:** +![](./2024秋冬季开源操作系统训练营-一二阶段总结-Zack//Pasted%20image%2020241106141334.png) + +因为我们的页表是 4K 大小,因此需要 12 位来寻址,因此低 12 位为页内偏移,高 27 位则为页码,MMU 的转换就是扎到虚拟页码到物理页的映射,Offset 不变,拼接成 56 位物理地址。 + +**页表格式:** +![](./2024秋冬季开源操作系统训练营-一二阶段总结-Zack/Pasted%20image%2020241106144107.png) + +**页表存储:** +如果按下面线性表的方式存储页表,即知道一个应用页表的 base*addr,对应的虚拟页页表 = base_addr + 8 * 虚拟页码,即可得到页表项从而找到物理地址。 +考虑到虚拟地址有 27 位用于页表索引,因此如果完全存储 2^27 页页表需要 2^27 \_ 8(64 位) = 1GB 的空间,这显然不现实。 + +![](./2024秋冬季开源操作系统训练营-一二阶段总结-Zack/Pasted%20image%2020241106144124.png) + +因此我们需要按需分配的机制,即保存真正有效的虚拟页表。 + +我们可以通过类似前缀树的方式采用三级页表的方式来索引页表项目 PTE,将 39 位虚拟地址的高 27 位分为 3 段,每段 9 位,剩下 12 位为偏移,如下图所示,通过三级索引我们拿到最终的物理页表页码,加上 Offset 就可以得到最终的物理地址。 +每一级页表的大小刚好 2^9 = 512 个 PTE,每个 PTE 大小 8Byte 刚好 4K 一个物理页帧,这样一来,12K 就足以存储页表了。 +![](./2024秋冬季开源操作系统训练营-一二阶段总结-Zack/Pasted%20image%2020241107161803.png) + +## 在地址空间下运行的应用 + +在地址空间下应用的程序与之前最大的区别在于,此时所有地址指令都需要经过多重的转换,期间有 TLB 加速了这个过程。除了之前提到的特权级别切换,我们还要切换相应的地址空间。 +为了这种切换能够顺畅,我们需要构造跳板以及陷入上下文等数据结构,在应用的高位地址存储用户态陷入上下文以及映射陷入内核的跳板代码,这部分代码尽管在用户地址空间,但是 S 态才有的权限。 + +# 进程 - 计算器力量的抽象 + +计算是一种强大力量,但是在操作系统上可能同时运行着多个不同的应用程序,每个应用都可能需要持有计算的权柄,前面提到我们可以让程序自己出让计算权利的协作式多任务模式,也有由操作系统根据时间片切换的抢占式模式。 +如何更好的管理这些任务的计算,我们需要更高阶的抽象,即进程,可以理解为比任务更高阶的概念,它不光包括了正在运行的任务,还包括了他拥有的资源,如地址空间等,下面这张图很好的描述了进程概念和任务的执行过程。 + +![](./2024秋冬季开源操作系统训练营-一二阶段总结-Zack/Pasted%20image%2020241111123056.png) + +# 文件系统 - IO 的抽象 + +关于 Linux 系统有一句非常著名的话,那就是一切接「**文件**」,这个概念如此的强大,以至于可以容纳一切外部设备,仔细想想,因为它只包含了最小的内涵,即「**读、写**」两个操作,只要能读写?那就是文件,包括我们的硬盘。 + +准确的说,文件系统是建立在块设备上的一种抽象,下面这张图描述了块设备的内部布局,类似于页表,不过用于管理索引的结构叫做位图。 +![](./2024秋冬季开源操作系统训练营-一二阶段总结-Zack/Pasted%20image%2020241111123713.png) + +操作系统上的应用不应关注块设备的内部结构,而是需要关心它提供什么能力,如果每次数据的读写都需要从索引一路找到数据块岂不是很麻烦。 +因此我们要为其实现目录结构的管理,这是一个内存中的数据对象,它完成了对块设备布局中存储的真正数据内容的映射。 +![](./2024秋冬季开源操作系统训练营-一二阶段总结-Zack/Pasted%20image%2020241111124043.png) + +# 并发 - 万物迸发 + +## 死锁检测 + +算法过程就是判断某一未结束线程 Finish[i] == false +它的需求是否小于操作系统所能提供的 Need[i, j] <= Work[j] + +如果小于那就执行到完成, 然后更新操作系统能分配的资源 +Work[j] = Work[j] + Allocation[i, j] +Finish[i] = true + +如果 Finish[0..n-1] 都为 true 即所有线程都有足够资源,否则就是不够,有死锁。 diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/20241101143428.png" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/20241101143428.png" new file mode 100644 index 0000000000..55cf063809 Binary files /dev/null and "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/20241101143428.png" differ diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106141334.png" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106141334.png" new file mode 100644 index 0000000000..b5980901d4 Binary files /dev/null and "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106141334.png" differ diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106144107.png" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106144107.png" new file mode 100644 index 0000000000..5431989103 Binary files /dev/null and "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106144107.png" differ diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106144124.png" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106144124.png" new file mode 100644 index 0000000000..0f69653aba Binary files /dev/null and "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241106144124.png" differ diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241107161803.png" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241107161803.png" new file mode 100644 index 0000000000..d00969376b Binary files /dev/null and "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241107161803.png" differ diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111123056.png" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111123056.png" new file mode 100644 index 0000000000..b935b9d13e Binary files /dev/null and "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111123056.png" differ diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111123713.png" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111123713.png" new file mode 100644 index 0000000000..6f59c37d46 Binary files /dev/null and "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111123713.png" differ diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111124043.png" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111124043.png" new file mode 100644 index 0000000000..acfc0c1541 Binary files /dev/null and "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245-\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Zack/Pasted image 20241111124043.png" differ diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\345\255\246\344\271\240\346\200\273\347\273\223\346\212\245\345\221\212-\344\273\243\344\277\212\345\213\203.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\345\255\246\344\271\240\346\200\273\347\273\223\346\212\245\345\221\212-\344\273\243\344\277\212\345\213\203.md" new file mode 100644 index 0000000000..db4072dae9 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\345\255\246\344\271\240\346\200\273\347\273\223\346\212\245\345\221\212-\344\273\243\344\277\212\345\213\203.md" @@ -0,0 +1,28 @@ +--- +title: 2024秋冬季开源操作系统训练营一二阶段学习总结报告-代俊勃 +date: 2024-11-10 21:52:16 +categories: + - report +tags: + - author: BoBoDai +--- + +# 阶段一: rust语言基础 + +身为一名java开发,对rust已经是神往已久,没有垃圾回收,没有STW这种不优雅的东西,我无法给自己一个不学它的理由。 + +rust的生命周期和和所有权等一系列强规范,虽然如同戴着镣铐跳舞,但是只要运用得当就是优美且安全的代码,希望以后可以从事相关的开发工作。 + +# 阶段二:rCore OS设计实现阶段 + +作为本硕都是机械,之后自学跑路java开发的我来说,操作系统之流、这些基础学科,只是停留在面试的八股中。平时自己也想补齐一些短板,但都被无边的业务给推的无限延期。 + +曾经也尝试过自学,但是缺少志同道合的小伙伴,也缺少正确的路径和学习材料,一直不得要领。 + +机缘巧合我遇见了咱开源操作系统社区,很多同学都是在校的学生,让我重回18,课后作业和细致的教程又让我回到了几年前的上学时光。 + +虽然空的时间不是很多,基础知识也很薄弱,回头还要再补习一下计算机组成的知识,但也是很艰难的坚持下来了。 + +现在已经对操作系统彻底祛魅,ch3 - ch8 从畏手畏脚到后来的越来越敢写,对我来说是巨大的进步。 + +现在更期待后续的学习了。 \ No newline at end of file diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\345\255\246\344\271\240\346\200\273\347\273\223\346\212\245\345\221\212-\347\216\213\347\277\212\345\230\211.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\345\255\246\344\271\240\346\200\273\347\273\223\346\212\245\345\221\212-\347\216\213\347\277\212\345\230\211.md" new file mode 100644 index 0000000000..14b59c0e33 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\345\255\246\344\271\240\346\200\273\347\273\223\346\212\245\345\221\212-\347\216\213\347\277\212\345\230\211.md" @@ -0,0 +1,106 @@ +--- +title: abcd1234-rust-os +date: 2024-10-15 18:32:19 +categories: + - rust-stage-1 +tags: + - author:17999824wyj + - repo:https://github.com/LearningOS/2024a-rcore-17999824wyj + - noob + - nickname:abcd1234 + - email:abcd1234dbren@yeah.net +--- + +# Rust OS 一阶段学习总结 + +@FinishTime: 2024-09-19 00:35:51 + +## 个人背景概述 + +我是一名软件工程专业的大三本科生,曾参与过 2023 年秋冬季训练营和 2024 年春夏季训练营。并且在 2024 季春夏训练营中,取得了通过的成绩。 +今年的秋冬季训练营,我想冲一冲优秀,多做一些项目,多为国内操作系统和开源做出贡献。 + +## 今年一阶段学习时间表 + +- 2024-09-18 大约 3.5 小时,重新完成 rustlings 的官方基础习题(前 94 题)。之后,又用了大约 2 小时,完成了后续的习题(95-110)。 + +## 一阶段学习内容概述 + +我按照[“rust 语言圣经”](https://course.rs/about-book.html)上的讲解顺序,复习之前的知识点。 +由于我在那段时间之前,在参加 InfiniTensor 的 AI 训练营,所以 rust 的基础语法等根本没有落下,这使得我在完成 rustlings 的习题时,几乎没有遇到什么问题。 + +只有到后续的习题中,我遇到了一些问题,主要是和 `智能指针` 有关的内容。这些部分我一直不是很理解的。但是因为我的经验,我还是能写出代码的。 + +## 总结 + +在第一阶段的学习中,我巩固了我所掌握的 rust 基础,我更是深深的意识到:`Talk is cheap, show me the code!`。实践是最好的老师! + +# Rust rCore 二阶段学习总结 + +@FinishTime: 2024-10-15 18:44:32 + +## 二阶段学习时间表 + +- 2024-10-08 开始实验,轻车熟路,一天晚上完事了大部分的 ch3。 +- 2024-10-09 凌晨 ch3 debug 完毕,下午进行提交完毕。 +- 2024-10-09 晚上 ch4 提交完毕。 +- 2024-10-11 ch5 提交完毕。 +- 2024-10-13 ch6 提交完毕。 +- 2024-10-15 ch8 提交完毕。撰写报告完毕。 + +## 问题复盘 + +通过上面的学习时间表可以看出,我虽然是第二次做大实验,但仍然每一部分都需要 1 到 2 天时间。虽然不是将大段时间全部投入,但可能也是比较慢了。 +以下是我实验过程中的问题复盘。 + +### ch3: 本地测例初始化问题 + +在 ch3 里,我遇到了两个问题,第一个是本地测例的初始化问题。 + +由于我是第二次参与训练营,所以在实验时,我就直接去看指导书的习题部分,在尝试开始实现时,在 rust-anaylzer 的使用上时发生了错误,插件因为找不到某些依赖库,拒绝提供服务。 + +我去查看了插件的报错信息,发现找不到 `user` 目录。而我在源代码仓库,也确实没看到源代码里有 `user` 目录。后续,我在自行尝试解决无果后,去微信群问了助教。得到的回答是 `看文档`。然后我去重新看了文档,找到了一个下载 `user` 的方法。这个问题是由于我的粗心导致的。 + +### ch3: 时间初始化问题 + +ch3 的第二个问题是,os 时间初始化问题。 + +在我实现的 os 里,在时间初始化,采用的是 `直接在任务加载时便初始化为当前系统时间`。这显然是错误的。但它的报错并不友好。 + +此问题的表现为:在直接指定 `BASE=1` 执行测试用例时,能够通过测试用例。指定 `BASE=0` 时,也能够通过测试。但指定 `BASE=2` 时,竟然不能通过测试!?同时,这种情况下,qemu 会出现“卡死”,让人一直等下去。 + +我通过细细阅读报错,发现 qemu 卡死是因为有一个用户测例 panic 导致的。经过分析的测例执行逻辑为: + +```plaintext +我分析,ch3里,qemu在跑测试用例的时候,qemu的退出机制是,当全部测试用例成功时,才会退出。 + +如果有测例没过,他会接着执行别的测试用例,但最后全部测试用例执行完后,就不会退出了,还没有信息提示。 +``` + +能够定位位置,之后的工作就要简单多了。我发现了问题所在,然后将时间改为 `Option` 类型,以 `None` 来初始化,在为某任务进行 sys_call 计数时进行判断,如果为 `None`,则进行初始化。这样,问题就解决了。 + +后续,我看到交流群里有个同学,和我遇到了一样的问题,于是,我也帮助了他。 + +### ch6: 又一次卡了一小点 + +在 ch6 时,平心而论,要求实现的逻辑并不是很复杂,只是需要层层的传递,最后由“easy-fs”进行实际干活即可。但我再次遇到了一个极为恶心的问题。 + +在春夏季训练营,我曾经遇到了“File is shorter than the first ELf header part”的问题,翻译过来就是,文件比文件描述符还短。我发现,只有 ch5_spawn0,也就是 helloworld 的测例出现了这个问题。 + +而这一次,我又遇到了这个问题。然后,我通过微信群的聊天记录,找到了当时一位热心同学给我的解答,即,此情况可能是由于 rust 的智能优化导致的,只需要把一个位置,由 `[u8; 512]` 改为一个 Vec 即可。 + +还好我留有聊天记录,再次感谢当时的那位同学! + +### ch8: 系统时间系统调用未实现 + +来到了 ch8,我没有看到指导书文档里,写着的 “除 sys_get_time 以外”,所以,我就没有合并我之前的代码。但问题是,在我实现了 ch8 的内容后,进行测例,有两个始终过不去,就是那两个死锁相关的。而且,它没有报错提醒。这使我很懵,是不是我的银行家算法写的有问题? + +在一次又一次地尝试为银行家算法进行 debug 后,我发现,好像我的算法没问题啊。于是,我又一次详细阅读测试代码,发现:这里面有个`sleep(500)`,这是调了哪个 sys_call?我查了,发现是获得系统时间的那个。但我没有合并之前的代码,导致其会进入死循环。由此,我才改好了问题。 + +## 总结 + +第二次来 rCore,感觉熟能生巧了一些,之前很多不太理解的部分,已经慢慢构不成威胁了。果然,实践能够带动理论!但我也因为自己的粗心,导致了一些不必要的麻烦。这是需要我进行反省的。 + +## 展望 + +这一次的三阶段,将会涉及到“组件化”。希望我能尽快完成相关内容,及早进入后续项目内容的学习!希望能够冲一冲优秀! diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\345\255\246\344\271\240\346\200\273\347\273\223\346\212\245\345\221\212-\351\242\234\347\206\231\347\202\206.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\345\255\246\344\271\240\346\200\273\347\273\223\346\212\245\345\221\212-\351\242\234\347\206\231\347\202\206.md" new file mode 100644 index 0000000000..6b195c1896 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\345\255\246\344\271\240\346\200\273\347\273\223\346\212\245\345\221\212-\351\242\234\347\206\231\347\202\206.md" @@ -0,0 +1,46 @@ +--- +title: 2024秋冬季开源操作系统训练营一二阶段学习总结报告-颜熙炆 +date: 2024-11-10 16:47:31 +categories: + - report +tags: + - author: ZIYAN137 +--- + +# 阶段一:rust语言学习阶段 + +作为一名C++程序员,不得不说,rust确实是让人眼前一亮。 + +## 所有权机制 + rust的所有权是rust语言内存安全的重要特性之一。一块内存(或者说一个值)在任一时刻只能有一个所有者,而其他的地方只能持有这块内存的引用。并且在同一时间内,不能有多个可变借用或同时存在可变和不可变借用。生命周期机制还会在编译时检查内存的生命周期,防止野指针的出现,大大地提高了内存安全性 + +## Option + rust的Option类型(一种枚举类型),有Some和None。这与传统意义上的NULL不同,虽然麻烦了点,但是也保证了安全,并且可读性更强。Option类型是一种显式的枚举类型,要求程序员在定义一个可能为空的值时必须使用Option类型。这种方式使得空值情况成为类型系统的一部分,强制程序员在编译时考虑到值可能不存在的情况。相较之下,Null带来的麻烦反倒会比使用它的便利来说更大一点。 + + +# 阶段二:rCore OS设计实现阶段 + +## lab1 + + lab1实现了get_time和get_task_info。粗略地了解了一遍现有的这个框架。 + +## lab2 + + lab2实现了mmap和mumap,并且重写了lab1的两个实现。由于页表有关的知识之前已经学过了,所以也是光速完成了(自己写了给v2p,虽然后面发现已经有线程的接口可供调用了,难蚌)。 + +## lab3 + + lab3实现了spawn和stride调度算法(其实压根没实现调度,本来想着先过了拿分,但是截止到这篇博客写下都还没实现)。spawn通常通过fork+exec实现,在这里就各copy一点出来就行了。 + +## lab4 + + lab4实现了linkat、unlinkat、fstat等。当时写完的时候发现fstat一直无法正确的输出nlink,后来才发现是我inode_id算错了。我一直在找一个只存在于虚空之中(bushi)的inode_id。 + +## lab5 + + lab5实现了死锁检测(又见到了我们熟悉的dijkstra大神的身影:指银行家算法)。虽然只是粗略地实现了semaphore的检测,但是确实让我更深入的学习了这个死锁检测算法。 + +# 其他: + +参加这个训练营的时候也才大二,暑假刚看完CSAPP,在看MIT的6.s081,刚看没几节课就听说了这个训练营,不仅很好地巩固了自学时所学到的知识,还学了一门新的,非常优秀的rust语言。非常享受这种解决一个又一个挑战所带来的成就感。 + diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\346\250\212\346\231\272\345\212\233.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\346\250\212\346\231\272\345\212\233.md" new file mode 100644 index 0000000000..7fa9c559cf --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\346\250\212\346\231\272\345\212\233.md" @@ -0,0 +1,27 @@ +--- +title: 2024秋冬季开源操作系统训练营一二阶段总结-樊智力 +date: 2024-11-10 23:08:42 +tags: +--- +# 阶段1 +21年冬季就接触过rust,但是后续没有继续学,直到今年5月份劳动节假期看到rust嵌入式群里活跃的气氛才逐渐入门,这次rustlings做第三次了,这次比之前多了算法题环节。 +一直有人调侃用rust写个链表,这次真就写了一次链表,rust的裸指针乱飞。 +# 阶段2 +## ch1-3 +深刻理解了操作系统的上下文切换,从最开始内核创建用户态程序,到用户态发起系统调用在返回的整个流程。 +## ch4 +工作第二年接触过内存分配的代码一直不是很明白,尤其是在触发中断时,页表是怎么从进程空间切换到内核空间,以前只是粗略的了解Linux会将内核对应的内存映射到进程空间,在这个实验中又学到“双页表这个概念”,比较两种的优劣 +## ch5 +以前对进程创建的了解仅限于fork这个系统调用,对其他进程相关的系统调用知之甚少,这次在参考fork、exec两个系统调用实现spwan过程中,理解了为什么 +## ch6 +这个实验中学习了文件系统在物理存储设备的布局方式,不过,该文件系统较为简单,应该是类似ramdisk的实现,并未考虑磨损均衡,实验中的部分实现也都可以偷懒。 +实验中最难的部分就是文件相关的那么多结构体,部分是方便操作系统进行管理的,部分是真实存在于块设备中的索引结构以及文件组织结构。 +在实验代码中也学到了rust trait对象的使用方式,Linux里一切皆文件的概念有了真切的感受。 +## ch7 +在过去写代码时,对进程间通信没有明确概念,尤其是敲命令行时,并没有想过管道符的实现,操作系统借助文件的概念,通过标准输入输出将管道符的多个程序连接到一起,在流水线处理某一任务时又方便又能充分发挥机器性能。 +## ch8 +到最后六小时才开始做这个,多亏群里好心人指点,才能赶在最后时间完成,不得不承认rust的静态分析真的不错,哪里变红改哪里。 +# 感想 +在毕业两年后有机会学习清华的操作系统课程倍感荣幸,补上了我本科操作系统选修未开课的遗憾,也补上了我工作两年中知识欠缺的地方。 + +阶段三我来了!!! \ No newline at end of file diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-Nowherechan.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-Nowherechan.md" new file mode 100644 index 0000000000..0bdee4f9d0 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-Nowherechan.md" @@ -0,0 +1,30 @@ +--- +title: 2024秋冬季开源操作系统训练营一二阶段总结报告-Nowherechan +date: 2024-11-10 17:11:01 +categories: 2024秋冬季开源操作系统训练营 +tags: + - author: Nowherechan + - repo: https://github.com/LearningOS/2024a-rcore-Nowherechan +--- + +## 第一阶段 + +是我第四次学习 Rust 编程语言,第一次看 course.rs,第二次用 rust 刷 leetcode,第三次刷 rustlings,第四次再刷了这里的 rustlings。每一次学完后总是不知道学到了什么,脑袋空空,但实际上写的时候,体验在逐渐变好。可能还是留下了些什么东西,只是没有察觉到。 + +不知道这次留下的东西能持续多久。 + +Rust 很有特点,枚举变量和模式匹配相得益彰,所有权机制比 std::move 舒服了不少,生命周期让我似懂非懂,trait 很有意思,宏看不明白,并发和异步根本没看。 + +Rustlings 比官网多了很多东西,例如后面的算法题。数据结构那一块,实在找不到什么优雅的方式来实现移动,应该是学的东西太少了;前几个题目对 copy trait 进行了滥用。后面的倒是简单,不涉及什么 Rust 独有的特性,如果会用其他语言写,那么这里就会写。 + +相关资料:course.rs,the book,rust by practice + +## 第二阶段 + +rCore 是一个的小内核,长得像 xv6,基于 riscv ISA 和 RustSBI。 + +前面几章的内容大同小异,主要是熟悉操作系统的基本概念,例如内存分页管理。文件系统章节需要对 easyfs 有基本了解,这一点通过阅读 guide book 可以达到。 + +死锁检测算法很新颖,是 xv6 里面没有的内容,一直以来,只学过算法,没考虑过要怎么把这个东西切实地加进内核里面,现在又多知道了一点点,还是有不少收获。 + +测试用例很弱,可以轻度 hack。 \ No newline at end of file diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-\346\235\216\346\254\243\350\224\223.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-\346\235\216\346\254\243\350\224\223.md" new file mode 100644 index 0000000000..eac8163f8b --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\346\200\273\347\273\223-\346\235\216\346\254\243\350\224\223.md" @@ -0,0 +1,64 @@ +--- +title: 2024年秋冬季开源操作系统训练营-Sylvia +date: 2024-11-10 00:39:53 +categories: + - report +tags: + - author:SylviaSylviaSylvia + - repo:https://github.com/LearningOS/2024a-rcore-SylviaSylviaSylvia + +--- + +### 开篇想先说点废话 +说实话真的挺感慨的,竟然真的顺利的做到了今天,竟然真的得到了500分。 + +我是一个小城市长大的孩子,虽然有幸出生于学历较高的家庭,但毕竟环境、观念等受限,我并没有像大城市的孩子一样有机会从小开始学习计算机。准确的说,我连电脑也几乎没有碰过,因为我家的电脑是对我设了密码的,而我的妈妈也比较反对我使用电子产品。 + +再加上年幼的我鼠目寸光,眼里只有考试成绩,小初高阶段我选择了在每一门不考试的科目,包括计算机课上,写物理数学作业。 +2022年8月,我考入大学。因为性格等原因,我报的志愿全部都是工科专业。虽然我很恐惧计算机,认为自己的基础根本不配学,但作为工科专业里的热门专业,我仍然把它排在了志愿前列。 + +很不巧,我刚好是计算机专业的分数线,但刚好未录取。我去了其他的工科专业。 + +于是,我的大一,也依然保持着高中的习惯,电脑常年放在抽屉里,认为拿出来占地方。如果在学习那基本就是做数学物理题。 + +变化发生于我转专业进入计算机大类。其实也许也不算吧,我是网安专业的,在很多学校可能也不算计算机。选择的原因呢,一个是我学校这个专业保研率真的很高,另一个是它看起来没有计科那么需要优秀的代码编写能力。我的必修课程有408四门,有编译原理等计算机常见课程,却连java和python都没有正经学过。不过那都不重要。 + +转入计算机大类,我就顺理成章的进入了计算机大类的学生群。在和各种计算机类专业的优秀同学的相处中,我竟慢慢的发现我爱上了计算机。 + +我开始尝试自学,并想要跨专业考研。选好目标院校后,我进入考研群了解相关消息,并在群里看到了训练营的信息。 + +这就是我与训练营相遇的开端。 + +当然,那不是2024秋冬季训练营。是春夏季训练营。 + +### 2024春夏季训练营,败给了自卑与拖延 +前面也提到了,我的计算机基础非常差,大二之前可以说是彻彻底底的零基础。大二刚转专业,又总抱着一种我大一学的c++比他们简单,又没学过好几门专业课的先修课,更加放纵着自己选择逃避。参加训练营时,又以自己年纪尚小、也没怎么学过(刚好是大二第二学期学,当时正在学)操作系统为由纵容自己拖延,直到时间不够为止。第一阶段内容不多,勉强完成。第二阶段,一题都没来得及做。 +我的第一次挑战,就这样结束了。 + +### 2024秋冬季,我又来了 +明年要开始复习考研初试,不会再有时间。我知道,这是我的最后一次机会了。说实话我自身实力并没强多少,但是没有退路的我选择了尽最大可能试一试。这一次,我希望我也可以顺利做到四阶段结束。 + +### 关于训练营本身,我的学习与解题过程 +进入正题。 +* 第零章、ch1、ch2: + 主要是下载了一些东西,对二阶段有了一点初步的了解,学习了git的分支是什么【终于意识到二阶段仓库不是只有一个.md文件】 + +* ch3: + 第一次在内核里写代码,说实话抵触心理强得可怕,依然是还没开始就开始暗示自己:我肯定写不出来的,我肯定看不懂的。 + 感谢我没有退路,硬着头皮也得写。历时三天,终于在一个周一的早晨顺利通过。我的代码应该是有很多冗余处的,但我依然很兴奋。因为这真的是我第一次纯靠自己动脑子想、去尝试、去不断地修改bug,写出来的代码。 + 以及第一次学会了git如何提交到一个分支上。 + +* ch4:虚存! + 可能也不一定是五个lab里最难的一个,但却是我记忆最深的一个。虚存是我上学期操作系统学的最差的地方,多级页表的题我几乎做一道错一道,我很恐惧这里。看书,尝试,发现自己还是不理解,又看书,又尝试...草稿纸足足用了小半本,一个垃圾桶都差点没塞下hh。开始时最不能理解的是sys_get_time和sys_task_info的改写,我一点也想不明白他们哪里使用了地址。研究了好几天才意识到:ti和ts就是指向虚拟地址的指针,所以要转换成物理地址才能保存信息。 + 以及,终于意识到最初提供的代码里传入的参数前加下划线是未被使用的意思,所以我不需要使用_ti等作为变量,我使用时可以直接删去下划线hhh + +* ch5、ch6、ch8: + 这几个因为本身纯底层理论的东西学的还行,逐渐也开始敢于尝试不妄自菲薄了,其实也没什么特别大的感悟。就是平均三天一章的边研读文档边尝试着写。 + 闭包函数很有意思!但是我还是有点没太摸透,还需要今后再深入学习。 + ch6写sys_fstat时忘了虚存映射,一晚上都在报错time_out,睡前才突然意识到。以后写代码一定要更加注意细节。 + +### 感谢 +真的非常非常非常感谢开源操作系统训练营能给我这么一次机会去参与学习一门新的语言以及内核相关知识。在这个过程中我不仅加深了对操作系统的理解,也逐渐培养起了自己解决问题,不轻言放弃的习惯。我第一次去将一个大的长期任务分隔成小任务,规划什么时候做完哪些,而不是全部拖延到截止日期再痛苦。也是第一次清晰的意识到:哇原来我也可以自己写出较长的代码!也很感谢群友们回答我的一个个过于基础导致对擅长计算机的人大概有点搞笑的问题。谢谢每一位训练营的创办者、负责人、讲师、助教、同学们。 + +### 补充 +补充后的内容为2024.11.11二次提交pr时增加。为向给负责拉取pr的老师带来麻烦致歉,以及感慨这段意想不到的小插曲,增加了这一小段。感谢训练营,春夏季训练营是我第一次使用github,秋冬季训练营是我第一次提交pr。我学到了很多关于git的知识,也因为开始频繁打开github,在github上见到了很多好的项目与知识。我一直很好奇git是怎么工作的,企业中团队不同成员又是如何通过git来合作的,都在训练营使用git的过程中得到了解答。我第一次知道github的不同分支,也第一次知道了提pr是什么样的一个流程。也是第一次知道,git add . 真的不能随意使用,会将自动修改而不应该被修改的文件也修改。git很有意思也很有用,希望我也能有一天真的和一群人一起合作开发一个项目。再次感谢训练营,感谢训练营的老师们! \ No newline at end of file diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-catme0w.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-catme0w.md" new file mode 100644 index 0000000000..488832b453 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-catme0w.md" @@ -0,0 +1,184 @@ +--- +title: 2024秋冬季开源操作系统训练营第一、二阶段总结-catme0w +date: 2024-11-10 20:33:00 +tags: + - author: CatMe0w + - repo: https://github.com/LearningOS/2024a-rcore-CatMe0w +--- + +# 来跟大伙唠会 + +你们好,我是清朝老兵。 + +我是catme0w,你可能已经在排行榜见过我的名字了。 + +去年这个时候,我已经来过一次了。为什么我会再来一次?马上揭晓答案。 + +在继续之前,你可以先看看我上一次留下的记录:[https://rcore-os.cn/blog/2023/11/14/2023开源操作系统训练营第二阶段总结报告-CatMe0w/](https://rcore-os.cn/blog/2023/11/14/2023%E5%BC%80%E6%BA%90%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E8%AE%AD%E7%BB%83%E8%90%A5%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E6%80%BB%E7%BB%93%E6%8A%A5%E5%91%8A-CatMe0w/) + +和上次一样,我的日志里不会有太多关于技术的流水账。如果你想看一个不错的故事,那你来对地方了;如果你还在rCore中挣扎,正在寻找一些攻略,那么我也不会让你白跑一趟,接好了: + +# 写在最前,太长不看攻略:你可能需要的建议 + +1. 强烈建议关闭Copilot或Codeium等代码补全器,它们完全无法理解内核,对于系统编程几乎一无是处;它们产生出的代码会浪费你非常多的时间。 +2. 小黄鸭调试法或许会有帮助。 +3. 在你100%理解代码库之前,你是做不出题目的。相信我。 +4. 完全理解代码库之后,先构思好你准备要做的东西,否则出场即大改。 +5. 做好因方向错误而大改的准备。 +6. 小心#[repr(C)]。 +7. rustsbi-qemu可能非常不稳定,遇到灵异现象?换个QEMU版本试试;如果你在用Apple Silicon,也换台机器试试。 +8. gdb或许很低效,多插trace!()。 + +# 未讲述的往事 + +如果你看了上边我参加去年训练营时的记录,你应该已经了解到,当时,我在ch5就停滞下来然后退出了。我其实内心是有些后悔的,觉得自己当时并没有看起来的那么忙,是自己的懒惰造成不得不退出的结局。 + +纵使rCore从不是什么大事,我还是觉得“抬不起头”,之后也从未和别人再提起这个精致的小操作系统,就好像未曾发生过。 + +我会自己写一个操作系统吗?也许永远不会吧。就当是一场梦。我这么想。 + +### + +次年夏天。 + +在一个非常非常偶然的机会,我了解到我的一位朋友参加了今年春季的训练营。而我们之间最大的不同,便是我自始至终都只抱着“60分万岁”一般的态度,指望着能混到晋级就是胜利,而他从一开始的目标就是榜一。 + +我仿佛挨了一记响亮的耳光:做不到,只是因为不想。 + +### + +所幸,训练营从不禁止清朝老兵(而且现在还越来越多了!),我有机会为rCore重新补上一个完美结局。 + +# rustlings II + +我也是Rust老兵了,这100题,不是把我去年的补丁直接合过来便是? + +然而从今年开始,rustlings多加了十道算法题,成了110题。清朝老兵遇到了它的第一个障碍! + +我了解Rust的脾气,知道什么数据结构得开unsafe,只是我已经很长时间没有摸过算法题了,更没有用过Rust写算法题,一打开,满屏的泛型让我虎躯一震。 + +这需要一点时间……不过,仍然只是小插曲。让我们直面恐惧,走向rCore吧。 + +# rCore II + +实不相瞒,我去年压根没看懂rCore的教程,我的确是一路完成到了ch5,可除了ch3以外,我自己都不知道我写了什么东西,我的代码以一种连我自己都无法理解的方式运行起来,大概纯粹是碰对了测例而已。 + +往好处想,将功补过的机会来了;往坏处想,这相当于一点经验都没有了! + +### ch3 + +没有什么困难,凭借着清朝时期的记忆,我还记得ch3要在哪里加上什么。迅速结束战斗。 + +### ch4 + +我至今都认为,ch4内存管理这一章是整个rCore难度最大的部分,至少在知识上是如此!题目本身反而是不那么难的,代码量不大,理解内存管理的情况下应该很快能找到正确的路;可如果对内存模型有哪怕一丢丢一知半解,你都会在ch4的题目上拥有一段不愉快的时光。 + +嗯,我承认我是后者…… + +### ch5 + +ch5做的是什么来着?我又给忘了。它就是如此没有存在感,前后的ch4和ch6都是令人大叫的程度,相比之下,ch5就有些难以给人留下深刻印象了。 + +哦想起来了是spawn。好像还真没有太多可以说的,在经历过前两章的毒打之后,你应该能很快处理完这一章。 + +### ch6 + +我抵达了我之前没能征服的地方,硬骨头来了。至此,rCore才真正展现出它的威力,BOSS进入二阶段了! + +ch6在知识上稍轻松些,但题目的难度却和教材文本完全不成比例。题目的难点在于非常的绕,为了改动某个功能,你可能需要前后在许多不同的地方修改一连串代码,更不必提这个在内核之外的模块很难调试。 + +到这里,有件事我一定要骂:用Rust编程,有时真的很让人沮丧。 + +人们总是说“编译期拦住错误比事后debug更好”,可是只有自己才知道,用Arc啊,Mutex啊那些东西的时候有多令人不快:它明明就在这里,就是这么直观,但就是不让你用。 + +当天中午11点,我已经几乎完成了ch6的功能实现,我想着,ch6通过就去吃午饭。 + +你能猜到这样立flag将会如何发展。 + +晚上11点的我:😅 + +### ch8 + +就快结束了!最开始看到ch8教程的时候,我心中窃喜:这么简单啊!就好像回到了ch3一样! + +题目要求也很简单,甚至详尽地描述了要实现的每个细节。 + +上机开写,很快就通过了互斥锁的测例,信号量的第一个测例也很快通过了。只剩最后一个测例了,马上就可以结局了! + +然后我就在这“最后一个测例上”卡了十多个小时。🌚 + +要么是第一个测试不过,要么是第二个,两个测例呈现出完全不可理解的结果。可是无论我怎么检查自己的代码,在能插的地方都插满了log,发现每个步骤确实都与预期中的相符,可为什么结果就是不对呢?我也没有理解错题意啊? + +“我也没有理解错题意”……等等。 + +会不会真是我把题目弄错了?我会不会实现了一个错误的算法? + +走投无路之下,我开始直接写这个算法的证明,很快发现,(按我理解的)这个算法根本无法检测任何死锁! + +题目在算法的描述上,确实有些混乱和容易误导人。我意识到我该寻求外部资料了,到此时,我才终于知道这就是Dijkstra的银行家算法。 + +在查看了这个算法的精确描述后,我才终于弄明白我出问题的地方:need矩阵,只减不增,更绝非我最初理解的从0开始增长。 + +终于,所有测例都能通过了,ch8结束了,可是我却有些哭笑不得,因为这么愚蠢和缺乏技术含量的原因卡住,感觉自己有些像是被戏弄了。 + +# rCore EnCore + +感谢你还有耐心看到这里。也许你已经留意到,我在今年的排行榜上是榜一。 + +二阶段classroom放出之后,我使用的是补丁方式提交,因而在补丁里还留有当时的实际完成时间:9月21日。 + +在我完成rustlings之后,也就是今年报名刚开的那个周末,我把整个周末的全部时间都投入到速通rCore上了。 + +不为别的,我就想给去年的自己一个耳光。 + +### 尾声 + +做rCore实验的过程,前半程就是一筹莫展:你看了半天文档,感觉自己好像会了,一打开代码却发现两眼一抹黑,根本不知道在干什么。 + +然后你花了很长时间终于大致摸清楚代码结构了,一准备开始写,发现还是一筹莫展,都不知道要写什么东西。 + +过了不知道多久,终于你有了一个差不多的思路,可以开始写了,此时,你来到了后半程。 + +你可能用了不太长的时间就写出来,也可能很久,但一定是跑不起来的。 + +接下来你会用几十个小时的时间去调试,gdb或者插满trace!()。 + +然而即使用了gdb也没有太大帮助,因为错误往往出现在你想象不到的地方。 + +有的时候你会发现测试用例能过,甚至是随机性可以过,有的时候会发现QEMU直接一动不动——rustsbi-qemu并不稳定。 + +当你第一次发现测试能过的时候,哪怕只是概率性成功,你以为自己离胜利近了,但你很快,或者很久以后才发现,方向完全错了,你必须全部推倒重来。 + +这种事将会反复发生。 + +最后,你离最终的目标越来越近,但每次都总差那么一点,你不禁怀疑自己是否真的在接近终点,还是又一次搞错了方向。 + +到了最后,你可能都记不清这中间到底推翻了多少次,改了多少个细节。你不再期待一蹴而就,而是学会了享受每次解决一个小问题的成就感。 + +不管失败多少次,推倒多少次,你已经比一开始那个两眼一抹黑的自己强太多了。也许方向错了,代码推倒重来,但至少,你知道自己在进步,哪怕每一步都很艰难。最终你会发现,这些反复挣扎的瞬间,才是整个过程里最有价值的部分。 + +### 尾声之后 + +“我会自己写一个操作系统吗?” + +会的。 + +### + +我回想起一件事,是在我很小的时候发生过的,我已经很难分辨究竟是什么时候了。 + +人们在讨论《30天自制操作系统》这本书。 + +当时的我想,啊,这竟然是可能的吗?这本书一定很厚,很难懂…… + +在那时的我眼里,除开最终呈现给用户的应用程序,其余关于计算机的一切都是魔法烟雾构成的黑盒——麻瓜弄不明白。 + +但在这么多年之后,我终于明白,做不到,只是因为不想。 + +我们都可以。 + +### + +训练营只是一个开始,在这之后,还有更广袤的世界等待着探索。 + +https://github.com/CatMe0w/attune diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-silver.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-silver.md" new file mode 100644 index 0000000000..b9130abcc1 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-silver.md" @@ -0,0 +1,21 @@ +--- +title: 2024秋冬季开源操作系统训练营第一、二阶段总结-silver +date: 2024-11-10 19:11:38 +tags: + - author:silver-ymz + - repo:https://github.com/silver-ymz/rcore-blog +--- + +## 第一阶段 + +第一个阶段是通过 Rustlings 练习 Rust 语言的基础知识,包括变量、函数、所有权等等。在这一阶段, 我更加深入地了解了 Rust 的一些基础概念,比如所有权、生命周期等等。在这一阶段的学习中, 我发现通过 debug 的方式来学习一门编程语言和熟悉其语法是非常有效的。这能够让我更深入的去思考一些语言设计的原因,而不是简单地记住一些规则。 + +在学习 Rust 的过程中,我发现 Rust 的所有权机制和生命周期机制是 Rust 的核心特性,也是 Rust 与其他语言最大的不同之处。这也是我认为 Rust 是一门非常有意思的语言的原因之一。所有权机制能够让我将每一个值看作绑定, 而非变量, 这种思维方式让我感觉非常新鲜, 也让我能够更多地将注意力放在程序的逻辑上, 而非内存管理上。生命周期机制则是 Rust 的一种保证, 保证了程序的安全性, 同时也能有效的避免 C/C++ 中的一些内存问题, 比如野指针、内存泄漏等等。 + +在后续的日常开发过程中,我应该会尝试使用 Rust 做为我的首选语言,因为我觉得 Rust 的所有权机制和生命周期机制能够让我写出更加安全、高效的代码。 + +## 第二阶段 + +第二个阶段是通过为 rcore 添加一些简单的 feature 来熟悉操作系统的基本概念和相关实现。在这一阶段,我更加深入地了解了操作系统的一些基本概念,比如中断、虚拟内存、系统调用、文件系统等等。在这一阶段的学习中,我发现通过实践来学习操作系统是非常有效的。这能够让我更加深入地理解操作系统的一些基本概念。对于模糊的概念,我可以直接看到它们的实现,这让我能够更加直观地理解这些概念,也能更深入的思考这些设计的原因。 + +在这一阶段,我的感受是操作系统是一个非常庞大的系统,其中涉及到的知识点和细节非常多,但同时操作系统对各组件的抽象也非常清晰。只要理解了这些抽象,将各组件串联起来也并不是一件非常困难的事情。 diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\350\226\233\345\273\272\346\226\260.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\350\226\233\345\273\272\346\226\260.md" new file mode 100644 index 0000000000..917c8b2de6 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\350\226\233\345\273\272\346\226\260.md" @@ -0,0 +1,29 @@ +--- +title: 2024秋冬季开源操作系统训练营第一、二阶段总结-薛建新 +date: 2024-11-11 08:42:31 +categories: + - 2024春夏季开源操作系统训练营 +tags: + - author:xuejianxinokok + - repo:https://github.com/LearningOS/2024a-rcore-xuejianxinokok +--- + +## + +## 总的收获: + +1. 通过学习操作系统对rust 编程中的借用和生命周期对保存内存安全有了深刻的理解 + +2. 对risc-v 和 arm以及x86 的不同的架构体系有了更深入的理解,理解了ISA 是软硬件接口,深化了接口的理解 + +3. 理解了 操作系统实现中软硬件配合方式,包括机制和策略。尤其是实现虚拟内存时 为了达到硬件不过于复杂而且性能的最大化,硬件和操作系统各有折中 需要紧密配合 + +4. 对于进程和文件的抽象 ,为什么会这样做 以及演变过程 有了更深刻的了解。 + + 从单纯执行任务的job进化到拥有 虚拟空间和状态 且可以动态创建和销毁的进程,并不是一开始就是这样的。 + + 对于文件系统的抽象,我理解了抽象的2个角度.第一个角度是从使用者角度,要越简单越好,第二个角度是从实现者角度,其实现的过程时复杂的,但暴露的接口是简单的,统一的 + +5. 对多线程和协程的实现机制,让我了解到了 对于不同的任务io密集和cpu 密集型,为什么协程更有效。对多线程之间的同步互斥机制有了更加具体深刻的认识,能够分辨自旋锁和基于等待的锁 的应用场景。 + +6. 通过这次学习填补了 概念和实践上的鸿沟,操作系统不再仅仅存在于书本中的理论中。感谢训练营的各位老师!! diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-MawtoA.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-MawtoA.md" new file mode 100644 index 0000000000..5405843bfd --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-MawtoA.md" @@ -0,0 +1,29 @@ +--- +title: 2024秋冬季开源操作系统训练营第一、二阶段总结报告 - MawtoA +date: 2024-11-10 23:38:30 +tags: +--- + +### 第一阶段 + +在参加训练营之前,我已经简单学习了一些 Rust 的相关知识,因此 Rustlings 我非常块的就完成了。在这个阶段的学习中,我复习了一些 Rust 的卖点,像是所有权和生命周期模型;除此之外也有一些新东西,尤其是宏和单元测试相关的内容,在之前的入门学习中我只是匆匆略过,到了现在才发现还有许多我没有关注到的重要的细节。 + +除了对 Rust 的学习,这个阶段对于我熟悉开发工具也提供了很大的帮助。由于学习时基本接触不到现代的项目开发,我对 git 这样的开发工具以及远程仓库、第三方库等等概念都没有了解。通过在设置 Rustling 的过程中反复使用各种命令、配置开发环境,我对接触大型项目也稍微自信起来了。 + +### 第二阶段 + +第二阶段对我就是非常大的挑战了。这次训练营可以说是我第一次接触操作系统底层,中间很多课程、作业也学的很辛苦,不过看着 ci 一片绿油油的 \[PASS\] 还是不由得感觉到一些成就感的。 + +这一阶段的学习我都是按照“看简略版教程 -> 看源代码 -> 参考完整版教程 -> 尝试实现 -> 看群友讨论难点”这样的节奏完成的。中间被别的事情占了一点时间,所以最后变得很仓促,好在是压线过关了,大概我的答案还有很多问题要我回去修改吧。 + +**lab3** 现在看来是其中最基础的一课了,不过刚拿到题目的时候还是让我面对着茫茫代码无从下手。现在想来,这一章最重要的就是推动我大胆行动,不要害怕动已经写好的部分。后面的作业很多需要跨越好几级抽象直接在系统最底层的部分做改动,如果还是按照学校里对着函数填空的思维去做是不可能有办法的。 + +**lab4** 主要关注的是“地址空间”抽象,对于写习惯了应用软件的人来说“解引用指针”突然变成了一个要跨越页面页帧、从好几层字典树中查表的行为,算是一个不小的跨度了,从群友的反馈来看这一篇作业也有一定难度。不过系统已经完成的部分中其实有许多可以类比推广的部分,比如 `translated_byte_buffer` 的实现稍加修改就能够变成“解引用指针”的 `translated_refmut`,像是作业中的其它部分也有类似的部分可以借鉴。 + +**lab5** 的 `sys_spawn` 可以参考 fork 和 exec 两大函数比较轻松的写完,不过 stride 算法就有一些难度了。说实话由于测例无法涵盖算法的所有问题,我在写这一段的时候也是一头雾水,到现在也不知道自己写的有没有问题。 + +**lab6** 是我认为这次作业的另一大难点,文件系统的几个 `Inode` 一开始让我陷入了混乱,不知不觉就把该磁盘上修改的节点修改到抽象节点上了。最后的实现也有一些偷懒的地方,链接没有和文件分开,擦除链接也有点粗暴,不过我想几个关键的思考方向我是没有搞错的。 + +**lab8** 虽然指导我使用银行家算法,但我的实现采用的是维护资源图、尝试解锁时寻找环的思路,好在从最后的结果来看我想应该是没有太大的问题的。 + +最后还是很幸运能通过这一阶段的训练营,感觉这两个月因为训练营过的非常充实,也对自己以后很可能投入的领域有了真正的了解,希望能带着这些收获继续走下去吧。 \ No newline at end of file diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-lm.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-lm.md" new file mode 100644 index 0000000000..a4f6f82f14 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-lm.md" @@ -0,0 +1,9 @@ +## 第一阶段 +参加训练营让我了解到有 rust 这门语言,以前写c/c++习惯了指针的自由操作,刚开始非常不适应,但随着之后的学习越来越爱。 +Rust 兼顾安全与性能,Rust的所有权机制让内存管理变得十分高效且安全, 减少了很多的内存问题, Rust的所有权、借用和生命周期模型可以在编译期防止常见的内存错误(如悬空指针、双重释放等),无需依赖垃圾回收器。Rust 编程思维不同于传统的面向对象,我认为更像是面向数据。 +## 第二阶段 +训练营给了我交流与实践的平台,通过参加此次训练营我学到了很多关于操作系统的知识,也让我了解到了在操作系统领域最新的研究与技术。 +从裸机运行到单道批处理系统,学习了如何通过操作系统将程序与硬件隔离,提高安全性和资源利用率。rCore通过硬件的运行特权级划分和系统调用来实现多程序批处理,将程序编译链接成bin文件,加载到内存中的约定地址执行。进一步地探索了进程调度的机制,理解了时间片和CPU利用率的关系,以及如何通过TCB(任务控制块)来维护程序的关键信息,实现程序的切换和调度。 +在内存管理方面,学习了从物理地址到虚拟地址的转变,以及MMU(内存管理单元)的重要性。rCore采用SV39内存管理策略,通过页机制和多级页表提高了内存的灵活性和安全性。对外存的管理,我了解了文件系统如何抽象和管理系统资源,以及rCore如何通过文件描述符(fd)框架来统一管理文件和设备。 +线程与进程的学习让我认识到了操作系统调度的复杂性。RCore通过线程的引入提高了响应速度和调度效率,同时使用信号量和互斥机制来解决线程间的冲突问题。 +总的来说,RCore的学习不仅加深了我对操作系统的理解,也锻炼了我的系统编程能力。通过实践,我对操作系统的许多概念有了更实际的认识。 diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-rjy.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-rjy.md" new file mode 100644 index 0000000000..9778890eda --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-rjy.md" @@ -0,0 +1,34 @@ +--- +title: 2024秋冬季开源操作系统训练营第一、二阶段总结报告-rjy +date: 2024-11-10 22:41:54 +categories: + - blog +tags: + - author:RDWaaaaaa + - repo:RDWaaaaaa/rust-based-os-camp-blog +--- +## 第一阶段 + +之前自己写过rustlings的一部分,这次是完整地完成了。rust和其他语言的不同之处在于,所有权、借用和引用检查等,在内存管理、安全性、并发性方面都有其优势和特点。 + +## 第二阶段 + +这段时间的操作系统学习让我了解了计算机系统中各个模块的结构和交互过程。从搭建实验环境到实现进程管理、文件系统与并发控制,我逐步掌握了操作系统的核心概念与实现方法。 + +在实验环境配置上,通过熟悉 QEMU 等模拟工具的配置,我能够创建隔离的测试环境,保证了开发过程的安全性和可控性。紧接着,在应用程序与基本执行环境部分,我学习了如何在内存中定位程序的不同部分,了解了程序的加载和执行过程,打下了对操作系统基本管理能力的初步认知。 + +随着课程深入,我逐渐接触到批处理系统和多道程序设计,进一步理解了操作系统资源分配与调度策略。这帮助我认识到系统资源的有限性,以及多任务分配中提高资源利用率的必要性。在此基础上,我学习了进程及地址空间的相关知识,理解了分时多任务的原理,并学会了如何通过调度算法提升系统响应速度和处理效率。 + +在进程管理与进程间通信部分,我逐步掌握了如何创建和管理进程,以及进程如何在系统中相互通信。这些内容让我理解了操作系统在多任务处理中的关键作用,也使我了解到不同进程通信方式的特点和应用场景。 + +最后,通过对文件系统与 I/O 重定向的学习,我了解了数据存储、访问控制与设备交互的基本机制。此外,并发编程的知识让我对线程和同步控制有了更清晰的认识,并学会了如何在多任务环境下避免资源竞争和死锁问题。 + +总体而言,这次学习不仅使我对操作系统的工作原理有了系统化的理解,还培养了我的编程能力和调试技能。操作系统涉及的原理性问题较多,需要不断在实践中加深理解,也让我认识到学习操作系统的深度和广度,为未来进一步的深入学习打下了坚实的基础。 + +### 收获 + +因为之前没有学习过操作系统,每个任务对我来讲都很困难,这次算是入门。 + +### TODO +有很多代码在写lab的时候实际上还没仔细看。 +有些功能可能写的不够漂亮,test偏弱,可能还有一些错误没有被发现。 diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-yukariyuko.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-yukariyuko.md" new file mode 100644 index 0000000000..41f9e02229 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-yukariyuko.md" @@ -0,0 +1,34 @@ +--- + +title: 24秋一二阶段总结---yukariyuko +date: 2024-11-10 16:40:28 +categories: + - report +tags: + - author:yukariyuko + - repo:2024a-rcore-yukariyuko + +--- + +# 学了什么 +## Rustlings + +- 学习了rust语言的基本概念 +- 了解了所有权,不可变性,生命周期,借用等概念 +- 了解RAII的思想(集美u这不是我们c++的RAII吗) + +## rCore + +- 学习了riscv汇编 +- 阅读了rCore源码 +- 实现了一些简单的系统调用 +- 实现了简单的死锁检测 +- ~~被借用检查拷打~~ + +# 收获了什么 + +- 了解了一些OS的基本概念 +- 锻炼了阅读源码的能力 +- 练习了debug与编程 +- 熟悉了git等工具的使用 +- 提高了安全意识 diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-\346\210\264\345\277\227\345\274\272.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-\346\210\264\345\277\227\345\274\272.md" new file mode 100644 index 0000000000..ac4d24b886 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\343\200\201\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-\346\210\264\345\277\227\345\274\272.md" @@ -0,0 +1,776 @@ +# 2024 秋冬季开源操作系统训练营 + +## 导学阶段 + +这个阶段主要是介绍 linux 和 github的基本操作。 + +## 基础阶段-Rust 编程 + +在这个阶段,学员将深入学习 Rust 编程语言的基础知识和高级特性。主要内容包括 Rust 的语法结构、内存管理机制、并发编程和错误处理等。 + + + +笔记: + +# Rust编程 基础知识 + +# 梳理后 + +变量与可变性: + +使用 let 声明变量 + +`let x = 5;` + +默认不可变(一旦赋值,不可修改) + +意义:防止意外的数据修改和并发问题 + +如何可变?使用 mut 关键字 + +`let mut x = 5;` + +常量声明 + +使用 const 而不是 let + +声明值只能为 **常量表达式** + +(即,不可以是只能在程序运行时才能计算出的值) + +`const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;` + +基本数据类型-原生类型(primitives) + +标量类型 + +有符号整数 (signed integers) : i8, i16, i32, i64, i128 和 isize (指针宽度) + +无符号整数 (unsigned integers) : u8 + +浮点数 (floating point): f32, f64 + +char (字符) : 单个 Unicode 字符,如 ‘a’ + +bool (布尔型) : 只能是 true 或 false + +单元类型 (unit type) : ()。 + +复合类型 + +数组 (array) : 如 [1, 2, 3] + +元组 (tuple) : 如 (1, true) + +元组 (tuple) + +把一个或多个其他类型的值组合进一个复合类型 + +长度固定(声明后长度不变) + +`let tup: (i32, f64, u8) = (500, 6.4, 1);` + +如何获取单个值? + +1. 使用点号 (.) 加值的索引 + +2. 使用模式匹配 (pattern matching) 来解构 (destruction) 元组值 + + ```rust + let tup = (500, 6.4, 1); + let (x, y, z) = tup; + println!("The value of y is: {y}"); + + ``` + +数组 (array) + +在栈上分配,单个内存块 + +声明 + +1. 直接声明 + + ```rust + let a = [1, 2, 3, 4, 5]; + + ``` + +2. 指定元素类型和元素数量 + + ```rust + let a: [i32; 5] = [1, 2, 3, 4, 5]; + + ``` + +3. 指定初始值和元素个数 + + ```rust + let a = [3; 5]; + + ``` + +使用 + +使用索引来访问 + +函数 + +声明 + +使用 fn 关键字来声明 + +```rust +fn main() { + println!("Hello, world!"); + another_function(); +} +fn another_function() { + println!("Another function."); +} + +``` + +if 表达式 + +loop 循环 + +循环标签 + +改变 break 或 continue 的作用对象 + +while 循环 + +for 循环 + +所有权概念 + +变量作用域与变量隐藏 + +函数参数的所有权 + +函数返回值 + +引用与借用 + +可变引用 + +String + +String slice + +结构体 + +元组结构体 + +Vector + +定义 + +可变长数组,只能存储相同类型的值 + +使用 + +Vec::new() 可以新建空 vector + +```rust +let v: Vec = Vec::new(); +//这个 Vec 可以不加 + +``` + +vec! 宏,会根据我们提供的值来创建一个新的 vector + +```rust +let v = vec![1, 2, 3]; + +``` + +使用 push 可以向 vector 中增加元素 + +hashmap + +定义 + +HashMap 类型储存了一个键类型为 K 对应一个值类型为 V 的映射 + +其通过一个 哈希函数(hashing function)来实现映射,决定如何将键和值放入内存中 + +使用 + +可以使用 new 来创建 + +使用 insert 增加元素 + +使用 get 方法来从 hashmap 中获取值 + +```rust +use std::collections::HashMap; +let mut scores = HashMap::new(); +scores.insert(String::from("Blue"), 10); +scores.insert(String::from("Yellow"), 50) + +``` + +第二节 + +枚举 enum + +枚举 Option + +定义 + +```rust +enum Option{ + None, + Some(T), +} + +``` + +使用 + +用 unwrap() 获取内部值 + +作用域 scope + +就是作用范围吧 + +同一个作用域不能有两个相同名称的项 → C++ 中的变量名冲突 + +(可以使用一下工具来解决名称冲突???) + +模块系统 the module system + +模块系统可以将 crate 中的代码分组和封装,提高可读性和重用性 + +包 Packages + +Cargo 的一个功能,允许构建、测试和分享 crate + +包是提供一系列功能的一个或者多个 crate , 包含一个 Cargo.toml 文件 + +Crates + +一个模块的树形结构,它形成了库或二进制项目 + +crate 是 Rust 在编译时最小的代码单位,有两种形式:二进制项和库 + +crate root 是一个源文件,是 crate 的根模块 + +crate 的约定 + +src/main.rs 是一个与包同名的二进制 crate 的 crate 根 + +src/lib.rs 是一个与包同名的库 crate 的 crate 根 + +src/bin 目录下的每个文件都会被编译成一个独立的二进制 crate + +模块 Modules 和 use + +允许你控制作用域和路径的私有性 + +use 关键字 + +可以在一个作用域内创建一个项的快捷方式,减少长路径的重复 + +类似于 Java 的导包? + +as 关键字 + +配合 use 使用,就是给快捷方式重命名 + +一些调用项的方式 + +```rust +//例如 xxx 模块的 yyy 子模块下定义了一个 zzz +crate::xxx::yyy::zzz; + +use crate::xxx::yyy::zzz;//之后就可以直接使用 zzz 了 + +use crate::xxx::yyy::zzz as z; + +``` + +私有和公有 + +默认所有内容私有 + +pub 关键字 + +可以将模块或模块内的项标记为公开的 + +模块树 + +crate 根文件 (src/lib.rs 或 src/main.rs) 是 crate 模块结构的根,也是名为 crate 的隐式模块的根 + +在 crate 根文件中,可以声明新的模块 + +使用 mod 关键字和花括号或分号 + +```rust +mod a_module { + //something... +} + +``` + +如何寻找 + +内联(以大括号结尾时) + +在文件 src/xxx.rs + +在文件 src/xxx/mod.rs + +在其它文件中,可以定义子模块 + +使用 mod 关键字和花括号或分号,并在以父模块命名的目录中寻找子模块代码 + +模块树可以用来展示 crate 中的模块层次结构,以及模块之间的父子和兄弟关系 + +路径 path + +一个命名项(结构体、函数或模块等)的方式 + +绝对路径 absolute path + +以 crate 根 (root) 开头的全路径 + +对于外部 crate 的代码,是以 crate 名开头的绝对路径 + +对于当前 crate 的代码,则以字面值 crate 开头 + +相对路径 relative path + +从当前模块开始,以 self, super 或当前模块的标识符开头 + +绝对路径和相对路径都后跟一个或多个由双冒号分割的标识符 + +## **专业阶段- OS设计实现** + + + + + +### 第三章:多道程序与分时多任务系统 + +**ch3 要求我们完成的任务具体要求如下:** + +- 实现系统调用 `sys_task_info`,系统调用号为 410 +- 该系统调用需要返回当前任务的以下信息: + - 任务状态(必定为 Running) + - 任务使用的系统调用及其调用次数 + - 系统调用时刻距离任务第一次被调度时刻的时长(单位ms) +- 使用 `TaskInfo` 结构体来存储这些信息 +- 系统调用成功时返回 0,失败时返回 -1 +- 注意:调用 `sys_task_info` 本身也会被计入系统调用次数 + +**思路:** + +**sys_task_info** 需要三个信息,我们一个一个来, + +**任务状态**:第一个是任务状态,这个实现起来比较简单,甚至因为在这个系统里,查询的是当前任务的状态,因此 TaskStatus 一定是 Running。 + +**系统调用时刻距离任务第一次被调度时刻的时长**:我们需要的这个时长等于 当前时刻减去第一次被调度的时刻,当前时刻很好获取,利用get_time_ms()函数就可以直接获取,而另外一个 则需要为任务控制块添加新的字段 ,用于记录第一次被调度的时长,这样我们就可以通过任务管理器直接地获取某任务初次调用时刻了。 + +**任务使用的系统调用及其调用次数**:我的方法和提示里给出地方法相同 因为系统调用号一定小于 500,所以直接使用一个长为 `MAX_SYSCALL_NUM=500` 的数组做桶计数。同样也直接在 任务控制块中添加 数组,实现把各个任务的系统调用次数数组与该任务绑定。在此之后,我们直接到系统调用前`syscall/mod.rs:syscall` 增加相应任务的相应系统调用类型的调用次数即可。 + +最后我们直接创建一个TaskInfo类型保存如上三种数据,并利用**sys_task_info**中的*mut类型参数,把Taskinfo的内容传回用户态即可完成该功能。 + +总的来说作为第一个任务,用来熟悉内核编程,还算比较简单。 + +[问答题-lab1](%E9%97%AE%E7%AD%94%E9%A2%98-lab1%2013908b3cc7f1805dae96cff7f5eb924d.md) + +### 第四章:地址空间 + +**ch4要求我们完成的具体内容如下:** + +> 重写 sys_get_time 和 sys_task_info +引入虚存机制后,原来内核的 sys_get_time 和 sys_task_info 函数实现就无效了。请你重写这个函数,恢复其正常功能。 + mmap 和 munmap 匿名映射 [mmap](https://man7.org/linux/man-pages/man2/mmap.2.html) 在 Linux 中主要用于在内存中映射文件, 本次实验简化它的功能,仅用于申请内存。 +请实现 mmap 和 munmap 系统调用,mmap 定义如下: `fn** sys_mmap(start: usize, len: usize, port: usize) -> isize` - syscall ID:222 - 申请长度为 len 字节的物理内存(不要求实际物理内存位置,可以随便找一块),将其映射到 start 开始的虚存,内存页属性为 port + **参数:** + start 需要映射的虚存起始地址,要求按页对齐 +len 映射字节长度,可以为 0 +port:第 0 位表示是否可读,第 1 位表示是否可写,第 2 位表示是否可执行。其他位无效且必须为 0 + 返回值:执行成功则返回 0,错误返回 1 +**说明:** 为了简单,目标虚存区间要求按页对齐,len 可直接按页向上取整,不考虑分配失败时的页回收。 + **可能的错误:** start 没有按页大小对齐 - port & !0x7 != 0 (port 其余位必须为0) - port & 0x7 = 0 (这样的内存无意义) - [start, start + len) 中存在已经被映射的页 - 物理内存不足 +> + +**思路:** + +**我们先说sys_get_time和sys task_info 这两个函数的重写:** + +首先我们来说说为什么这两个函数会失效,在ch3中我们的系统调用 利用*mut类型指针来将数据直接地写入用户态中的地址,但是在ch4中,我们将用户态和内核态都分别做了不同的地址映射,这导致我们没办法像ch3那样直接利用_ts来写入数据,因为ch4中传入的是用户态中的虚拟地址,必须借助页表机制,将虚拟地址转换为物理地址,来修改。获取 time 和 TaskInfo的步骤和之前ch3中提到的方式类似。 + +需要注意的是,我们写入的数据结构可能存在如下所示的跨页的问题, + +`/// YOUR JOB: get time with second and microsecond +/// HINT: You might reimplement it with virtual memory management. +/// HINT: What if [TimeVal] is splitted by two pages ?` + +所以我们不能把数据一次性全部写入,这里我选择的办法是将数据转换为字节数组 并依次写入。 + +**mmap和unmmap:** + +**首先来说mmap:** + +mmap需要实现的是将一段虚拟地址进行页面映射,想要实现这个功能,我们需要在进程的地址空间中加入添加新的页面映射,而该系统正好提供了为地址空间提供了insert_framed_area方法,用于插入新的逻辑段,这样的话情况就会变得很简单。 + +首先我们根据可能的错误对函数给出的参数进行对应的判断,并且通过port得到MapPermission。 + +```rust +if _start % PAGE_SIZE != 0 || _port & !0x7 != 0 || _port & 0x7 == 0 { +return -1; + +} +let portpomis = Portpomiss::from_bits_truncate(_port as u8); +let mut flag:MapPermission=MapPermission::empty(); +flag|=MapPermission::U; +if portpomis.contains(Portpomiss::R){ + flag|=MapPermission::R; +} +if portpomis.contains(Portpomiss::W){ + flag|=MapPermission::W; +} +if portpomis.contains(Portpomiss::X){ + flag|=MapPermission::X; +} +``` + +之后我们检查虚拟页面是否已经被映射过 + +```rust +for vpn in vpn_range { + if let Some(pte) = inner.tasks[cur].memory_set.translate(vpn) { + if pte.is_valid() { + return -1; + } + } +} +``` + +最后我们直接添加页面映射 + +`inner.tasks[id].memory_set.insert_framed_area(_start, _end, port);` + +[问答题-lab2](%E9%97%AE%E7%AD%94%E9%A2%98-lab2%2013a08b3cc7f1809ba8bcf3af8545930f.md) + +### 第五章:进程及进程管理 + +ch5要求我们具体完成的内容如下: + +> **spawn 系统调用定义**( [标准spawn看这里](https://man7.org/linux/man-pages/man3/posix_spawn.3.html) ): +> +> +> **`fn** sys_spawn(path: ***const** u8) -> isize` +> +> - syscall ID: 400 +> - 功能:新建子进程,使其执行目标程序。 +> - 说明:成功返回子进程id,否则返回 -1。 +> - **可能的错误:** +> - 无效的文件名。 +> - 进程池满/内存不足等资源错误。 +> +> TIPS:虽然测例很简单,但提醒读者 spawn **不必** 像 fork 一样复制父进程的地址空间。 +> +> **stride 调度算法** +> +> 算法描述如下: +> +> (1) 为每个进程设置一个当前 stride,表示该进程当前已经运行的“长度”。另外设置其对应的 pass 值(只与进程的优先权有关系),表示对应进程在调度后,stride 需要进行的累加值。 +> +> 1. 每次需要调度时,从当前 runnable 态的进程中选择 stride 最小的进程调度。对于获得调度的进程 P,将对应的 stride 加上其对应的步长 pass。 +> 2. 一个时间片后,回到上一步骤,重新调度当前 stride 最小的进程。 +> +> 可以证明,如果令 P.pass = BigStride / P.priority 其中 P.priority 表示进程的优先权(大于 1),而 BigStride 表示一个预先定义的大常数,则该调度方案为每个进程分配的时间将与其优先级成正比。证明过程我们在这里略去,有兴趣的同学可以在网上查找相关资料。 +> +> 其他实验细节: +> +> - stride 调度要求进程优先级 ≥2,所以设定进程优先级 ≤1 会导致错误。 +> - 进程初始 stride 设置为 0 即可。 +> - 进程初始优先级设置为 16。 +> +> 为了实现该调度算法,内核还要增加 set_prio 系统调用 +> + +**思路:** + +**我们先说spawn系统调用:** + +就如同TIPS一样,我们不需要复制父进程的地址空间, + +```rust +pub fn sys_spawn(_path: *const u8) -> isize { + trace!( + "kernel:pid[{}] sys_spawn NOT IMPLEMENTED", + current_task().unwrap().pid.0 + ); + let current_task = current_task().unwrap(); + let token =current_user_token(); + let path = translated_str(token, _path); + let tcb=Arc::new(TaskControlBlock::new(get_app_data_by_name(path.as_str()).unwrap())); + let mut inner=tcb.inner_exclusive_access(); + let mut pin=current_task.inner_exclusive_access(); + inner.parent=Some(Arc::downgrade(¤t_task)); + pin.children.push(tcb.clone()); + drop(inner); + let pid = tcb.pid.0 as isize; + add_task(tcb); + pid +} +``` + +我们直接用 app_data来创建TaskControlBlock而跳过了复制父进程的地址空间,并将他连接到父进程的子进程上,这样才能在sys_waitpid中被遍历到。然后把他加入任务管理器。就可以通过测试。 + +**stride 调度算法:** + +时间原因暂未实现.. + +[问答题-lab3](%E9%97%AE%E7%AD%94%E9%A2%98-lab3%2013a08b3cc7f180538397fa4a1ad9d508.md) + +### **第六章:文件系统与I/O重定向** + +ch6要求我们具体完成的内容如下: + +### 硬链接 + +硬链接要求两个不同的目录项指向同一个文件,在我们的文件系统中也就是两个不同名称目录项指向同一个磁盘块。 + +本节要求实现三个系统调用 `sys_linkat、sys_unlinkat、sys_stat` 。 + +**linkat**: + +> syscall ID: 37功能:创建一个文件的一个硬链接, linkat标准接口 。C接口: int linkat(int olddirfd, char* oldpath, int newdirfd, char* newpath, unsigned int flags)Rust 接口: fn linkat(olddirfd: i32, oldpath: *const u8, newdirfd: i32, newpath: *const u8, flags: u32) -> i32参数:olddirfd,newdirfd: 仅为了兼容性考虑,本次实验中始终为 AT_FDCWD (-100),可以忽略。flags: 仅为了兼容性考虑,本次实验中始终为 0,可以忽略。oldpath:原有文件路径newpath: 新的链接文件路径。说明:为了方便,不考虑新文件路径已经存在的情况(属于未定义行为),除非链接同名文件。返回值:如果出现了错误则返回 -1,否则返回 0。可能的错误链接同名文件。 +> + +**unlinkat**: + +> syscall ID: 35功能:取消一个文件路径到文件的链接, unlinkat标准接口 。C接口: int unlinkat(int dirfd, char* path, unsigned int flags)Rust 接口: fn unlinkat(dirfd: i32, path: *const u8, flags: u32) -> i32参数:dirfd: 仅为了兼容性考虑,本次实验中始终为 AT_FDCWD (-100),可以忽略。flags: 仅为了兼容性考虑,本次实验中始终为 0,可以忽略。path:文件路径。说明:注意考虑使用 unlink 彻底删除文件的情况,此时需要回收inode以及它对应的数据块。返回值:如果出现了错误则返回 -1,否则返回 0。可能的错误文件不存在。 +> + +**fstat**: + +> syscall ID: 80功能:获取文件状态。C接口: int fstat(int fd, struct Stat* st)Rust 接口: fn fstat(fd: i32, st: *mut Stat) -> i32参数:fd: 文件描述符st: 文件状态结构体#[repr(C)] +#[derive(Debug)] +pub struct Stat { + /// 文件所在磁盘驱动器号,该实验中写死为 0 即可 pub dev: u64, + /// inode 文件所在 inode 编号 pub ino: u64, + /// 文件类型 pub mode: StatMode, + /// 硬链接数量,初始为1 pub nlink: u32, + /// 无需考虑,为了兼容性设计 pad: [u64; 7], +} + +/// StatMode 定义:bitflags! { + pub struct StatMode: u32 { + const NULL = 0; + /// directory const DIR = 0o040000; + /// ordinary regular file const FILE = 0o100000; + } +} +> + +**我们先来实现fstat:** + +fstat需要我们先得到三个位置信息分别是 + +> /// inode 文件所在 inode 编号 pub ino: u64, + /// 文件类型 pub mode: StatMode, + /// 硬链接数量,初始为1 pub nlink: u32, +> + +inode 文件所在 inode 编号: + +在rcore系统的内核中,似乎没有提供能直接从OSinode得到inode id的接口,所以有两种办法,一种是去修改Easy-fs,另有一种方法是在OSinode被创建的时候给他们分配id。在这里我使用的是后者。 + +在OSinode结构体中添加如下结构体 + +`pub struct Ino{ +link:u32, +ino:u64, +}` + +然后再每次打开文件之后对OSinode 使用一个bitmap来分配 id,在文件关闭时用bitmap来回收id; + +bitmap在 easyfs中的`bitmap.rs`有相应实现。我们只需要利用bitmap的alloc和dealloc来实现id的分配和回收。 + +其中文件类型,可以通过使用 `read_disk_inode` 获取 `inode` 对应的 `DiskInode` 的只读引用,并从中读取相关信息即可。 + +最后是硬链接数量,该信息显然需要结合link 与 unlink来维护,我们在link时通过刚刚所分配的id,来进行映射即可 + +`ITOS.get_mut(&op).unwrap().link+=1;` + +ps:ITOS是一个BTreeMap 可以通过文件名来获取或者修改对应的 Ino结构体。 + +**我们再来实现link与unlink:** + +首先我们刚刚已经为文件进行了id的分配,我们继续利用先前所分配的id,进行管理。 + +首先我储存了新的名字和磁盘块原名之间的映射,在文件open与close前,将输入的名字(如果已经被映射)替换为原名来实现两个不同名称目录项指向同一个磁盘块。 + +如下: + +```rust +if unsafe { +UMAP2.contains_key(&path1) +}{ +{ + path = unsafe { UMAP2 [&path1].clone()}; + + } +} +else { + path =path1; +} + +``` + +unlink的逻辑也与link类似,通过用BTreeMap的remove方法将映射删除。 +但是要特别注意的是,当link为0时,我们需要回收inode id + +```rust +let c= unsafe { ITOS .get_mut(&name).unwrap()}; +if c.link>0{ + c.link-=1; + } + else{ + let fd=unsafe { UMAP1.get(&name).unwrap() }; + let bind=current_task().unwrap(); + let inner=bind.inner_exclusive_access().fd_table[*fd].clone().unwrap(); + inner.clear(); + + } +``` + +**向前兼容:** + +唯一要注意的点就是在spawn 中,我们需要用ch6新建立的文件系统来获取应用数据,而不是像ch4一样用translated_str方法直接获取应用数据。 + +`let Some(app_inode) = open_file(path.as_str(), OpenFlags::RDONLY)` + +向前兼容的其他方面都与先前类似。 + +[问答题-lab4](%E9%97%AE%E7%AD%94%E9%A2%98-lab4%2013a08b3cc7f1800b8911c11b8195ec54.md) + +### 第八章:并发 + +ch8要求我们具体完成的内容如下: + +> 死锁检测 +> +> +> 目前的 mutex 和 semaphore 相关的系统调用不会分析资源的依赖情况,用户程序可能出现死锁。 我们希望在系统中加入死锁检测机制,当发现可能发生死锁时拒绝对应的资源获取请求。 一种检测死锁的算法如下: +> +> 定义如下三个数据结构: +> +> - 可利用资源向量 Available :含有 m 个元素的一维数组,每个元素代表可利用的某一类资源的数目, 其初值是该类资源的全部可用数目,其值随该类资源的分配和回收而动态地改变。 Available[j] = k,表示第 j 类资源的可用数量为 k。 +> - 分配矩阵 Allocation:n * m 矩阵,表示每类资源已分配给每个线程的资源数。 Allocation[i,j] = g,则表示线程 i 当前己分得第 j 类资源的数量为 g。 +> - 需求矩阵 Need:n * m 的矩阵,表示每个线程还需要的各类资源数量。 Need[i,j] = d,则表示线程 i 还需要第 j 类资源的数量为 d 。 +> +> 算法运行过程如下: +> +> 1. 设置两个向量: 工作向量 Work,表示操作系统可提供给线程继续运行所需的各类资源数目,它含有 m 个元素。初始时,Work = Available ;结束向量 Finish,表示系统是否有足够的资源分配给线程, 使之运行完成。初始时 Finish[0..n-1] = false,表示所有线程都没结束;当有足够资源分配给线程时, 设置 Finish[i] = true。 +> 2. 从线程集合中找到一个能满足下述条件的线程 +> +> `1Finish[i] == false; +> 2Need[i,j] ≤ Work[j];` +> +> 若找到,执行步骤 3,否则执行步骤 4。 +> +> 1. 当线程 thr[i] 获得资源后,可顺利执行,直至完成,并释放出分配给它的资源,故应执行: +> +> `1Work[j] = Work[j] + Allocation[i, j]; +> 2Finish[i] = **true**;` +> +> 跳转回步骤2 +> +> 1. 如果 Finish[0..n-1] 都为 true,则表示系统处于安全状态;否则表示系统处于不安全状态,即出现死锁。 +> +> 出于兼容性和灵活性考虑,我们允许进程按需开启或关闭死锁检测功能。为此我们将实现一个新的系统调用: `sys_enable_deadlock_detect` 。 +> +> **enable_deadlock_detect**: +> +> - syscall ID: 469 +> - 功能:为当前进程启用或禁用死锁检测功能。 +> - C 接口: `int enable_deadlock_detect(int is_enable)` +> - Rust 接口: `fn enable_deadlock_detect(is_enable: i32) -> i32` +> - **参数:** +> - is_enable: 为 1 表示启用死锁检测, 0 表示禁用死锁检测。 +> - **说明:** +> - 开启死锁检测功能后, `mutex_lock` 和 `semaphore_down` 如果检测到死锁, 应拒绝相应操作并返回 -0xDEAD (十六进制值)。 +> - 简便起见可对 mutex 和 semaphore 分别进行检测,无需考虑二者 (以及 `waittid` 等) 混合使用导致的死锁。 +> - 返回值:如果出现了错误则返回 -1,否则返回 0。 +> - **可能的错误** +> - 参数不合法 +> - 死锁检测开启失败 + +**思路:** + +要为锁机制实现死锁检测,关键在于如何维护相关状态,并在检测前构造出合适的资源分配结构。我们可以从mutex开始:每个线程最多只需要一把锁,因为线程只有在获取所需的锁资源后才能继续执行,若没有锁资源可用,线程将会被阻塞。因此,为每个线程分配一个 `mutex_need` 变量,用来记录当前线程所需的锁的 id。 + +除此之外,我们还需要维护一个 `mutex_allocation` 向量,记录线程当前已获得但未释放的锁资源的 id。当线程调用 `sys_mutex_lock(mutex_id)` 请求锁时,在实际获取锁前,我们将当前线程的 `mutex_need` 设置为 `mutex_id`。当线程成功获得锁时,将 `mutex_id` 加入 `mutex_allocation` 向量,并将 `mutex_need` 置为空。这里可以使用 `usize::MAX` 来表示空值,也可以使用 `Option` 类型,后者更为优雅。 + +当线程调用 `sys_mutex_unlock(mutex_id)` 释放锁时,在实际释放锁前,首先查找并移除 `mutex_allocation` 向量中对应的 `mutex_id` 元素。 + +通过这种方式,我们能够在死锁检测之前,根据维护的信息构建出 `Available`、`Allocation` 和 `Need` 数据结构,进而使用银行家算法判断当前系统是否处于不安全状态。信号量的实现方式大致相同,但由于信号量的数量不再是二值的,因此线程的资源分配向量需要记录每个信号量的数量。我们可以用 `` 这样的二元组来表示,或者也可以使用 `cnt` 个 `sem_id` 元素。在这里,我采用前者。 + +此外,信号量还可以为负数,负值的绝对值表示资源的提前“透支”数量。在银行家算法中,`Available[i][j]` 不应为负值,若出现负数,应视为 0 处理。 + +```rust +fn deadlock_check(available: Vec, allocation: Vec>, need: Vec>) -> bool { + // n: thread count m: resources count + let (n, m) = (allocation.len(), allocation[0].len()); + let mut work = available; + let mut finish = vec![false; n]; + loop { + let mut idx = usize::MAX; + for i in 0..n { + let mut flag = true; + if finish[i] { + continue; + } + for j in 0..m { + if need[i][j] > work[j] { + flag = false; + break; + } + } + if flag { + idx = i; + break; + } + } + // has found a thread meet the requirement + if idx != usize::MAX { + for j in 0..m { + work[j] += allocation[idx][j]; + } + finish[idx] = true; + } else { + break; + } + } + finish.iter().all(|&x| x) + } + +``` + +[问答题-lab5](%E9%97%AE%E7%AD%94%E9%A2%98-lab5%2013a08b3cc7f180caaa7df2266f4cf7cc.md) + +--- \ No newline at end of file diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\346\233\271\350\276\260\345\256\207.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\346\233\271\350\276\260\345\256\207.md" new file mode 100644 index 0000000000..0204eedb27 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\346\233\271\350\276\260\345\256\207.md" @@ -0,0 +1,37 @@ +--- +title: 2024秋冬开源操作系统训练营第一、二阶段总结-曹辰宇 +date: 2024-11-10 23:53:10 +categories: +- blog +tags: +- author:a6d9a6m +--- + + +# 阶段一:rust语言学习阶段 + 学了一门新的语言,很是开心,对于Rust这门语言,在使用了一段时间后,虽然编译器折磨了我好久好久,但是,适应后我觉得实在是太贴心了。我不用担心内存 + 莫名其妙崩溃,不用担心自己的代码“不够快”。对于C++来说,Rust更像工业化的结晶,浑身上下散发着标准化的气息,我认为Rust的未来是光明的,它肯定会逐渐 + 顶替掉现在几大编程语言的地位。 + +# 阶段二:rCore OS设计实现阶段 + 这个阶段我获益匪浅,反复修bug也极大增强了我的编程能力,真的很喜欢,就是时间好感呀┭┮﹏┭┮,现在马上时间就截止了 +## lab1 + 实现了一个TaskInfo的提取,本身难度不大,不过刚接触系统的我写了好久,后面发现关键问题后自然就“迎刃而解”了 + 。 +## lab2 + 实现虚拟内存,诶,之前我暑假就学习了CSAPP,对虚拟内存的运作还是比较熟悉的,可是,我没有做实验,呀呀呀呀。做实验时那个TimeVal我一直不确定怎么 + 处理,还有就是我一开始思路严重有问题,舍弃了已有的封装好的系统,企图自己在写一个全局内存管理器。其实用户空间跟每个任务已经绑定在一起了,轻轻push + 一下就好了。在四处求救后TimVal也是解决了 + +## lab3 + 这个实验不理解具体区别,我一遍就过了,嘻嘻 + +## lab4 + + 文件IO重定向,这个我全程独立解决,并且我认为很好的实现了最开始的要求,unlinkat能够替换掉close,并且更改到了内存。 +## lab5 + 很难,很有跳战,银行家算法也让人眼前一亮,哈哈,写出来了 + +# 其他: + 诶,最后一个实验自己sys_get_time没写,一直卡关,真的好无语 + diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" new file mode 100644 index 0000000000..49a4465494 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" @@ -0,0 +1,174 @@ +--- + +title: rust体会 +date: 2024-11-11 +categories: + - rust language +tags: + - author:ekkure + - repo:https://github.com/ekkure/blog +--- +### Rust编程技巧与示例:宏、算法与类型转换 + +在Rust编程中,有许多细节和技巧可以帮助开发者更好地组织代码、优化算法性能,以及确保类型安全。本篇博客汇总了一些Rust编程的核心要点和实用代码示例,涵盖了宏的使用、排序算法、树和图的操作等内容。 + +--- + +### 1. 宏与#[macro_export]、#[macro_use] + +Rust中的宏非常强大,用于生成重复代码和提升代码的灵活性。使用`#[macro_export]`可以导出宏,使其在其他模块或包中可用;而`#[macro_use]`则在现代Rust中被推荐通过`use`语句显式引入。 + +示例宏定义: +```rust +#[rustfmt::skip] +macro_rules! my_macro { + () => { println!("Check out my macro!"); }; + ($val:expr) => { println!("Look at this other macro: {}", $val); }; +} +``` + +这里的`#[rustfmt::skip]`用于避免自动格式化,保持代码样式的灵活性和可读性。 + +--- + +### 2. Rust中的类型与特性 + +在实现数据结构或算法时,我们通常需要对泛型类型T施加一些特性约束,例如: +- `Ord`:使得元素可以比较大小,适用于排序、合并等操作。 +- `Clone`:便于复制元素值,即使是复杂类型,也可以无所有权转移地复制。 +- `Display`:实现字符串友好的格式化输出,便于打印和日志记录。 + +这些特性可以通过`where`语句在泛型实现中指定: +```rust +impl LinkedList +where T: Ord + Clone + Display +``` + +--- + +### 3. 内存操作与指针 + +Rust通过`unsafe`块支持手动管理内存和指针操作,用于高性能或底层操作。 +例如,获取节点的指针并解引用: +```rust +let node_ptr = Some(unsafe { NonNull::new_unchecked(Box::into_raw(node)) }); +res.add((*node_ptr.as_ptr()).val.clone()); +cur_a = (*node_ptr.as_ptr()).next; // 注意这里直接获取的是ta的next指针 +``` + +指针的安全解包和操作要格外小心,可以使用`Option`配合`unsafe`避免空指针风险。 + +--- + +### 4. 算法设计示例 + +#### 4.1 链表与树的操作 + +##### 插入与查找 +在链表或树结构中,我们经常用到`Option`类型来表示节点的存在与否。例如,在插入和查找二叉树中,可以选择使用`if let`语句来处理`Some`和`None`的情况: +```rust +fn insert(&mut self, value: T) { + if let Some(ref mut node) = self.root { + node.insert(value); + } else { + self.root = Some(Box::new(TreeNode::new(value))); + } +} +``` +这种写法在处理可变引用时尤其简洁。 + +#### 4.2 排序算法与Ord与PartialOrd + +选择排序等算法需要比较泛型元素的大小,通常需要`PartialOrd`特性来支持部分排序(如非全序关系的情况),而对于要求全序的场景可以使用`Ord`。 + +#### 4.3 深度优先与广度优先搜索 + +在图算法中,深度优先搜索(DFS)和广度优先搜索(BFS)是两种基础的遍历方式: +- DFS示例: + ```rust + fn dfs_util(&self, v: usize, visited: &mut HashSet, visit_order: &mut Vec) { + visited.insert(v); + visit_order.push(v); + for &nei in self.adj[v].iter() { + if !visited.contains(&nei) { + self.dfs_util(nei, visited, visit_order); + } + } + } + ``` + +- BFS示例: + ```rust + fn bfs_with_return(&self, start: usize) -> Vec { + let mut visit_order = vec![]; + let mut visited = vec![false; self.adj.len()]; + let mut queue = VecDeque::new(); + queue.push_back(start); + visited[start] = true; + + while let Some(node) = queue.pop_front() { + visit_order.push(node); + for &neighbor in &self.adj[node] { + if !visited[neighbor] { + visited[neighbor] = true; + queue.push_back(neighbor); + } + } + } + visit_order + } + ``` + +#### 4.4 平衡堆的插入与调整 + +Rust标准库中`Vec`的`swap_remove`方法可以高效地删除指定位置的元素,适用于实现优先队列等堆结构: +```rust +let result = self.items.swap_remove(1); // 移除并返回指定位置的元素 +``` + +在删除元素后,可以通过调整堆结构(如最小/最大堆)来保持堆的性质。 + +--- + +### 5. 实现栈与队列 + +使用双队列实现栈的操作逻辑: +```rust +pub struct myStack { + q1: Queue, + q2: Queue +} + +impl myStack { + pub fn push(&mut self, elem: T) { + self.q2.enqueue(elem); + while !self.q1.is_empty() { + self.q2.enqueue(self.q1.dequeue().unwrap()); + } + std::mem::swap(&mut self.q1, &mut self.q2); + } +} +``` + +这种方法利用队列的FIFO特性来模拟栈的LIFO特性。 + +--- + +### 6. 函数与内存管理 + +Rust中的`Box`和`unsafe`结合用于手动管理堆内存。`Box::from_raw`可以从裸指针重新创建`Box`,这在需要手动内存管理的场景中非常有用。 +```rust +unsafe fn raw_pointer_to_box(ptr: *mut Foo) -> Box { + let mut ret: Box = unsafe { Box::from_raw(ptr) }; + ret.b = Some(String::from("hello")); + ret +} +``` + +这种方法常用于FFI(外部函数接口)中将指针恢复为拥有所有权的Rust类型。 + +--- + +### 总结 + +Rust语言通过丰富的内存管理工具和类型系统,确保了在安全性和性能上的平衡。无论是自定义数据结构还是排序、图遍历等基础算法,Rust的特性可以为代码提供极大的灵活性和安全保障。 \ No newline at end of file diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-wingrew.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-wingrew.md" new file mode 100644 index 0000000000..030e6d890a --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-wingrew.md" @@ -0,0 +1,7 @@ +--- +title: 2024秋冬季开源操作系统训练营第一阶段总结-wingrew +date: 2024-11-10 21:18:27 +tags: +--- + +该阶段练习rust语法。一百道题目够大家堪堪入门,数量正好。当然还有很多用法是后面在第二阶段慢慢掌握的。rust这门语言特色比较鲜明,所有权机制、生命周期管理等等,都挺有意思的,而且要和rustc斗智斗勇,很难忘。 diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-firecrack.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-firecrack.md" new file mode 100644 index 0000000000..1b8883a9e7 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-firecrack.md" @@ -0,0 +1,80 @@ +--- +title: 2024秋冬季开源操作系统训练营第一阶段总结报告-firecrack +date: 2024-11-10 21:19:59 +tags: +--- +# 语言基础 + +## 编译 +rustc **.rs +## 项目构建cargo +cargo build默认根据代码创建一个可执行文件 +cargo run 运行程序 +cargo check检查语法和类型错误 + +## 模块 +crate +一个crate相当于C++中的翻译单元 ++ binary crate 可以被编译成可执行程序,其必须有一个main函数作为程序入口 ++ library crate 常说的库文件,用于供其他程序调用 + +## package +包是crate的集合,由一个Cargo.toml文件定义,可以有多个binary crate 但最多有一个库 +在使用cargo new创建一个包后,默认包含一个以src/main.rs为入口的同名binary crate; +如果还有一个src/lib.rs文件,则包还包含了一个同名的library crate + +## modules +module 是关于在一个crate内如何组织代码 +编译期在编译时,首先从src/main.rs开始查找要编译的代码 + + +## 模块声明 +可以通过mod xxx {} 来直接在文件内定义一个模块。 ++ 一个module内的成员默认是模块私有的,如果想要在模块外使用,必须声明为pub + +## 子模块 +模块允许嵌套,子模块的成员要想被最外层的访问,其每一层都需要pub关键字 + +## 字符串 +字面量的类型是&str +``` +let s = "hello, world"; +``` +带所有权的String,分配在堆上,底层是一个Vec,注意结尾没有null +``` +let s = String::with_capacity(32); +s.push_str("Hello world"); +``` +这里会在堆上申请32字节的内存,在栈上创建指针s(p, 11, 32) + +## String 和 &str +### &str to String ++ to_owned:从借用类型创建拥有所有权的副本,适用于任何实现了 ToOwned 特性的类型。 ++ to_string:将实现了 Display 特性的类型转换为 String。 ++ String::from 和 .into():将 &str 转换为 String,分别适用于显式和隐式转换。 + +## 字符串拼接 +使用+号进行拼接时,左侧必须是一个String类型,而右操作数则必须是&str,其中&String可以自动转成&str + +## Unwrap 和 ? +1. unwrap 是一个方法,通常用于在你确定一个 Result 或 Option 包含值时获取该值。如果 Result 或 Option 包含 Err 或 None,调用 unwrap 会导致程序崩溃,并显示一个错误信息。因此,使用 unwrap 时,你应该确保被操作的 Result 或 Option 不会为 Err 或 None。 +2. ? 是一个在函数中处理错误的语法糖,它可以帮助你编写更简洁的代码。当你在一个返回 Result 或 Option 的函数中使用 ? 时,如果结果为 Err 或 None,? 会立即返回该错误,否则它会解包 Ok 或 Some 中的值。这种方式允许你在遇到错误时将错误传播给调用者,而不需要显式地处理每一个可能的错误情况。 + +## traits +在 Rust 中,实现 trait 时,不能直接在 trait 的函数签名中给参数添加 mut、&mut、& 等符号。这些符号只能出现在具体实现(impl)中, +而不是在 trait 定义本身。 + +## lifetime +生命周期与引用是紧密相关的。 ++ 为编译期在编译期提供一种验证指针有效性的的方式 ++ 生命周期是指定的代码区域,引用必须在这些区域内有效 +### 'xxx ++ 生命周期的标记只是描述多个引用之间的生命周期关系,不影响实际对象的真实生命周期 +### 标记消除 +为了减少编码量,当引用满足如下规则时,不需要标记引用的生命周期 ++ 针对输入参数,编译器会给每个参数不同的生命周期标记 ++ 对于返回值,如果函数只有一个输入参数,那么引用类型的返回值的生命周期标记与输入相同 ++ 如果第一个参数是&self或者&mut self那么输出的生命周期与其一致 + +## 迭代器 +map的闭包参数时迭代器元素本身,而filter的闭包参数则是迭代器元素的引用,所以filter的引用符号比map多一个 \ No newline at end of file diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-hxingjie.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-hxingjie.md" new file mode 100644 index 0000000000..f38c8dfaa2 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-hxingjie.md" @@ -0,0 +1,33 @@ +--- +title: 2024秋冬季开源操作系统训练营第一阶段总结报告-hxingjie +date: 2024-11-06 15:17:06 +categories: + - summary report +tags: + - author:hxingjie + - repo:https://github.com/LearningOS/rust-rustlings-2024-autumn-hxingjie +--- + + +## 一、前言 + + 在过去两周,我学习了Rust编程语言的相关知识。通读了一遍《Rust程序设计语言》书籍并完成了训练营的Rustlings练习。经过两周的学习,我对Rust有了初步的认识和掌握。以下是我对这两周学习过程的总结。 + + +## 二、学习内容 + +1. Rust基础知识 + +- Rust编程语言的基本语法,包括变量、数据类型、运算符、控制流等。 +- Rust的所有权系统,包括所有权、借用、生命周期等概念。 + +2. Rustlings练习 + +- 通过完成一系列练习,巩固对Rust基础知识的理解和应用。 +- 练习涵盖了Rust的所有权、借用、生命周期、错误处理、宏、模式匹配等方面的内容。 + + +## 三、学习心得 + + 非常感谢训练营的老师和助教帮助,让我能够在两周的时间快速入门rust这门优秀的语言。印象最深的还是最后的算法部份,尤其是前两道关于链表的题目,其实之前一直是使用c++做算法题,对链表也较为熟悉了,但是由于对rust的特性不熟悉以及对链表没有深刻理解,让我有一种有力使不出的感觉,后面通过阅读题目的框架,以及对书本知识的巩固,终于是对rust中的链表有了初步的认识,写完链表的题目后,后续的题目也很快完成了。rust语言的特性让我对编程和计算机有了更深的理解,尽管现在还是写得磕磕绊绊,和编译器打架,但是相信通过不断学习和时间,将来我也能够编写出优秀的rust代码。 + diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-rqj.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-rqj.md" new file mode 100644 index 0000000000..0f41753c55 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-rqj.md" @@ -0,0 +1,18 @@ +--- +title: 2024秋-一阶段总结-rqj +date: 2024-11-09 14:34:47 +categories: + - 2024秋冬季开源操作系统训练营 +tags: + - author:ghj1222 + - repo:https://github.com/LearningOS/rust-rustlings-2024-autumn-ghj1222 +--- + +# Rustlings + +我使用过一段时间 Rust,但是已经有大约一年时间没使用了,Rustlings 的练习让我重新找回对 Rust 的熟悉感。Rustlings 前面的一些部分都是通过魔改代码,让编译通过并通过测试,从而理解 Rust 的特性。因为魔改的地方就是教学的地方,所以通过魔改少量代码的方式教学,我认为是非常高效的,而 watch 功能则更快捷方便连续地学习,就跟闯关一行。 + +相比于 Rust-by-Example,Rustlings 更偏向于实践,更方便开发者在了解 C/C++ 等高级语言的基础上,了解 Rust 的内存安全特性,包括所有权、生命周期等机制,迁移到 Rust。Rust 更强大的枚举、元组等类型以及支持更完善的标准库,更方便开发者专注于程序算法本身,而不是对程序的具体过程操心。Rust 的迭代器抽象还能够方便的提供函数式编程,使用一行语句就完成对数组全部操作,在数学上更美观,同时应该也更方便编译器进行优化。 + +Rustlings 最难的部分应该是一些算法的实现。正如 Rust 的所有权机制,通常对象之间有树形的嵌套关系,而描述双向链表、图这种数据结构则并不适合使用这种关系;其中有一个练习是二叉排序树,但是不要求实现平衡。如果要实现平衡树则要考虑到树的旋转等操作,在这种所有权机制下则更难实现。而对于图,则只能对于整个图整体分配存储、整体管理,最后很容易写出 C 风格的代码,或者使用了大量的 unsafe( algorithm1 的单链表合并中就有大量 unsafe)。我也尝试过使用 Rust 练习 AtCoder,体验堪称坐牢,写 Rust 就是和编译器作斗争。所以使用 Rust 完成漂亮的算法类代码,是一门深刻的艺术。 + diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" new file mode 100644 index 0000000000..44d97b6544 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" @@ -0,0 +1,158 @@ +--- + +title: 调度与死锁 +date: 2024-11-11 +categories: + - os +tags: + - author:ekkure + - repo:https://github.com/ekkure/blog + +--- +## 调度 + +#### **三级调度:** + +作业调度、高级调度(频次最低):主要解决:接纳多少个任务+接纳那哪些任务这两个工作 + +进程调度、低级调度(频次最高): 必须有、**核心**,**确定哪个进程可以占有CPU并执行** + +中级调度:将那些暂时不能运行的进程从内存挂起到外存,(阻塞状态下进程实体(程序 + 数据 + PCB)还在内存中,而挂起状态会把进程实体挂到外存,但是PCB会存在系统内核空间中,会记录进程在外存的状态以及位置),一般在**内存紧张**时使用 + + + +高级调度,用于批处理系统中,将任务从外存调度到内存中去。(在分时/实时系统中,任务是直接在内存,因此没有高级调度) + +分时系统:**只有进程调度** + +批处理系统:**进程调度 + 作业调度** + + + +#### 调度算法相关 + +准则:周转时间(常用于**批处理系统**)、平均周转时间、带权周转时间 + +响应时间(交互性作业、分时系统)、截止时间的保证(实时系统)、优先权准则 + +周转时间 = 完成时间 - 到达时间 + +带权周转时间 = 周转时间 / 服务时间 + + +**调度算法** + +- FCFS(first come first serve), SJ(P)F等等 +对于抢占式调度,注意**服务时间的更新,然后再比较,看谁抢** + +- 高优先权 优先调度算法 + + 静态优先权:简单,但存在饥饿现象 + + 动态优先权:eg Rp = (等待时间 + 服务时间)/ 服务时间 作为优先权 1 + tw / ts; + +- 时间片轮转 ......? + + 多级反馈队列 S1 < S2 < S3 优先权 S1 > S2 > S3 + + + +- 实时调度 + + 非抢占:轮转 || 优先权 + + 抢占:基于中断时钟,好处是减少了上下文保存切换的次数 + + ​ 立即抢占 + + 实时调度算法:EDF、LLF,还有例题 + +- 其他一些?? + + MPS:CPU共享内存, 共享缓存(单个儿独立的,容易出现绑定,忙闲不均) + + SMP中进程分配方式:静态分配和动态分配 + + ​ 调度方式: 自调度和成组调度(两种方式就对应了用户级线程和系统级线程), 专用处理机分配? + + +## 死锁 + +**一些定义**: + +- 可剥夺资源:如主存,CPU,可以在使用时被强占的资源 +- 不可剥夺资源:不可被打断抢占的资源,如驱动器,打印机 +- 永久资源(外存),临时资源(进程运行过程中临时产生的数据资源等等) + +**竞争非剥夺资源,或者竞争临时资源可导致死锁** + +### 死锁的必要条件 + +- 互斥条件:进程互斥的使用临界资源 +- 不剥夺条件(不可抢占) +- 请求-保持条件:进程在申请新的资源的同时,保持对某些资源的占有 +- 环路等待:循环等待链 + + + + + +### 解决死锁的方法 + +从严格依次降低,为 + +预防 -> 避免 -> 检测与解除 + +#### 预防 + +上面4个条件是死锁的必要条件 , Deadlock -> 4 其逆否命题为 !4 -> !Deadlock,所以我们从4个条件入手 + +1. 互斥,并没有好的办法 +2. 不抢占:不抢占变成"抢占",如果进程申请不到全部资源时,主动释放 +3. 请求保持条件:使用AND机制,但是有点浪费资源 +4. 环路等待:破除环路,资源排序,参考哲学家进餐 + +#### 避免死锁 + +**这是比较中庸的做法,既不损耗很多的效率,也比较的严格** + +##### 银行家算法 + +一种是,资源分配表,为 + +| Process | Allocation | Need | Available | +| ------- | ---------- | ---- | --------- | +| | | | | + +另一种是,计算表 + +| Work | Need | Allocation | work + Allocation | Finish | +| ---- | ---- | ---------- | ----------------- | ------ | +| | | | | | + +**对资源进行分配时,分成两步** + +1. 判断分配请求 R是否满足 R < Available && R < Need +2. 如果满足1,使用表1表示分配后的资源表T1,再次计算是否存在安全序列,如果不安全,退回至T0,否则保存T1,下次分配将从T1开始。 + +#### 检测和解除 + +使用方法:**资源分配图** + +**几个结论** + +- 不可完全简化 => 存在死锁 +- 分配图中无环 => 不会存在死锁 +- 分配图中有环 => 不一定死锁 + +简化方法,对一个资源分配图,首先考虑持有边,如果持有者线程能够完成(获得所有需要的资源),将持有边消去后,将资源返回,如果不能完成,边消去后,仍保持资源占有,直到完成。 + +然后考虑请求边,如果请求的资源有空闲的,可以把边消去,若请求线程能够完成,则可将该资源返回,否则保持占有 + +重复上述过程,直至卡住,或者全部成孤立。 + +**解除** + +通过撤销进程或者挂起进程来释放一些资源,进而推动僵持状态。 + +而具体的对哪些进程,以什么样的顺序进行操作,可以参考`Dijkstra`之类的算法,找到一种损耗最小、利益最大的方法。 diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-wingrew.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-wingrew.md" new file mode 100644 index 0000000000..a4ec5aaa6c --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-wingrew.md" @@ -0,0 +1,9 @@ +--- +title: 2024秋冬季开源操作系统训练营第二阶段总结-wingrew +date: 2024-11-10 21:18:27 +tags: +--- + +本实验难度基本和os课程大作业难度一致,可能稍微容易一点。出于时间原因和个人能力不足,代码写得比较丑陋,健壮性不足。 + +第二阶段很好的带着大家窥探了os的几个基本部分。进程管理、内存管理、通信、文件系统等等比较好的串联起来了,能够让大家有连贯的感觉,知道os是怎么跑起来的,从教学代码来说,算是比较成功的,环环入扣。 diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-firecrack.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-firecrack.md" new file mode 100644 index 0000000000..0b34647ce7 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-firecrack.md" @@ -0,0 +1,219 @@ +--- +title: 2024秋冬季开源操作系统训练营第二阶段总结报告_firecrack +date: 2024-11-10 21:26:10 +categories: + - 2024秋冬季开源操作系统训练营 +tags: + - author: firecrack + - subtitle: Something Not yet done + - repo: git@github.com:LearningOS/2024a-rcore-firecrack.git +--- +# 专业阶段总结 + +## 四个抽象 ++ 执行环境 ++ 进程 ++ 地址空间 ++ 文件 +## 五个特征 ++ 虚拟化 + + 内存地址虚拟化 + + 内存大小虚拟化:通过引入硬盘空间、 + + CPU虚拟化:通过分时以及快速切换 ++ 并发 ++ 异步 ++ 共享 ++ 持久 + +## 程序编译流程 ++ 预处理:宏展开 ++ 编译器: ——> 汇编语言 ++ 汇编器:——> 目标代码 ++ 链接器:——> 可执行文件 + +## 不使用标准库 +`#![no_std]`添加在文件开头 +## 禁用默认的main +`#![no_main]` + +## qemu启动流程 +``` +qemu-system-riscv64 \ + -machine virt \ + -nographic \ + -bios ../bootloader/rustsbi-qemu.bin \ + -device loader,file=target/riscv64gc-unknown-none-elf/release/os.bin,addr=0x80200000 +``` ++ virt 硬件平台上,物理内存的起始物理地址为 0x80000000 ,物理内存的默认大小为 128MiB,所以物理地址空间为[0x80000000,0x88000000) ++ 启动时先将sbi文件加载到0x8000 0000, 将os.bin 加载到0x8020 0000 + +计算机启动的三个阶段: +1. CPU固有的一些程序,对应于QEMU就是其自带的一段程序,该程序第一条指令在0x1000处,最后一条指令就是跳转到0x8000 0000 执行bootloader +2. bootloader 负责一些计算初始化工作后,再跳转到第三程序,对于RUSTSBI第三阶段程序的入口地址固定为0x8020 0000,实际的应用中bootloader会负责将操作系统程序从硬盘加载到该地址 +3. 执行os的代码 + +## GDB调试 +### 查看内存 +`x/nfu addr` ++ n:表示要显示的内存单元的数量。 ++ f:格式,可以是 b(byte)、h(halfword,2字节)、w(word,4字节)、g(giant,8字节)等。 ++ u:显示的格式,可以是 x(十六进制)、d(十进制)、o(八进制)、t(二进制)等。 ++ addr:内存地址。 +### 打印寄存器 +`p/x $xx` + + +## 特权级切换 +S模式下的控制状态寄存器(CSR) ++ sstatus:SPP 等字段给出 Trap 发生之前 CPU 处在哪个特权级(S/U)等信息 ++ sepc:当 Trap 是一个异常的时候,记录 Trap 发生之前执行的最后一条指令的地址 ++ scause: Trap的原因 ++ stval: Trap的附加信息 (例如缺页异常时的虚拟地址) ++ stvec: Trap处理程序的入口地址 + +### 硬件 +当执行ecall时,硬件的工作: ++ sstatus 的 SPP 字段会被修改为 CPU 当前的特权级(U/S)。 ++ sepc ecall的地址记录在sepc寄存器中 ++ scause/stval 分别会被修改成这次 Trap 的原因以及相关的附加信息。 ++ CPU 会跳转到 stvec 所设置的 Trap 处理入口地址,并将当前特权级设置为 S ,然后从Trap 处理入口地址处开始执行。 + +sret ++ CPU 会将当前的特权级按照 sstatus 的 SPP 字段设置为 U 或者 S ; ++ CPU 会跳转到 sepc 寄存器指向的那条指令,然后继续执行。 +### 软件(操作系统) +在内存中存在两个全局变量,分别表示用户栈和内核栈,位于操作系统程序的.bss段 +从用户栈切换到内核栈,只需要将寄存器sp值进行更改即可 + +## 虚拟内存 +当程序所需空间大于物理内存时怎么办。在没有虚拟内存前一种方法是,程序员对程序手工分段,依次将每段程序装入内存中运行。 +虚拟存储器与此类似,只是由操作系统每次将正在使用的一部分放入内存,其余存在硬盘上。 ++ 虚拟存储器大小由处理器位数决定,对32位处理器来说地址范围为$0 \sim FFFF\quad FFFF$,其中某一地址就叫做虚拟地址(VA) ++ 没有使用虚拟地址的系统,其处理器输出的地址直接就是物理地址(PA),而如果使用了虚拟地址,则处理器输出的就是虚拟地址了,其需要通过内存管理单元(MMU)转换成物理地址 ++ 优点:使用虚拟地址的一大优点,对于需要同时运行多个程序的系统来说,每个系统都可以随意使用地址空间,不需要考虑地址的限制。以及保护(同一虚拟地址可以转换为不同物理地址)和共享(不同虚拟地址可以转换为统一物理地址) +### 地址转换 +目前虚拟存储器通常使用请求分页(demand page)的方式实现,将虚拟内存按页划分,典型的是4KB,物理地址空间按同样大小划分,称为frame。 +对4KB大小的页,页内索引需要12位,即VA[11:0],称为page offset,剩余部分可以看做是页编号VPN(virtual page number)。相应的PA[11:0]是frame offset,剩余部分PA[:11]是PFN(physical frame number)。从虚拟地址到物理地址的转换时间上就是VPN到PFN的转换。 +#### 单级页表 +当要访问的page,没有与物理内存中的某个frame形成映射关系时,即需要的内容不在物理内存中,此时发生缺页异常(page fault),需要将内容从硬盘加载到内存中。 +操作系统如何知道page和frame的对应关系?目前普遍使用表格存储VPN到PFN的映射,称为页表(page table,PT),一般存放在物理内存中。 ++ 每个程序都有一个张页表,页表在物理内存中的首地址存放在页表寄存器中 ++ 页表与cache不同,其包含了所有VPN的映射关系 ++ 操作系统会在不同进程之间进行切换,在当前进程被替换掉之前,需要保存其状态,对于页表来说只需要保存页表寄存器就好 ++ 页表存在于物理内存中,因此一次取数据需要访问两次内存,第一次是访问页表完成VPN到PFN的转换,第二次是用物理地址来访问内存 + +一张页表存储了所有VPN的映射因此,对32位处理器,其虚拟空间为4GB,假设页大小为4KB,则VPN编号为VA[31:12]共20位,则一个页表有$2^{20}$项,假设每项占32位,则页表大小为4MB。而对64位处理器,一张页表达到了PB大小,这已经超出了物理内存的大小。而且大多数程序也不会用光虚拟空间,只会使用一小部分,页表项大多都是空的。解决方法就是多级页表 +#### 多级页表 +通过引入多级页表,页表也无需占用一块连续空间。页表中的一项称为PTE(page table entry) +多级页表的引入,使得访问数据需要的访存次数增加 +#### 缺页异常 +缺页异常通过是由软件来处理 ++ 这是因为发生缺页是需要将数据从磁盘搬运到内存,读磁盘所需时间对异常处理程序来说绰绰有余。 ++ 处理过程中可能需要进行页替换,使用软件来处理更加灵活,可以按需选择合适的替换策略 + +发生缺页异常时,如何知道虚拟地址中的一个页在硬盘哪个位置? ++ 操作系统会为每个进程的所有页在硬盘中开辟一块空间(swap空间),同时建立一张表保存每个页在硬盘中的位置 +缺页处理可能会发生页替换,为了保证一致性,对页表项添加一个脏位,此外为了实现LRU替换策略,添加一个使用位,当使用位为1说明该页最近访问过,操作系统会周期性将使用位置0. +缺页异常的硬件支持: ++ 缺页发生时,硬件会产生相应的异常,并跳转值异常处理程序 ++ 脏位 ++ 使用位 + +除此之外还可以通过在PTE中添加AP来实现访问控制,添加cacheable来表示是否允许被缓存 + +### 引入TLB和cache +#### TLB +多级页表节省了页表的存储空间,但是也增加了防存次数。因此引入缓存思想,对最近访问过的PTE(页表项)进行缓存,这个缓存被称为TLB,其本质就是页表的cache。不过其只有时间相关性。 +现代处理器通常采用二级TLB,第一级是哈佛结构即分为指令TLB和数据TLB,一般全相连,容量不大,第二级则是组相连模式。 + +#### TLB缺失 +TLB是页表的一个子集 +1. 虚拟地址对应的页不在物理内存中,此时页表中对应的PTE是空的,所以TLB中也一定不会有相关信息 +2. 虚拟地址对应的页已经在物理内存中,则页表中对应PTE是有效的,但可能该PTE不在TLB中。 + +要解决TLB缺失,本质是从页表中找到相应PTE,将其写入TLB中,这个过程叫Page Table Walk。该过程可以用硬件实现,也可以用软件实现。 +1. 软件实现:发生缺失时,硬件将产生缺失的虚拟地址保存到一个特定寄存器中,同时产生一个TLB miss 异常,然后执行异常处理程序,这个过程中可能还会出现缺页异常。软件的优点就是灵活,可以调整TLB写入换出的算法。软件实现中,当发生TLB miss时,流水线中的指令需要清空用于执行异常处理程序,有一个流水线清空和恢复的操作,流水线越深,清空的指令越多。 +2. 硬件实现:一般由内存管理单元MMU实现。好处就是不需要清空恢复流水线 + +一种LRU近似算法:假设TLB使用全相连,使用一个计数器每周期加一,计数器宽度为TLB表项数目,当TLB表中的项目需要被替换时,就以此时计数器中的数作为要被换出的表项编号。 +#### TLB的写入 +TLB与页表的一致性。 +在对物理内存进行页替换时,需要检查被替换页PTE的脏位,如果是1,需要将被替换页内存写回硬盘,如果是0可以直接替换。 +而引入了TLB后,就产生了TLB中表项与PTE的一致性问题。load和store命令都会先去查看TLB,如果存在对应的项,则会将use bit置1,此外store指令会将脏位变为1,这时TLB与页表就产生了不一致。如果使用写回策略,那么知道该TLB表项被替换才会更新页表,在此之间如果系统发生了页替换,系统可能以为该内存页没有更新而直接覆盖。 + +解决方法:页替换发生在page fault时,在进行页替换前,先将TLB写回页表,然后再执行替换 + +## 进程调度 +### 批处理系统的调度 +特点:计算为主,少量I/O +#### 约束条件 +1. 每个进程同时到达。 +2. 进程的执行时间是已知的。 +3. 进程在整个执行过程期间很少执行I/O操作。 +4. 进程在执行过程中不会被抢占。 + + +#### 性能指标 +周转时间(turn around),即进程完成时间(completion)与进程到达时间(arrival)的差值: +$T_{turn} = T_{complete} - T_{arrive}$ +平均周转: +$T_{avg} = \frac{1}{n}\sum T_{turn}$ + +#### 先来先服务FIFO + +通常使用队列来实现: ++ 就绪队列 ++ 阻塞队列 + +优点: ++ 实现简单 ++ 公平 + +缺点: ++ 如果长任务先到,那么短任务会被迫等待很长时间,使得平均周转时间变长 + +#### 最短作业优先SJF +在约束条件下,如果把平均周转时间作为唯一的性能指标,那么SJF是一个最优调度算法。 +### 交互式系统的调度 +#### 约束条件 +1. 每个进程可不同时间到达。 +2. 每个进程的执行时间不同。 +3. 进程的执行时间是已知的。 +4. 进程在整个执行过程期间会执行I/O操作。 +5. 进程在执行过程中会被抢占。 + +#### 性能指标 +目标是提高用户的交互性体验和减少I/O响应时间 +响应时间(response time),即从程序到达到第一次被执行的时间 + +#### 最短完成时间优先(STCF) ++ 在支持抢占式的系统上,可以改善周转时间 ++ 但对于响应时间,FIFO/SJF/STCF都没用特别好的效果 +#### 基于时间片的轮转 ++ 每个任务轮流占用CPU一段时间 ++ 时间片越短,任务得到响应越快,但存在进行切换开销 ++ 任务越多,轮到下一次执行的时间越长,系统平均周转时间也会变长 + +### 通用计算机系统的调度 +#### 约束条件 +1. 每个进程可不同时间到达。 +2. 每个进程的执行时间不同。 +3. 进程的启动时间和执行时间是未知的。 +4. 进程在整个执行过程期间会执行I/O操作。 +5. 进程在执行过程中会被抢占。 +#### 固定优先级的多级无反馈队列Multi-level Feedback Queue +一般CPU密集型优先级低,I/O密集型优先级高 +1. 如果进程PA的优先级 > PB的优先级,抢占并运行PA。 +2. 如果进程PA的优先级 = PB的优先级,轮转运行PA和PB。 + +难点: ++ 无法预估程序的类型 ++ I/O密集程度多样 +#### 可降低优先级的多级反馈队列 ++ 动态调整进程的优先级 ++ 操作系统首先需要以优先级的数量来建立多个队列。当然这个数量是一个经验值,比如Linux操作系统设置了140个优先级。 ++ 优先级的变化是单向的,一个进程的优先级只会持平/降低 ++ 低优先级可能会一直无法拿到CPU,产生饥饿现象 + +如何提升优先级 ++ 定期统计进程在就绪态/阻塞态的等待时间,等待时间越长,其优先级的提升度就越高 \ No newline at end of file diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-hxingjie.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-hxingjie.md" new file mode 100644 index 0000000000..f415e1f240 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-hxingjie.md" @@ -0,0 +1,61 @@ +--- +title: 2024秋冬季开源操作系统训练营第二阶段总结报告-hxingjie +date: 2024-11-06 15:18:43 +categories: + - summary report +tags: + - author:hxingjie + - repo:https://github.com/LearningOS/2024a-rcore-hxingjie +--- + + +## 一、前言 + + 在过去两周,我学习了rCore操作系统。通过阅读实验指导书,我跟着操作系统的发展历程,学习了批处理系统、多道程序与分时多任务、虚拟地址空间、进程管理、文件系统、进程通信和并发等核心概念,并对rCore的实现有了更深入的认识。以下是我对这两周学习过程的总结。 + + +## 二、学习内容 + +1. 搭建执行环境: + + 学习了平台与目标三元组,理解了应用程序执行环境。 + +2. 批处理系统: + + 学习了批处理系统的基本原理,包括作业调度、作业执行过程。 + +3. 多道程序与分时多任务: + + 掌握了多道程序设计的基本概念,以及如何实现分时多任务调度。 + +4. 虚拟地址空间: + + 理解了虚拟内存的概念,包括页表、地址映射。 + +5. 进程管理: + + 学习了进程的管理、调度,更加深入的理解了fork+exec。 + +6. 文件系统: + + 掌握了文件系统的基本结构,包括目录、文件。 + +7. 进程通信: + + 学习了父子进程之间的通信机制——管道。 + +8. 并发: + + 学习了线程管理机制的设计与实现,理解了同步互斥的实现,包括锁、信号量、条件变量。 + + +## 三、学习心得 + + Rust语言的安全性和性能在rCore开发中得到了充分体现,经过阅读系统的源码以及作业的编写,对rust语言在内存安全和并发控制方面的优势有了更深的理解,第一阶段学习rust的很多疑问也得到了解答。 + + 通过学习rCore,我对操作系统的原理有了更深入的理解,虽然考研的时候较为系统的学习了操作系统的知识,但是基本上还是停留在理论知识方面。这次rCore学习之旅,我获取PCB对进程进行操作、实现课本上学习过的系统调用、深入汇编代码理解什么是 '陷入' ,让我对操作系统的设计理念、计算机的体系结构有了具象化的认识。 + + 在学习过程中,我也遇到了许多挑战,包括环境搭建遇到报错、Rust基础不够牢固导致代码编写举步维艰等,但通过不断解决这些问题,我的编程能力和问题解决能力得到了显著提升。 + + 两周的rCore学习之旅让我受益匪浅。通过学习rCore,我对操作系统的设计和实现有了更深刻的认识,同时也提升了我的编程技能。我相信,这些知识和经验将对我未来的学习和职业发展产生积极影响。 + diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-rqj.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-rqj.md" new file mode 100644 index 0000000000..d55396b286 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223\346\212\245\345\221\212-rqj.md" @@ -0,0 +1,35 @@ +--- +title: 2024秋-二阶段总结-rqj +date: 2024-11-09 14:34:47 +categories: + - 2024秋冬季开源操作系统训练营 +tags: + - author:ghj1222 + - repo:https://github.com/LearningOS/2024a-rcore-ghj1222 +--- + +本人算是对操作系统比较熟悉,之前做过 xv6-riscv 实验,这次体验了 Rust 的操作系统试验。 + +# ch3 + +ch3 可以说是 rCore 的入门题,帮助上手 rCore 的代码以及 rust 系统编程,还帮助熟悉 risc-v 的特权级指令。 + +# ch4 + +ch4 主要是熟悉了操作系统的内存管理,从此,用户的地址空间和内核的地址空间隔离开,同时开启了页表。 ch4 实验重点是在内核和用户空间传输数据,因为内核是恒等映射,而用户空间开启了分页。如果二者没有共用页表,那么就需要手动模拟地址转换,并且还需要按照分页去写内存,因为一次返回的数据可能在不同的页中。此外还实现了简单的空间分配和管理,实现了简单的 mmap 和 munmap。 + +# ch5 + +ch5 主要实现的进程管理和进程调度。把 fork 和 execve 融合魔改出来一个 spawn 能加深对 fork 和 execve 的印象。 + +# ch6 + +ch6 是完善 easy-fs 的功能,但是我认为 easy-fs 没有解析路径、构建树形目录结构不能让学生了解到一个功能比较正常的文件系统的样子,而在此过早将 easy-fs 与其它内核模块解耦合也带来了增加功能和 debug 上的困难。 + +# ch8 + +ch8 要求实现死锁检测算法,但是传统的银行家算法要求预先知道各个线程需要的资源,并且没有考虑线程因等待资源而阻塞的情况,这与现实的操作系统有出入。在现实中,可以认为阻塞的线程所需要资源为它所需的全部资源,将检测的申请资源调用后的状态作为最终状态,只需要检测此次申请是否可能死锁。 + +# 总结 + +对 rCore 的学习与实践更深入了解了操作系统的基本原理,也锻炼了 rust 代码能力。 diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-n1ptune.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-n1ptune.md" new file mode 100644 index 0000000000..8408a03bad --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-n1ptune.md" @@ -0,0 +1,15 @@ +--- +title: 2024秋冬季训练营第二阶段总结-n1ptune +date: 2024-11-10 12:58:08 +tags: + - author: n1ptune + - repo: https://github.com/LearningOS/2024a-rcore-n1ptune +--- +## 第一阶段 +学习rust基本语法,了解rust相对于其他语言的优越性 +## 第二阶段 +ch3 实现sys_get_task_info +ch4 实现mmap和munmap +ch5 实现sys_spawn和stride调度算法 +ch6 实现sys_linkat、sys_unlinkat、sys_stat +ch8 实现sys_enable_deadlock_detect \ No newline at end of file diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-aka-adi.md" "b/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-aka-adi.md" new file mode 100644 index 0000000000..db198dae3d --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-aka-adi.md" @@ -0,0 +1,24 @@ +--- +title: 2024秋冬开源操作系统训练营一二阶段总结-aka-adi +date: 2024-11-10 14:25:08 +tags: +--- +## 第一阶段 +通过rustlings学习rust的概念,相比于单纯的阅读更能让使用者明白rust的意义和具体的用法。在rustlings的完成过程中,越做越觉得rust在解放心智上的好处。 + +当然,在完成过程中我也发现仅仅完成rustlings是不够的,学习rust还需要更多的实践和思考。 + +## 第二阶段 +第二阶段为在实验指导书的引导下熟悉rcore,并尝试编写一些内核功能。这一阶段重点在于对内核的熟悉程度,只有在对已有代码的熟悉之上才能比较顺利的完成任务。 + +### ch3 +该实验着重于记录信息,只需对内核相关代码了解即可。如知道何时发生系统调用 +### ch4 +该实验引入了页表,需要对memory_set相关函数熟悉才能从分配内存的细节中抽身,减轻负担 +### ch5 +spawn只需熟悉exec即可写出 +stride算法实现上容易,更多可以关注正确性的证明等 +### ch6 +该实验引入文件系统,需要在理清文件系统中inode,目录项,数据node的关系后再进行编码实现 +### ch8 +该实验更多关注死锁检测本身,不需要太多对内核的熟悉,实现银行家算法即可 \ No newline at end of file diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-lzh.md" "b/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-lzh.md" new file mode 100644 index 0000000000..4072bcbbb1 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-lzh.md" @@ -0,0 +1,35 @@ +--- +title: 2024秋冬开源操作系统训练营一二阶段总结 +date: 2024-11-10 22:39:56 +categories: + - 2024秋冬开源操作系统训练营 +tags: + - author:RedFlagLee + - repo:https://github.com/LearningOS/2024a-rcore-RedFlagLee + - rust + - rcore +--- +首先非常感谢训练营的主办方为我们提供了交流的平台和详细的文档,也非常感谢群里的助教和各位大佬们。作为一个已经工作了几年的人,如果没有这次训练营的机会,我很难相信自己能把操作系统重新捡起来并坚持学习。 + +## 第一阶段 + +这个阶段我主要是通过rustlings来零基础学习rust语法,主要参考的的书籍有《Rust圣经》和《Rust程序设计》。感觉rustlings有些过于简单,只能用来粗略地学习一下语法,通过了也谈不上熟练,我就在后面的项目编程中因此浪费了大量的时间去调试一下基础的rust语法问题,后期准备通过斯坦福的cs110l课程来加深下对rust在内存安全方面的理解。此外,在数据结构和算法部分我也主要靠chatGPT提供思路,很受打击,未来准备在leetcode上多刷下题来长长见识。 + +## 第二阶段 + +再次感谢文档的详细和版本划分的合理,本来之前停留在课本上的内存划分、进程调度、cache、并发等概念都变得触手可用,操作系统的迷雾总算被拨开了一角。 + +ch1里主要是摆脱了标准库依赖构建了祼机执行环境。通过qemu模拟器加载了最初版本的内核并在屏幕上输出了文字,我第一次感觉到了操作系统其实也和普通的应用程序一样,克服了畏难情绪。在这里也对第一性原理也了进一步的了解,再复杂的程序它的最初版本也是比较精简和易于理解的,从能完成最小功能的初始版本开始,会更有利于进一步学习其它更加抽象的概念。 + +ch2里的难点在于汇编知识和链接器的使用。我以前对于链接器的认识仅限于使用c++第三方库,但ch2里对于link_app.S的使用让我大开眼界,原来程序链接时每个段的处理可以这么灵活。同时,我也学到了在rust里通过extern c引入外部汇编文件定义的符号,可以直接拿到内存地址。我之前没有学过riscv架构,未来准备通过《RISC-V体系结构编程与实践》系统学习。 + +ch3里的难点在于汇编写的_switch在任务切换里的作用,例如保存寄存器、切换栈、切换控制流等。内核通过内嵌ecall汇编指令来引发trap异常陷入S特权级。抢占式调度里让我认识了时钟中断,原来轮转调度里的时间片就是通过定时器来触发时钟中断,进行任务切换。 + +ch4里内存地址空间我认为是最有趣也是最难的部分。通过为用户和内核单独实现的地址空间,解释了虚地址和物理地址的由来。为了实现地址空间,rcore里设计了大量的数据结构,重点要掌握MapArea和MemorySet里接口的使用,查找页表、生成地址空间等核心功能都在里面实现。 + +ch5里进程的精华主要在于fork和exec等系统调用的实现,本章还实现了一个shell程序,让我理解到了进程怎么从一个程序通过fork和exec运行其它程序的。 + +ch6里是文件的实现,ch8里是并发,我对这两章的理解不太深,主要还是围绕测试案例来理解的,后续还要再反复多次的看看文档。 + +现在非常期待下一阶段ArceOS的学习,希望能成为我入门hypervisor的阶梯!!! + diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Artoria.md" "b/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Artoria.md" new file mode 100755 index 0000000000..b170b7a1ef --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-Artoria.md" @@ -0,0 +1,92 @@ +--- +title: 2024秋冬开源操作系统训练营第一二阶段总结-Artoria +date: 2024-11-09 18:30:37 +tags: + - author:Artoria + - repo:https://github.com/LearningOS/2024a-rcore-Artoria302-1 +--- + +## 第一阶段 + +工作中已经写了一年多的Rust代码,体验确实不错,特别是完善的包管理工具,大大减少了编译时的心智消耗,所有权和借用系统并没有很复杂,写一阵代码自然就熟悉了 + +第一阶段算是再巩固下基础,数据结构题比较有意思,不过深入学习还是要抽空看看[链表](https://rust-unofficial.github.io/too-many-lists/) + +## 第二阶段 + +对操作系统很感兴趣,尤其是第三阶段的虚拟化方向。由于平时工作比较忙,也就晚上和周末抽时间抓紧写写代码,下边主要总结下各个章节产生的疑问和实验遇到的问题吧 + +### lab1(ch2,ch3) + +主要熟悉了应用加载,中断,用户栈/内核栈切换,任务切换的机制,再是学习了下`risc-v`寄存器以及汇编方面的知识,受益匪浅 + +实验比较简单,主要熟悉下`syscall`的开发流程 + +### lab2(ch4) + +本章新增了地址空间,内核和应用通过页表机制把虚拟内存映射到实际的物理内存,虚拟内存使得不同应用都可以使用连续独立的地址空间,并做到了不同应用之间的隔离 + +`strampoline`段用于保存陷入内核和返回用户态的代码,并通过把内核和应用的`strampoline`设置成相同地址的虚拟页(最高页),使得切换地址空间之后,这段代码地址可以无缝衔接 + +`TrapContext`紧贴着`strampoline`位于应用的次高页(看起来没必要一定要在次高页)。`TrapContext`用于保存上下文切换时的寄存器以及`kernel satp`和`kernel sp`等。`TrapContext`位于用于应用地址空间是因为,切换上下文时只有一个`sscratch`寄存器可以用于周转,而如果`TrapContext`位于内核栈(内核栈不是恒等映射),那就只需要先通过`sscratch`得到`kernel satp`切换到内核地址空间之后才能访问`TrapContext`,这时就没有额外寄存器获得`kernel sp`也就拿不到`TrapContext`地址。如果位于应用地址空间,就可以通过`sscratch`寄存器保存`TrapContext`地址,在陷入内核后,先在应用地址空间保存好上下文之后,再切换到内核地址空间 + +实验需要注意的点: + +- mmap页面要4k对齐,空间左闭右开 + +- syscall复制结构体时,需要通过`translated_byte_buffer`拿到用户态结构体对应的物理地址(这部分内存在内核是恒等映射的,可以正确写入),再复制 + +### lab3(ch5,ch7) + +这两章主要讲了进程和进程通信,是概念相对简单的章节,难点主要在于调度算法 + +实验是实现spawn,总体来说是new+fork,不复制地址空间,但继承父子关系 + +### lab4(ch6) + +本章学习了文件系统,代码中数据结构依赖关系相对复杂,画图梳理下比较方便理解 + +实验中`nlink`需要要保存在`DiskInode`中,同时减少`INODE_DIRECT_COUNT`保证`DiskInode`只占用`128bytes` + +这里我把`file_count`也存下了,方便`unlink`时候,直接把最后一个`DirEntry`替换到被删除的`Entry`位置。其次可以用`union`保存`nlink`和`file_count`来节省空间,毕竟文件没有`file_count`,目录不允许有`link`,这里偷懒了 + +```rust +const INODE_DIRECT_COUNT: usize = 26; + +#[repr(C)] +pub struct DiskInode { + pub size: u32, + pub nlink: u32, + pub file_count: u32, + pub direct: [u32; INODE_DIRECT_COUNT], + pub indirect1: u32, + pub indirect2: u32, + type_: DiskInodeType, +} +``` + +一定要注意锁不能重入,在开发`unlinkat`时,因为调用其他也加文件锁的函数导致了死锁,被卡了有一阵 + +### lab5(ch8) + +本章主要讲了内核中线程和锁的实现机制 + +下面有几个注意点 + +- 调用`schedule`切换线程前一定要手动`drop`资源,否则会造成资源泄露或`panic` + +- `exit_current_and_run_next`在调用`schedule`之前分配了`TaskContext::zero_init()`作为dummy TaskContext,开始还想着这个线程不会再恢复了,那这个栈空间在后边是如何保证地址合法并最终回收的,而这就是`sys_waittid`存在价值之一 + +- 代码中的`PROCESSOR`并不是`thread_local`的,但有很多`exclusive_access`的调用,可能教学用的rCore并不存在多个cpu,这里暂时不需要考虑这个问题 + +```rust +lazy_static! { + pub static ref PROCESSOR: UPSafeCell = unsafe { UPSafeCell::new(Processor::new()) }; +} + +pub fn current_task() -> Option> { + PROCESSOR.exclusive_access().current() +} +``` + +实验是死算检测,并不复杂,按着文档来就好 diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-FunCheney.md" "b/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-FunCheney.md" new file mode 100644 index 0000000000..0d0b074af7 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-FunCheney.md" @@ -0,0 +1,43 @@ +--- +title: 2024秋冬开源操作系统训练营第一二阶段总结-FunCheney +date: 2024-11-11 07:40:28 +categories: + - 2024秋冬季季开源操作系统训练营 +tags: + - author:FunCheney + - repo:https://github.com/LearningOS/2024a-rcore-FunCheney + - ... +--- + +### 第一阶段 + +之前在公司工作的时候就了解到了 rust,工作中客户端使用 rust,当时觉得 rust 离后端(服务端)比较远,就错过了 rust 的深入了解与学习。 +在这几年的工作与学习中,慢慢接触到rust在操作系统的领域也在使用。于是准备学习一下这门新的语言。自己也想对操作系统相关方面做一些 +更加深入的了解。 + +一次偶然的机会在github 上看到了 rcore,用 rust 写操作系统,这个和自己的需求完美契合了。。。 + +本课程第一阶段采用 rustlings,在线测评,然后对应的知识点也会有出处。是一个非常好的学习工具,但是整体来说数据结构部分相对较难,需要 +一些相关数据结构的基础,以及对 rust 语法的了解。 + +自己对 rust 相关的一学习的例子与记录: https://github.com/FunCheney/Fos/tree/main/rust-study + +整体感觉: +1. 语言的学习还是要多动手练习,多写代码才能理解 +2. 数据结构与算法相关的章节还需要持续的练习 +3. 后续还要安排并发相关的学习与记录:https://github.com/FunCheney/Fos/tree/main/rust-study/rust-atomics-and-locks + +### 第二阶段 + +整体来时实验教程偏简单且大多都一笔带过。学习的过程中主要还是参考: rCore-Tutorial-Book 第三版! + +因为在训练营开始之前我就已经在通过看: rCore-Tutorial-Book 以及抄 https://github.com/rcore-os/rCore-Tutorial-v3 中每一章节相关的代码, +因此在训练营阶段也在抄其中代码,在自己的仓库里面手写了一遍相关代码。 + +对 risc-v 精简指令架构有了一些了解:https://github.com/FunCheney/Fos/tree/main/code/asm 主要代码实现。 +整体感觉: +1. risc-v 的指令架构没有 intel x86 指令复杂。 +2. 文件系统,与锁相关的实验部分用时教程,偏难。 +3. rust-sbi 和 qemu 会简化很多汇编的相关的工作。降低了对操作系统启动记载相关的一些理解门槛,之间看过 linux 0.11 相关的启动逻辑,还是比较复杂。 +4. 通过在线测评的方式,通过一些测试用例可以起到检测的收手段。比自己写代码的时候(尤其是还在学习过程中)不知到对错会更有方向。但是还是缺少一些 + 最佳的实现知道,因为不知道自己过了是否就是好的编码方式,好的实现思路。 diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-\345\206\257\344\272\210\350\275\251" "b/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-\345\206\257\344\272\210\350\275\251" new file mode 100644 index 0000000000..9ae6a3ae38 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-\345\206\257\344\272\210\350\275\251" @@ -0,0 +1,10 @@ +--- +title: 2024秋冬开源操作系统训练营第一阶段总结-冯予轩 +date: 2024-11-10 13:18:10 +categories: + - blog +tags: + - author:Murrrphy +--- + +最初是从学校中了解到这个开源操作系统项目的,首先是这种以做促学的形式吸引了我。以往学习一种新语言可能存在一个问题,就是实践总结的过程不够,以至于“一看就会,一做就废”。rustlings的测评方法十分的方便,在过程中我也学习了rust course、rust by example等教材,并逐渐意识到rust的方便之处。通过这一阶段的学习,我也是较为熟练的掌握了rust的基本语法和一些关于cargo的用法等,并开始慢慢喜欢上了rust diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-\346\235\250\345\255\246\350\201\252.md" "b/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-\346\235\250\345\255\246\350\201\252.md" new file mode 100755 index 0000000000..e948f88f0f --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223-\346\235\250\345\255\246\350\201\252.md" @@ -0,0 +1,36 @@ +--- +title: 2024秋冬开源操作系统训练营第一阶段总结-杨学聪 +date: 2024-11-10 09:57:10 +categories: + - summary +tags: + - author:dosconio +--- + +After the first stage of the 2024 autumn winter open source operating system training camp, I would like to share my experience and summary with you. + +It was the training camp that made me touch the magic programming language -- Rust. + +In the stage, I learned the basic knowledge of Rust, and met a lot of like-minded students. + +Before, besides assembly, I write the documents with only C series languages (C, C++ and C#). I was not able to imagine how the "memory-safety" would be. But after the touching the Rust, I found that the Rust is not only a simple language. Rust can check your code in compile time, considering the using for multi-thread, memory-leak, and the optimization in general. + +Below are the impressive places that Rust has left me: + +- `unsafe` keyword: Like C#, Rust has a `unsafe` keyword, which is used to mark the code, which cannot be assured by the compiler. This enables us plug in the special code, solving the possible lack for the implementation of the current supply. + +- `ownership` and `lifetime` system: Rust has a very special `ownership` system, which is different from the C series language. It can help us to avoid the memory leak or multi-reference. In other languages, we may forget to consider the mechanism about variable usage. + +- `trait`: Rust has a very special `trait` system, which is similar to the interface in C#. It can make the code more flexible and reusable. We can avoid pure-virtual class in C++, which may import v-table making us uncomfortable. + +- default immutable: This encourages me to delare more variable when writing codes, which makes code more readable. + +However, there are still a lot I need to learn. In fact, I have not mastered standard C and C++ yet. This let me have a weakness in computer programming -- I cannot consider from all sides to output a great code. + +In the future, I'd like to study the Embedded-development relatived knowledge of Rust. + +Thank you. Thanks to teachers and the people who have helped me. + +*Dosconio*. + +2024AD Nov 10, Sun. diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\345\206\257\344\272\210\350\275\251" "b/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\345\206\257\344\272\210\350\275\251" new file mode 100644 index 0000000000..1f32bce023 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\345\206\257\344\272\210\350\275\251" @@ -0,0 +1,12 @@ +--- +title: 2024秋冬开源操作系统训练营第二阶段总结-冯予轩 +date: 2024-11-10 13:18:10 +categories: + - blog +tags: + - author:Murrrphy +--- + +在结束第一阶段的学习后,我进入了第二阶段的做题研究。老实说刚开始有点迷茫,对庞大复杂的代码不知道怎么下手。虽然在学校之前也上过操作系统课,但实际实现起来要比课本上要复杂得多。在刚开始的三章我卡了很久,但借助思维导图和笔记,我逐渐搞清楚了整个代码框架的逻辑和常用模块等,到后面做题速度也是越来越快。我印象最深的是ch5,那时我刚从ch4的虚拟地址空间中大彻大悟,ch5从开始接触到完全通过测例就用了2天不到,当时还是十分有成就感的。 +做题过程中,我总是思考rust的安全性设计。由于其严格的语法规范,有很多运行时可能才会出现的问题可以在编译阶段就被发现。这一方面可以提高代码的安全性,一方面也能很好培养programmer的编程思维。 +在整个第二阶段的过程中,我也遇到了很多志同道合的学长、前辈,也得到了一些他们热心的帮助。总之,第二阶段的学习让我收获了很多,也见识了很多,十分感谢主办方给了我这样一个良好的学习机会。 diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\346\235\250\345\255\246\350\201\252.md" "b/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\346\235\250\345\255\246\350\201\252.md" new file mode 100755 index 0000000000..0965c01aa8 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223-\346\235\250\345\255\246\350\201\252.md" @@ -0,0 +1,34 @@ +--- +title: 2024秋冬开源操作系统训练营第二阶段总结-杨学聪 +date: 2024-11-10 09:57:29 +categories: + - summary +tags: + - author:dosconio +--- + +After the second stage of the 2024 autumn winter open source operating system training camp, I would like to share my experience and summary with you. + +This is the twice time I participated in the training camp, and this time I mainly learned about the basic knowledge of operating systems, including the process of operating system development, the structure of operating systems, and the basic principles of operating systems. + +In the past years, I have spent a lot in operating system for x86 boards, including assembly, multi-language, segmentation, paging and multitask, which covers the real mode and protected mode. + +It was the training camp that made me touch the RISC-V64 architecture. + +I love RustSBI, but it made me kind of confused. I cannot make sense of the base implementation of the system, although it may be all right to depend on it. + +With RustSBI, I easily realised the TIMER interrupt. This makes OSDev much easy! In x86 board, we need to write the control words to the PIC/RTC peripheral. + +We used Trampoline to implement the syscall, exception, interrupt and other traps. This is very smart and comfortable. And this is the only assemly code in the whole project. + +I also had the problem to treat the paired-notions like: "task" and "thread", "process" and "task". + +In general, I have had a better understanding of operating systems development, RISC-V64 architecture, Rust and QEmu Simulator. + +In the future, I'd like to rewrite my [MECOCOA](https://mecocoa.org) project, which make architecture "x86" and "Risc-V64" harmonious. + +Thank you. Thanks to teachers and the people who have helped me. + +*Dosconio*. + +2024AD Nov 10, Sun. diff --git "a/source/_posts/2024\347\247\213\345\206\254\350\256\255\347\273\203\350\220\245Blog-\351\230\266\346\256\265\344\270\200\346\200\273\347\273\223-\345\255\231\345\256\207\350\210\252.md" "b/source/_posts/2024\347\247\213\345\206\254\350\256\255\347\273\203\350\220\245Blog-\351\230\266\346\256\265\344\270\200\346\200\273\347\273\223-\345\255\231\345\256\207\350\210\252.md" new file mode 100644 index 0000000000..57b455584c --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\350\256\255\347\273\203\350\220\245Blog-\351\230\266\346\256\265\344\270\200\346\200\273\347\273\223-\345\255\231\345\256\207\350\210\252.md" @@ -0,0 +1,21 @@ +--- +title: 2024秋冬训练营Blog-阶段一总结-孙宇航 +date: 2024-11-11 12:55:19 +tags: +--- +# Rustlings总结 +## 心路历程 + 这个训练营我也算是“老兵”了,不断地入门再入门,到终于下定决心,投入大量的时间来完成这个训练营,真的经历了很多很多,有着太多的心路历程,从想着三天写完,慢 +慢的查漏补缺,温习之前的知识点,慢慢的开始一道题一道题手写rustlings... + 我总是说我基础很差,并拿这句话当做挡箭牌,最终一事无成。未来会更好吗?但是我已经有点厌倦痛惜过去了。 +说起来这也是我第三次的写rustlings了,开始越来越熟练了,这当然是开心的,不过后面有这更多的更艰难的挑战,阶段二,阶段三,都是未知的大山,更艰难的挑战。 +## 学习由来 + 对于这个训练营,我是大二机缘巧合开始接触到的,那时候听学长说,特别有含金量,可以“咸鱼翻身”,那可是清华啊,多么具有神话幻想意味的大学,那时的我突然想着, +我一定能做出一番令人羡慕的成就。 + 但是那时的我用两个字来概括是“摆烂”,在羡慕同学取得的成就和奖项,和自己什么事也不做只打游戏的情况下还希望着未来自己能有一番大成就,如今看来竟全是一片混 +浊,在孤独和幻想中,我度过了大学的大半时光,我的性格是卑劣的。 + 但是未来的路仍旧一片黑暗,载着家人的希望,我在原地自顾自的打转,跳不出自己的镣铐,做不到的仍然做不到,幻想的事物越发离谱。 + 慢慢得走来,我只剩了这个训练营了,又是另一个未完待续。 + 我可以做到吗?我问过了很多人,很多未曾谋面的人,陌生的人对我的态度竟大都是积极的,他们不了解我。我也正在逐步了解自己。忘记了太多的事情,记忆也越来越差,但 +是从哪个角度来说,我这次真的想完成这次训练营,即使我每次都这么说... + 祝武运隆昌。 \ No newline at end of file diff --git "a/source/_posts/2024\347\247\213\345\206\254\350\256\255\347\273\203\350\220\245Blog-\351\230\266\346\256\265\344\272\214\346\200\273\347\273\223-\345\255\231\345\256\207\350\210\252.md" "b/source/_posts/2024\347\247\213\345\206\254\350\256\255\347\273\203\350\220\245Blog-\351\230\266\346\256\265\344\272\214\346\200\273\347\273\223-\345\255\231\345\256\207\350\210\252.md" new file mode 100644 index 0000000000..5672e95b22 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\350\256\255\347\273\203\350\220\245Blog-\351\230\266\346\256\265\344\272\214\346\200\273\347\273\223-\345\255\231\345\256\207\350\210\252.md" @@ -0,0 +1,10 @@ +--- +title: 2024秋冬训练营Blog-阶段二总结-孙宇航 +date: 2024-11-11 12:55:40 +tags: +--- +# 专业阶段 +这个阶段给我最大的感受是学习,因为很多函数都被事先书写好了,这几个实验基本都是在已有的框架上进行相关的函数调用即可,在明白每个函数的实现逻辑以及每个lab实验要实现的功能即可完成实验。同时这个阶段给我的感受是有许多深层次的知识仍需要学习,有许多一知半解的知识需要实践,看到学习群里面大家对于实验的讨论的诸多见解,受益匪浅,完完全全的拓宽了我的眼界,许多从未听过的名词出现在我的面前,只感到纸上得来终觉浅,绝知此事要躬行。可惜时间所迫,没办法对于每个lab实验进行进一步的深究。 +第二阶段我重点理会到了操作系统的一步步发展以及实现的功能,在进行每个实验的时候,感受到学校里面的讲解十分片面,并且浅薄(也是我没有认真学习的缘故,学校里面的课检验的只有期末突击而已)。 +非常重要的一点是这次的理论和实践一起进行,让我极大的认识到了抽象和具体之间的联系,有时候理论上很复杂并且难以理解的东西,转化到实践上面竟然可能只是一个数组,一个函数栈而已,这让欠缺实践的我大开眼界。 +非常期待第三阶段带来的挑战,也非常希望可以通过第三阶段(哈哈哈)。 \ No newline at end of file diff --git "a/source/_posts/Zxx\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223.md" "b/source/_posts/Zxx\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223.md" new file mode 100644 index 0000000000..b4e519d9e9 --- /dev/null +++ "b/source/_posts/Zxx\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223.md" @@ -0,0 +1,14 @@ +--- +title: Zxx开源操作系统第一阶段总结 +date: 2024-11-10 18:18:49 +tags: + - author:Zia6 + - repo:rust-rustlings-2024-autumn-Zia6 +--- +### 前言 +从零基础开始学习 Rust,通过 Rustlings 逐步入门。Rust 作为一门编程语言,以其优秀的内存安全和高性能设计而闻名,非常值得深入学习。 + +### 个人感悟 +通过 Rustlings 学习了 Rust 的基础语法、数据结构、所有权和引用等核心概念,初步掌握了编写 Rust 程序的基本能力。在接下来的 rCore 学习中,通过实践进一步深化了对 Rust 编程的理解。这种从基础到实践的学习方式非常高效。未来还需要通过更多项目积累,不断提升 Rust 编程能力以及对操作系统的理解。 + +Rust 的所有权机制是学习过程中最具挑战的部分。变量所有权的转移、引用的使用,以及 Copy 和 Clone 等特性的分析,都需要反复实践和大量代码积累,才能在实际编写 Rust 代码时得心应手。 \ No newline at end of file diff --git "a/source/_posts/Zxx\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223.md" "b/source/_posts/Zxx\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223.md" new file mode 100644 index 0000000000..df97639cc7 --- /dev/null +++ "b/source/_posts/Zxx\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\347\254\254\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223.md" @@ -0,0 +1,34 @@ +--- +title: Zxx开源操作系统第二阶段总结 +date: 2024-11-10 18:18:58 +tags: + - author:Zia6 + - repo:rust-rustlings-2024-autumn-Zia6 +--- +### lab1总结 +实验一相对简单。由于尚未实现页表,内核地址空间可以直接访问用户态地址空间,因此系统调用的实现只需直接返回用户态地址即可。此外,为了确保后续实验中的测试通过,建议使用高精度的 get_time_us 获取时间,避免因时间精度不足导致错误。至于统计系统调用次数,可以在 syscall 函数执行时进行更新。若需获取系统第一次调用的时间,则需修改 run_first_task 和 run_next_task。 + +### lab2总结 +与实验一不同的是,实验二中需要实现页表。因此,之前的系统调用逻辑需要重构。内核必须找到传递参数的实际物理地址,并在该地址进行读写操作。 + +此外,实验要求实现两个新的系统调用:mmap 和 munmap。这两个调用涉及一段连续地址,因此需要检查该地址范围是否完全未映射或已映射。mmap 负责插入页面,而 munmap 则用于删除页面。 + +### lab3总结 +在实验三中,首先需要将之前实验的代码引入本次实验。如果之前的代码有问题,本次实验的测试通常也无法通过。 + +本次实验的核心是实现 spawn 和 stride 调度算法。spawn 并非简单的 fork + exec,因此在实现前需充分理解其语义。可以借助 fork + exec 的部分代码来调整 TCB 数据结构至目标状态。 + +stride 是一种进程调度算法,由于本次实验示例较简单,按照教程步骤实现即可完成。 + +### lab4总结 +相比之前的实验,实验四的难度有所增加。首先,需要确保兼容之前的代码。一开始 spawn 的兼容性问题导致多次报错,最终通过重新实现 spawn 才解决。 + +此外,还需注意可能出现的死锁问题。由于某些函数(如 find 和 create)对文件系统加锁,不能直接调用它们,而是需要将其实现逻辑拷贝并进行调整。 + +最后是 unlink 的目录项删除操作。在找到目标目录项后,需要更新内容。采用了群里分享的方法:删除原目录项后,将内容重新拷贝过去。 + +### lab5总结 +实验五的任务是实现一个死锁检测算法,类似于银行家算法。但教程中的描述较为模糊,许多细节未明确说明。因此,需要仔细分析各个细节。sem 和 mutex 的实现思路基本相同。另外,sys_get_time 的实现必不可少,否则某些测试实例会出现死锁。 + +总结 +总体来说,这些实验内容并不复杂,涉及的概念较为基础。但由于是第一次接触 Rust 并使用它编写操作系统,对我而言是一次新奇的体验。实验的主要难点在于 Rust 的使用上。此外,教程的部分内容并不完善,例如死锁检测算法的部分解释得较为模糊。若不是群里有人提到类似银行家算法,我很可能难以理解。因此,希望教程能进一步完善文档内容,帮助更多初学者。 diff --git a/source/_posts/blog-for-yjymosheng.md b/source/_posts/blog-for-yjymosheng.md new file mode 100644 index 0000000000..0df023b8ad --- /dev/null +++ b/source/_posts/blog-for-yjymosheng.md @@ -0,0 +1,51 @@ +--- +title: blog_for_yjymosheng 殷金钰 +date: 2024-11-09 22:34:33 +tags: +--- + +# 阶段一总结 + +## 初次认真学习rust每一个点 ## + +通过某些渠道,第一次了解到操作系统训练营,以前我也有用rust写过一些内容,但严格来说这是我的第一次正式学习rust。学习了很多的内容,了解到了 +闭包,函数式编程,拓展了我的知识面,我第一次见到结构如此清晰的语言。 + +## 所有权的理解 ## + +众所周知,rust很贪。所有权是其中的灵魂。我认为,其本质就是“作茧自缚”,减少了许多东西,又为方便开发与调试添加了许多东西。这些设计减少了许 +多的错误。所以简单的把所有权当作单线程的脑子就行,不要给它过多的行为。 + +## 错误处理中的Option Result ## + + +- Option 用于表达可能存在或者不存在的值,它有两种可能的状态:Some(值) 和 None。当你不确定一个值是否存在时,可以使用 Option 来处理这种情 +况。 + +- Result 则用于表达可能的操作成功或失败的结果,它有两种可能的状态:Ok(值) 和 Err(错误)。当你需要处理可能出现错误的情况时,可以使用 Result 来处理这种情况。 + +## 令人耳目一新的枚举类型 match ## + +在 Rust 中,枚举类型是一种非常强大的数据结构,它可以用来表达一组相关的值。而使用 match 关键字可以让我们更加灵活地处理枚举类型的值,使得 +代码更加清晰易懂。 + +match 表达式可以用来匹配枚举类型的不同变体,并根据不同的情况执行相应的代码逻辑。这种模式匹配的方式让代码的逻辑结构清晰明了,同时也增强了 +代码的可读性和可维护性。 + +## 我的收获 ## + +通过学习 Rust,我收获了很多。不仅仅是语言本身的特性和语法,更重要的是 Rust 给我带来的编程思维方式的转变。在学习 Rust 的过程中,我更加注 +重代码的安全性和可靠性,学会了如何利用 Rust 的各种特性来编写更加健壮的程序。 + +另外,通过与社区的交流和分享,我还了解到了很多其他开发者的经验和见解,这也让我受益匪浅。总的来说,学习 Rust 是一次非常有意义的经历,我相 +信在将来的工作和项目中,我会继续运用 Rust 的知识和思想,为我的编程生涯注入新的活力和动力。 + + + +# 阶段二总结 + +在rcore的实验课程中,最让我印象深刻的就是虚拟地址部分,由于之前接触的操作系统全部都是nommu类型,直接访问物理地址的概念非常不容易被打破,并且ch4也是一个门槛,需要非常深入的理解rcore的整体代码框架,对虚拟地址的映射,软硬件协同操作,多级页表,地址空间的概念理解了很久,在不断查找资料,不断理解的情况下,才对虚拟地址的概念有了很浅显的理解,把任务完成。 + +在完成任务的过程中,其实rcore的整体框架非常完善,作业的部分也就是一些扩展功能,整体还是对rcore的实现的理解,在完成任务时,也是对已有实现进行模仿,理解整体框架,之后调用已有函数或自己根据数据结构实现函数,实现功能。 + +以及最后两个lab难度上升有点大,并且文章 中是对功能的描述居多(很多对于实验有用的点会被淹没在其中)虽然在边做lab边反复查看资料时可以发现,但是对于没有接触过的同学可能很多api看过就没印象了,在后续做lab中也找不到,所以可以适当增加一下强调或者提示,来减少难度增加坡度。 \ No newline at end of file diff --git a/source/_posts/hkp-blog.md b/source/_posts/hkp-blog.md new file mode 100644 index 0000000000..083e23cf28 --- /dev/null +++ b/source/_posts/hkp-blog.md @@ -0,0 +1,65 @@ +--- +title: hkp_blog +date: 2024-11-10 20:38:39 +tags: + - author:贺琨鹏 + - repo:https://github.com/LearningOS/2024a-rcore-nakedhkp +--- + +# 第一阶段的总结 +1. 初识 Rust:语法与基础 +Rust 的语法相比其他系统编程语言更为严谨,并且采用了一些新颖的设计,比如所有权(Ownership)和借用(Borrowing)模型。通过学习变量、函数、控制流、数据结构等基础内容,我逐步熟悉了 Rust 的编程风格。同时,Rust 强制规定的编译检查(如变量不可变性、类型检查)帮助我在早期阶段就养成了良好的编程习惯。 + +2. 内存管理与所有权 +Rust 的核心设计之一是其所有权系统,这也是区别于其他语言的最显著特性之一。在 Rust 中,每个变量都有一个所有权(Owner),变量的作用域结束时会自动释放资源,无需手动管理内存,这有效地避免了悬空指针和内存泄漏问题。在学习过程中,我对以下几个概念有了深刻理解: + +所有权(Ownership):每个数据在同一时间只能有一个所有者。 +借用(Borrowing):通过引用传递数据,允许在不改变所有权的情况下访问数据。 +生命周期(Lifetimes):通过生命周期标注来管理数据的引用,使得引用的使用更加安全。 +所有权系统让 Rust 在没有垃圾回收的情况下也能有效管理内存,学习这些概念后,我对资源管理的意识有了显著提升。 + +3. 错误处理 +Rust 的错误处理也与众不同。相比传统语言使用的异常机制,Rust 使用 Result 和 Option 枚举来处理错误和可选值。这种显式的错误处理方式让我更加清楚地理解了每个操作的潜在风险,并培养了防御性编程的习惯。此外,通过使用 ? 操作符,代码变得简洁易读,让错误处理不再显得冗长。 + +4. 并发编程 +Rust 在并发编程上的独特设计使其特别适合编写高性能的并发应用。Rust 的所有权模型通过在编译期检查数据竞争,有效防止了多线程中的常见问题,比如数据竞争、死锁等。Rust 提供的 std::thread、async/await 异步编程模型等工具使并发编程更加安全且易于管理。在学习中,我掌握了创建线程、共享数据的方式,并学会使用 Mutex 和 Arc 等工具来处理复杂的多线程情况。 + +5. Cargo 和生态系统 +Rust 的包管理器和构建工具 Cargo 是 Rust 生态系统的重要组成部分。在学习中,我逐步熟悉了如何使用 Cargo 创建项目、管理依赖、构建和测试代码。Rust 拥有活跃的社区和丰富的库资源,通过使用第三方库,可以快速实现许多功能,这大大提升了我的开发效率。 + +6. 实践中的收获 +理论学习之后,我通过一些实际项目(如简单的 Web 服务、命令行工具)将所学知识应用于实践。Rust 对类型和内存的严格检查在初期可能显得繁琐,但这些设计让我更清楚地理解代码结构,减少了运行时的错误。实践过程中,我逐渐感受到 Rust 的优势:高性能、可靠性和低级控制,使得 Rust 成为编写高效、安全应用的理想选择。 +# 第二阶段总结 + +在第二阶段的学习中,我逐步深入理解了操作系统中各个功能模块的实现。这一阶段的主要任务是通过阅读和理解代码的层次结构,来掌握系统的整体设计。在初期,我对项目代码结构的理解较为模糊,但随着长时间的代码阅读和实践,我总结出了一些行之有效的阅读和理解方法: + +1. **分层次理解代码**:我发现从高层抽象入手去理解代码结构更为高效。首先理清最高层的抽象层次,理解其核心功能,再逐渐深入到底层实现的细节。在实验中,掌握和利用高层抽象往往能够帮助节省大量时间。 + +--- + +### 实验总结 + +#### 实验一:基础系统调用实现 + +实验一的难度较低,因为此阶段还未实现页表管理,内核可以直接访问用户态的地址空间。实验中,系统调用的返回值可以直接使用内核中的数据。此外,通过高精度的 `get_time_us` 函数获取时间,能够确保后续实验中通过测试。为实现系统调用计数,可以在 `syscall` 函数调用时更新计数,而首次调用时间则需在 `run_first_task` 和 `run_next_task` 中进行设置。 + +#### 实验二:引入页表管理 + +在实验二中,由于实现了页表管理,系统调用设计需要改写。内核在处理传入参数时需找到实际的物理地址,并在该地址上读写数据。同时,本次实验引入了两个新的系统调用 `mmap` 和 `munmap`。因为两者涉及一段连续地址,因此在设计中需确保连续地址的映射状态(已映射或未映射)。最后,`mmap` 和 `munmap` 分别负责插入和删除页面。 + +#### 实验三:进程管理与调度算法实现 + +实验三的任务是实现 `spawn` 系统调用和 `stride` 调度算法。`spawn` 不能简单地视作 `fork + exec` 的组合。在实现前需理解 `spawn` 的语义,然后从 `fork + exec` 中提取相关代码,使得 TCB 数据结构达到所需的状态。`stride` 是一种进程调度算法,实现较为简单,按照教程提供的步骤逐步操作即可。 + +#### 实验四:兼容性与文件系统操作 + +实验四相较之前难度有所提升,需要保证现有代码的兼容性。一开始 `spawn` 存在兼容性问题,导致错误频发,最终通过重新实现 `spawn` 解决了兼容问题。此外,死锁处理是本实验的重点之一。由于某些函数(如 `find` 和 `create`)对文件系统上了锁,因此这些函数不可直接调用,但可以通过拼接部分代码来实现所需的功能。`unlink` 目录项的删除操作则需更新目录项,实验中借鉴了其他同学的思路,通过删除原内容并复制新内容实现目录项更新。 + +#### 实验五:死锁检测算法实现 + +实验五主要任务是实现死锁检测算法,该算法类似于银行家算法。教程中的描述较为简略,许多细节需要在实现中仔细推敲。`sem` 和 `mutex` 的实现大致相同。此外,`sys_get_time` 必须实现,否则某些实例可能会引发死锁。 + +--- + +### 整体总结 +本阶段实验的概念和难度相对基础,更多的挑战在于使用 Rust 编写操作系统。对于首次接触 Rust 的我来说,这种体验充满新奇,同时也加深了我对 Rust 的理解。Rust 强大的内存安全和所有权模型,在系统级编程中尤为适用 diff --git a/source/_posts/inchinaxiaofeng-rCore-study-record.md b/source/_posts/inchinaxiaofeng-rCore-study-record.md new file mode 100644 index 0000000000..18560461d3 --- /dev/null +++ b/source/_posts/inchinaxiaofeng-rCore-study-record.md @@ -0,0 +1,33 @@ +--- +title: "inchinaxiaofeng-rCore-study-record" +date: 2020-11-10 16:23:22 +tags: + - author:inchinaxiaofeng + - repo:zCore +--- + +# 训练营学习记录 + +## 第一阶段 + +本阶段我是学习使用Rust的基本内容,阅读的是Rust圣经中文版,那个版本对于入手而言还是很舒服的,之后就开始对着内容,找对应的Rustlings的练习,然后就做一章,看一章。 +学习的速度绝对不快,掌握的其实也不算太好,特别是偏向后面的部分,那个内容也是缺乏练习,导航i学习的不是很深刻,最后也是压哨提交的内容。 + +## 第二阶段 + +本阶段的目标是完成rCore-tutorial的五个实验。这个阶段的内容要难的多,也花了很多时间。 +好在之前有过相关的学习经历(本校的书院有OS方向,我是CPU&OS的学员),这样对我而言压力也小了很多很多。 + +### 整个学习的经历 + +我在配置环境上没有出现大的问题,我使用的是Ubuntu22.04LTS直接安装的,所以的很舒服,一边就OK了。 + +配置环境之后就开始学习,并逐步完成实验。 + +我一开始学习的速度很慢,花了很长时间去做笔记,这部分笔记几乎写了2000K行吧(还是在Vim中的“行”概念)。 +后来发现,这样的学习速度过于慢了,一周的时间仅仅勉强学到了CH4,而后面的任务要更加艰巨。 +之后我将做笔记的部分融合到了看代码的过程中,大大加快了我学习的进度。 + +之后的学习速度就快了很多很多,然后在最后一周花了很大的精力学完了hhhhhhhh。 + +由于最后一段时间的进度不理想,我甚至最后一周翘了好多课,连轴转了好久才转出来500分,只能说能过我已经很幸运了! diff --git "a/source/_posts/liguanxiao-\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223.md" "b/source/_posts/liguanxiao-\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223.md" new file mode 100644 index 0000000000..a2a65e2342 --- /dev/null +++ "b/source/_posts/liguanxiao-\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223.md" @@ -0,0 +1,19 @@ +--- +title: liguanxiao-第一二阶段总结 +date: 2024-11-11 11:58:03 +tags: +--- + +第一阶段: + +比较简单非常方便新手入门,可以很好的对rust的学习曲线难度经行拟合. + + + +第二阶段: + +由于研究生的忙碌确实让我有感觉到力不从心, + +但是课程比较有用 我之前是干java的对于设计模式和代码规范有一定的追求所以希望在第二阶段对这方面补充 + +实操教学真的很好 不仅可以检验知识点也可以检验代码能力两不耽误 \ No newline at end of file diff --git "a/source/_posts/mtul-rcore-\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223.md" "b/source/_posts/mtul-rcore-\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223.md" new file mode 100644 index 0000000000..f093ec4c51 --- /dev/null +++ "b/source/_posts/mtul-rcore-\347\254\254\344\270\200\344\272\214\351\230\266\346\256\265\346\200\273\347\273\223.md" @@ -0,0 +1,130 @@ +--- +title: mtul-rcore-第一二阶段总结 +date: 2024-11-11 22:22:14 +tags: +--- +## 第一阶段 - rust基础与算法 + +由于我有rust基础,没有遇到什么困难,但还是第一次完整地做一次rustlings,有一定收获 + +## 第二阶段 - 专业阶段 + +### 实验环境配置 + +rcore开发的环境配置比较繁琐,而我恰好比较熟悉nix,因此我用nix定义的一个完美兼容rcore的开发环境。由于不需要 `rustup` , 必须在 `os/Makefile` 中禁用 rust 环境的检测. `flake.nix` 如下: + +```nix +{ + description = "rCore dev flake"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + oldnixpkgs.url = "github:NixOS/nixpkgs/7cf5ccf1cdb2ba5f08f0ac29fc3d04b0b59a07e4"; + flake-utils.url = "github:numtide/flake-utils"; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { + nixpkgs, + oldnixpkgs, + flake-utils, + rust-overlay, + ... + }: + flake-utils.lib.eachDefaultSystem (system: let + pkgs = import nixpkgs { + inherit system; + overlays = [(import rust-overlay)]; + }; + oldpkgs = import oldnixpkgs { + inherit system; + }; + in { + devShells.default = pkgs.mkShell { + packages = with pkgs; + [ + (rust-bin.nightly."2024-05-02".minimal.override { + extensions = [ + "rust-src" + "llvm-tools" + "rustfmt" + "rust-analyzer" + "rust-docs" + "clippy" + ]; + targets = ["riscv64gc-unknown-none-elf"]; + }) + cargo-binutils + python3 + gdb + tmux + ] + ++ [oldpkgs.qemu]; + + # 进入环境后显示rust和qemu版本 + shellHook = '' + rustc --version + cargo --version + qemu-system-riscv64 --version + qemu-riscv64 --version + ''; + }; + }); +} +``` + +这份 `flake.nix` 也已经分享到[官方问答论坛](https://opencamp.cn/os2edu/bbs/1391) + +### 第一/二章 + +阅读这两章需要先了解riscv,特别是特权级相关设计。 + +主要参考: + +- [RISC-V开放架构设计之道][1] +- [The RISC-V Reader: An Open Architecture Atlas][2] 前一个的英文原著,而且有能下载到单独的RISC-V Green Card,方便查阅 +- [RISC-V Instruction Set Manual][3] 完整详细 + +### 第三章:多道程序与分时多任务 + +学习了系统调用,陷入等知识,上下文切换过程中通用寄存器和CSR的使用加深了我对riscv特权级设计的理解。 + +本章练习较为简单。 + +### 第四章:地址空间 + +SV39的设计又引入了若干相关的寄存器,如satp, pmp csr。查阅[riscv manul][3]以加深理解。 + +本章练习中,为了处理*请求映射已经被映射的页*的错误,我使用了`Result`错误传递,无法想象如果不使用`Result`和`?`语法糖我的代码会多么丑陋。然而很奇怪,整个rcore中极少使用`Result`。 + +### 第五章:进程及进程管理 + +本章内容比较轻松,完善了TCB的设计并实现`fork()`和`exec()`系统调用。 + +本章练习也比较简单。 + +### 第六章:文件系统 + +easy-fs is **NOT** easy!层层抽象几乎让我晕头转向! + +尽管如此,easy-fs囊括了superblock、索引节点、blockcache等现代文件系统中的基础概念,似乎不能再精简了。 + +link和unlink操作主要是查找inode并创建/删除目录项。在inode_block里创建与删除目录项无非是一些线性序列的操作,但由于没有封装成`&[DirEntry]`,需要手动操作,比较费劲。将来有空我会尝试改进一下。 + +### 第七章:进程间通信 + +本章内容较少,但进程间通信是个大话题,还需拓展学习。 + +### 第八章:并发 + +学习了多线程的同步与互斥。 + +练习:学习了死锁检测算法 + +[1]: https://ysyx.oscc.cc/books/riscv-reader.html +[2]: http://www.riscvbook.com/ +[3]: https://github.com/riscv/riscv-isa-manual + diff --git a/source/_posts/os-camp-arsnshsj.md b/source/_posts/os-camp-arsnshsj.md new file mode 100644 index 0000000000..ad6f9e56ac --- /dev/null +++ b/source/_posts/os-camp-arsnshsj.md @@ -0,0 +1,22 @@ +--- +title: os-camp-arsnshsj +date: 2024-11-10 16:41:51 +tags: + - author:arsnshsj +--- + +### Rustlings + ++ Rust 的编译器异常严格,尤其在内存安全、生命周期、借用检查等方面。Rustlings 的每个练习都会在编译时给出详细的错误信息,这些错误信息通常带有很强的提示性,帮助理解如何管理内存、如何使用所有权、如何避免数据竞争等 ++ Rust 的内存管理系统(所有权、借用、生命周期)对比其他语言来说非常独特。特别是在多线程编程中,Rust 提供的无垃圾回收的内存管理方式可以有效避免潜在的并发问题,这使得它非常适合高并发、性能要求高的场景 ++ 在 Rustlings 中,经常使用到 `Result` 和 `Option` 和`match`,不仅提供了对错误的显式处理,还强制我们处理所有可能的异常情况,避免遗漏潜在的错误分支 ++ Rustlings有很多智能指针,感觉之前没有接触过类似的东西,各种特性让我理解了很多操作 + +### rCore + ++ 在 rCore 实验中,我深入理解了操作系统的各个核心部分:进程管理、内存管理、文件系统、系统调用等。每个实验模块都涉及到这些基本概念的实现,通过动手实践,我加深了对操作系统内部机制的理解 ++ 在内核代码的开发过程中,Rust 强制管理内存的方式迫使我时刻注意内存分配、释放的问题,这大大降低了由于内存问题引发的 BUG 的出现几率 ++ rCore 的实验涉及到进程调度、线程切换等并发编程内容,这让我更好地理解了操作系统是如何管理和调度多个进程或线程的 ++ Rust 的多线程编程模型使得并发的实现变得更容易。通过使用 `Arc` 和 `Mutex`,我能够有效地管理共享数据,避免了传统的并发编程中容易出现的数据竞争问题 ++ 在 rCore 的实验过程中,调试是一个非常重要的部分。在操作系统内核层面,调试不像应用程序那样可以直接运行调试工具,而是需要通过打印日志、手动检查内存、模拟调试等方式进行 ++ rCore 是一个操作系统内核,涉及到硬件编程的部分,比如中断处理、IO 设备控制、虚拟内存等。这些部分让我更加理解了操作系统如何与硬件交互 \ No newline at end of file diff --git a/source/_posts/rcore-1.md b/source/_posts/rcore-1.md new file mode 100644 index 0000000000..6b5fa2cf15 --- /dev/null +++ b/source/_posts/rcore-1.md @@ -0,0 +1,5 @@ +--- +title: rcore_ +date: 2024-11-10 19:15:44 +tags: +--- diff --git "a/source/_posts/rcore-2024-a\344\270\200\344\272\214\351\230\266\346\256\265\345\256\236\351\252\214\346\200\273\347\273\223.md" "b/source/_posts/rcore-2024-a\344\270\200\344\272\214\351\230\266\346\256\265\345\256\236\351\252\214\346\200\273\347\273\223.md" new file mode 100644 index 0000000000..f873425557 --- /dev/null +++ "b/source/_posts/rcore-2024-a\344\270\200\344\272\214\351\230\266\346\256\265\345\256\236\351\252\214\346\200\273\347\273\223.md" @@ -0,0 +1,27 @@ +--- +title: rcore-2024-a-一二阶段实验总结 +date: 2024-11-11 16:00:00 +tags: + - author: shengdaozm +--- + +紧赶慢赶,终于是在交上了最后的实验,带着许多想法,写下了这篇总结。同样作为清朝老兵,我又回来了。 + +## 一阶段:Rust 110 道算法题 +在第一阶段,主要通过编写 110 道算法题,掌握 Rust 编程的基本和高级特性。Rust 语言强调安全性和高效性, +这在算法编程中尤其重要,是的,单个语法题目还是比较轻松的,查阅资料什么的也都可以直接解决。当真正开始算法部分的 +题目的时候,才开始慢慢体会到rust的折磨——借用检查器(Borrow Checker)、所有权(Ownership)以及生命周期(Lifetime)等概念 +统统在处理复杂数据结构时痛击我,Rust 的强类型系统帮助我减少了运行时错误,经常是我的逻辑天衣无缝,但还是borrowed error。 +没事,慢慢折磨吧,折磨多了就会了。。。 + +实际上,作为清朝老兵,一阶段倒是没花多少时间,翻出以前的仓库,看看填填也就过去了,但是当时熬夜做rustlings的日子仍然是我挥之不去的记忆。酸爽! + +## 二阶段:实现 rCore 简单内核 + +是的,一拳打爆rcore。第二阶段则是操作系统开发,说白了就是补全各种系统调用。我也慢慢感受到书上说的种种在我看起来毫不起眼的东西,真正实现起来是真的困难,(这里点名虚拟内存和文件系统,东西是真的很多),每个ch都是一脸懵到嘎嘎乱写,然后对着panic疯狂调,期间甚至一度动摇额我的计算机世界观——计算机是真的有玄学啊。不过还好,总是能在某些神奇的地方调调代码就神奇地通过了测试,也是给我留下了不少的未解之谜。 + +在二阶段确实收获了不少,我现在对于一些较大的工程项目已经没有感觉了,也确实是慢慢熟悉rust了,好玩,爱玩!期待后面的考核。 + +祝训练营越办越好!希望能在虚拟内存和文件管理那多讲一点,确实很抽象。 + +最后,感觉你读完我的碎碎念,不管怎样,还是磕磕绊绊弄完了这两个阶段,也算是给上半年自己中间跑路一个交代吧,行文至此,拜拜。 \ No newline at end of file diff --git a/source/_posts/rcore-debug-log.md b/source/_posts/rcore-debug-log.md new file mode 100644 index 0000000000..6b77e387be --- /dev/null +++ b/source/_posts/rcore-debug-log.md @@ -0,0 +1,32 @@ +--- +title: rcore-debug-log +date: 2024-11-09 20:06:39 +tags: +--- +# 1 Stage +初次学习rust ,之前一直以c++作为主语言进行编程,大概了解了rust 的借用机制,而且对操作系统感兴趣,所以开始学习 + +在学习rust过程中,与c++相对比很多“不适应” +1. 变量的借用机制以及生命周期的声明让人很水土不服,还在深刻感受 +2. 基础stl库的文档过于繁杂了。 +3. 比 c++ 更原生支持的泛型编程,而且比c++模板可读性更高,debug也相对更容易一些 +4. 总体比 c++ 更约束,c++ 的自由带来的是各种错误和不安全性,rust在这一方面确实做得更加出色(听说性能也与c++相差不多) + +# 2 Stage +## lab1 +简单的系统调用实现,关键是了解了 os 最初的形状,见识到了如何使用汇编和高级语言一起实现应用,很酷 + +## lab2 +rcore 路上的第一道门槛,如果lab1只能算花了我一小会的放松时间,那lab2就相当于一整天。 +1. 先把lab1的实现在lab2中兼容(地址转换有点难受)。 +2. 而后专注于实现 mmap 与 munmap,在实现时,采用直接新建area的方式,删除则删除对应 start area +3. 但遇到了问题,在校验vpn是否合法时,pte检验一直出现问题,最后在translate to ppn相关函数中做了自己理解的改动 + +## lab3 +实现角度看比较简单,但自己尝试了不同的stride位数 以及 bigstride,看到了很多很有意思的现象,包括一直不被调度,包括反转现象 + +## lab4 +感觉关键在于硬链接统计方法,实现比较粗暴(实在没时间了。。),有时间可以考虑如何使用抽象的思维跳过 STDOUT STDIN + +## lab5 +考虑银行家算法 和 死锁检测的区别(🤔) diff --git a/source/_posts/reganzm-s-SOS.md b/source/_posts/reganzm-s-SOS.md new file mode 100644 index 0000000000..9eea9c6f14 --- /dev/null +++ b/source/_posts/reganzm-s-SOS.md @@ -0,0 +1,45 @@ +--- +title: 2024秋冬开源操作系统训练营第二阶段总结-reganzm(阿敏) +date: 2024-11-10 17:08:30 +categories: + - 2024秋冬季开源操作系统训练营 +tags: + - author:reganzm + - repo:https://github.com/reganzm/2024a-rcore-reganzm +--- + +# 第一阶段总结 +1年前开始学习rust,初衷是了解一门操作系统级别的开发语言(深入了解,作为工作语言那种)。并为此写了系列的微信公众号文章《拥抱未来语言Rust》并在社区取得了不错的反响,感兴趣的可以微信公众号搜索“三角兽”,欢迎关注。 +rust作为一门系统级语言,拥有高性能的同时还具有高安全性,基于RAII风格自动资源管理,避免了很多内存安全问题(这也是吸引我学习的主要原因)。本次比赛是我第一次参加的系统级比赛,通过比赛,夯实了对rust语言的理解,包括:所有权,作用域,生命周期,智能指针等。非常有意义,在此感谢主办方! + +# 第二阶段总结 +一直以来对OS非常感兴趣,本次通过身临其境的“代码调试”,熟悉了整个项目架构,并对OS有了进一步的深刻认识。在调试过程中不仅熟悉了OS,还对Rust语言有了更深入的认识。第二阶段的操作系统实现基于RISC-V指令集,之前没有了解过RISC-V,因此看汇编语言会有些头痛,但结合RISC-V手册加上AI的辅助,理解这些汇编代码完全没有问题。 +通过第二阶段的学习,破除了一直以来对操作系统底层实现机制的迷雾,那层隔阂在应用开发人员心底的对操作系统的朦胧自此打破,世界上没有彩蛋,只有认识盲区,破除这些盲区,就是扩大自己的认知,增加自己的技术自信。后面打算写系列的博客来总结、分享操作系统,影响更多的人来学习操作系统。 + +下面是第二阶段各个实验的总结,重要的知识点我都画成了流程图,希望帮到需要的人。 + +### lab1 +这是第一个实验,整体难度不大,通过print打印信息调试,一天过关。 +{% asset_img 基础知识.png 基础知识 %} + +### lab2 +地址空间映射这一章知识密度较高,反复看了几遍才基本弄懂,调试代码陆陆续续调试了3天。(还是太菜,菜就多练!) + +简单总结下本章:在开启分页SV39分页之前,OS和都是直接访问物理地址,这给系统带来很多潜在的安全隐患,例如地址空间未隔离等。开启分页模式后,OS和用户代码中就都是虚拟地址了,需要通过页表和MMU进行转换,并且页表上的属性区分出了U和S,进行了权限和空间的隔离,分别在特权级和地址空间上保证了OS内核的安全,同时也保证了用户程序之间相互隔离,彼此空间不会重叠。(虚拟空间可以重叠,但通过页表映射后通常是隔离的,有种特殊情况是通过映射到相同的物理也实现内存共享) + +另外为了OS在开启分页后能平滑的访问,对于OS采用的是恒等映射(虚拟页号=物理页帧)。而对于用户程序通常采用Framed映射,通过栈式页帧分配器分配页帧并和虚拟页号建立映射关系,动态生成页表及页表项,实现物理页帧的按需分配。 + +另外一个比较好的抽象是地址空间MemorySet,它作为任务的一部分,管理着页表及和逻辑区。在实现采用了RAIL机制,加上rust的所有权及drop trait自动实现页表项的释放。 + +{% asset_img ch4_基础知识.png 基础知识 %} + +### lab3 +sys_spawn第一版实现测试通过,但是到实验4发现实现有问题,然后修改为exec+fork的方式完成。 + +### lab4 +文件系统章节是我花时间最多的一个章节,时间主要花在了对文件系统的理解上,看源码也费了些时间。将细节通过在线文档整理如下图所示: +{% asset_img ch6_基础知识.png 基础知识 %} + +### lab5 +死锁检测可以基于银行家算法实现,参考Wiki + diff --git "a/source/_posts/reganzm-s-SOS/ch4_\345\237\272\347\241\200\347\237\245\350\257\206.png" "b/source/_posts/reganzm-s-SOS/ch4_\345\237\272\347\241\200\347\237\245\350\257\206.png" new file mode 100644 index 0000000000..7d8b76463f Binary files /dev/null and "b/source/_posts/reganzm-s-SOS/ch4_\345\237\272\347\241\200\347\237\245\350\257\206.png" differ diff --git "a/source/_posts/reganzm-s-SOS/ch6_\345\237\272\347\241\200\347\237\245\350\257\206.png" "b/source/_posts/reganzm-s-SOS/ch6_\345\237\272\347\241\200\347\237\245\350\257\206.png" new file mode 100644 index 0000000000..d06a7934d9 Binary files /dev/null and "b/source/_posts/reganzm-s-SOS/ch6_\345\237\272\347\241\200\347\237\245\350\257\206.png" differ diff --git a/source/_posts/reganzm-s-SOS/lab1.md b/source/_posts/reganzm-s-SOS/lab1.md new file mode 100644 index 0000000000..5221b73211 --- /dev/null +++ b/source/_posts/reganzm-s-SOS/lab1.md @@ -0,0 +1,48 @@ +#### 总结 +一直以来对OS非常感兴趣,通过本次的“代码调试”,熟悉了整个项目架构,并对OS有了进一步的深刻认识。在调试过程中不仅熟悉了OS,还对Rust语言有了更深入的认识。 +本次实现的功能是打印任务信息:系统调用及调用次数,运行时间。 +整体思路:在syscall入口处调用set_task_info方法。每调用一次系统调用,更新一次syscall_times和time。 +踩的坑:需要注意Rust结构体与C结构体的区别,Rust编译器会对Rust中的字段进行重排序,以达到优化存储的目的。在OS中的结构体和user中的结构体字段要保持一致,否则会蛋疼:( +另外附图一张,表示我曾用心学习:) + +![笔记](./基础知识.png) + + +#### 第一题 +应用分别出现: +- PageFault in application, bad addr = 0x0 bad instruction = 0x804003a4 , kernel killed it. +- IllegalInstruction in application, kernel killed it. +使用的sbi版本是:RustSBI version 0.3.0-alpha.2 + + +#### 第二题 + +1.刚进入__restore时,a0代表kernel stack pointer , restore的两种使用场景:a.trap 恢复 b.创建新任务 + +2.处理了sstatus sepc sscratch。sstatus用于指定返回的特权级(SPP字段);sepc用于指定返回后执行哪条指令;sscratch存储着用户栈地址,U态程序要执行必须正确找到U态的栈地址。 + +3.application不会使用x4;x2已经被交换到了sscratch代表着用户栈指针 + +4.sp指向user stack , sscratch 指向kernel stack + + +5.__restore总状态切换在csrw sstatus,t0这行指令,sstatus中的SPP字段记录了陷入前的特权级,csrw sstatus,t0执行后,恢复到用户特权级。最后的指令sret ,指令返回用户程序,原因是该指令会从sepc中读取指令地址,并赋予pc寄存器,而U态的栈等已恢复好,sret临门一脚,步入U世界。 + +6.指令之前sp -> user stack , sscratch -> kernel stack ;指令后sp -> kernel stack, sscratch -> user stack。指令进入内核运行。并且用sscratch保存着U态的栈地址,从内核态返回即可用sscratch恢复用户态栈指针。 + +7. csrrw sp,sccratch, sp是程序从U态进入S态的关键指令,sp指向内核栈。 + + + +#### 荣誉准则 + +1. 在完成本次实验的过程(含此前学习的过程)中,我曾分别与 以下各位 就(与本次实验相关的)以下方面做过交流,还在代码中对应的位置以注释形式记录了具体的交流对象及内容: + 无 + +2. 此外,我也参考了 以下资料 ,还在代码中对应的位置以注释形式记录了具体的参考来源及内容: + + 我的参考资料:rCore-Tutorial-Book-v3 + +3. 我独立完成了本次实验除以上方面之外的所有工作,包括代码与文档。 我清楚地知道,从以上方面获得的信息在一定程度上降低了实验难度,可能会影响起评分。 + +4. 我从未使用过他人的代码,不管是原封不动地复制,还是经过了某些等价转换。 我未曾也不会向他人(含此后各届同学)复制或公开我的实验代码,我有义务妥善保管好它们。 我提交至本实验的评测系统的代码,均无意于破坏或妨碍任何计算机系统的正常运转。 我清楚地知道,以上情况均为本课程纪律所禁止,若违反,对应的实验成绩将按“-100”分计。 diff --git a/source/_posts/reganzm-s-SOS/lab2.md b/source/_posts/reganzm-s-SOS/lab2.md new file mode 100644 index 0000000000..f809d7c35b --- /dev/null +++ b/source/_posts/reganzm-s-SOS/lab2.md @@ -0,0 +1,42 @@ +#### 总结 +地址空间映射这一章知识密度较高,反复看了几遍才基本弄懂,调试代码陆陆续续调试了3天。(还是太菜,菜就多练!) +简单总结下本章:在开启分页SV39分页之前,OS和都是直接访问物理地址,这给系统带来很多潜在的安全隐患,例如地址空间未隔离等。开启分页模式后,OS和用户代码中就都是虚拟地址了,需要通过页表和MMU进行转换,并且页表上的属性区分出了U和S,进行了权限和空间的隔离,分别在特权级和地址空间上保证了OS内核的安全,同时也保证了用户程序之间相互隔离,彼此空间不会重叠。(虚拟空间可以重叠,但通过页表映射后通常是隔离的,有种特殊情况是通过映射到相同的物理也实现内存共享) + + +另外为了OS在开启分页后能平滑的访问,对于OS采用的是恒等映射(虚拟页号=物理页帧)。而对于用户程序通常采用Framed映射,通过栈式页帧分配器分配页帧并和虚拟页号建立映射关系,动态生成页表及页表项,实现物理页帧的按需分配。 + + +另外一个比较好的抽象是地址空间MemorySet,它作为任务的一部分,管理着页表及和逻辑区。在实现采用了RAIL机制,加上rust的所有权及drop trait自动实现页表项的释放。 + + + + +![笔记](./ch4_基础知识.png) + + +#### 第一题 +最低的位则是标志位,它们的含义如下: +仅当 V(Valid) 位为 1 时,页表项才是合法的; +R/W/X 分别控制索引到这个页表项的对应虚拟页面是否允许读/写/取指; +U 控制索引到这个页表项的对应虚拟页面是否在 CPU 处于 U 特权级的情况下是否被允许访问; +G 全局页表项。这意味着即使是在上下文切换(例如,进程切换)之后,该页表项也不会被冲洗(flushed)或失效。简而言之,G位用于指示页表项在地址空间的多个上下文中保持有效。 +A(Accessed) 记录自从页表项上的这一位被清零之后,页表项的对应虚拟页面是否被访问过; +D(Dirty) 则记录自从页表项上的这一位被清零之后,页表项的对应虚拟页表是否被修改过。 + +#### 第二题 + + + + +#### 荣誉准则 + +1. 在完成本次实验的过程(含此前学习的过程)中,我曾分别与 以下各位 就(与本次实验相关的)以下方面做过交流,还在代码中对应的位置以注释形式记录了具体的交流对象及内容: + 无 + +2. 此外,我也参考了 以下资料 ,还在代码中对应的位置以注释形式记录了具体的参考来源及内容: + + 我的参考资料:rCore-Tutorial-Book-v3 + +3. 我独立完成了本次实验除以上方面之外的所有工作,包括代码与文档。 我清楚地知道,从以上方面获得的信息在一定程度上降低了实验难度,可能会影响起评分。 + +4. 我从未使用过他人的代码,不管是原封不动地复制,还是经过了某些等价转换。 我未曾也不会向他人(含此后各届同学)复制或公开我的实验代码,我有义务妥善保管好它们。 我提交至本实验的评测系统的代码,均无意于破坏或妨碍任何计算机系统的正常运转。 我清楚地知道,以上情况均为本课程纪律所禁止,若违反,对应的实验成绩将按“-100”分计。 diff --git a/source/_posts/reganzm-s-SOS/lab3.md b/source/_posts/reganzm-s-SOS/lab3.md new file mode 100644 index 0000000000..77bf609398 --- /dev/null +++ b/source/_posts/reganzm-s-SOS/lab3.md @@ -0,0 +1,15 @@ + + + +#### 荣誉准则 + +1. 在完成本次实验的过程(含此前学习的过程)中,我曾分别与 以下各位 就(与本次实验相关的)以下方面做过交流,还在代码中对应的位置以注释形式记录了具体的交流对象及内容: + 无 + +2. 此外,我也参考了 以下资料 ,还在代码中对应的位置以注释形式记录了具体的参考来源及内容: + + 我的参考资料:rCore-Tutorial-Book-v3 + +3. 我独立完成了本次实验除以上方面之外的所有工作,包括代码与文档。 我清楚地知道,从以上方面获得的信息在一定程度上降低了实验难度,可能会影响起评分。 + +4. 我从未使用过他人的代码,不管是原封不动地复制,还是经过了某些等价转换。 我未曾也不会向他人(含此后各届同学)复制或公开我的实验代码,我有义务妥善保管好它们。 我提交至本实验的评测系统的代码,均无意于破坏或妨碍任何计算机系统的正常运转。 我清楚地知道,以上情况均为本课程纪律所禁止,若违反,对应的实验成绩将按“-100”分计。 \ No newline at end of file diff --git a/source/_posts/reganzm-s-SOS/lab4.md b/source/_posts/reganzm-s-SOS/lab4.md new file mode 100644 index 0000000000..40771a5cff --- /dev/null +++ b/source/_posts/reganzm-s-SOS/lab4.md @@ -0,0 +1,31 @@ +#### 实验总结 +文件系统章节是我花时间最多的一个章节,时间主要花在了对文件系统的理解上,看源码也费了些时间。将细节通过在线文档整理如下图所示: +![ch6 基础知识](./ch6_基础知识.png) + +#### 问答作业 +Root Inode 的作用: +文件系统的入口点:在类 Unix 文件系统中,root inode 是文件系统层次结构的根,即 / 目录。它是访问文件系统其余部分的起始点。 +存储目录信息:root inode 存储了根目录下的文件和子目录的元数据,例如它们的名称、inode 编号、文件类型(文件或目录)等。 +维护文件系统结构:root inode 作为文件系统结构的起点,确保了整个文件系统的组织性和可访问性。 +权限控制:root inode 还包含了访问权限信息,用于控制对根目录及其下内容的访问。 +如果 Root Inode 损坏: +如果 root inode 中的内容损坏,可能会发生以下情况: + +无法访问文件系统:由于 root inode 是访问文件系统的入口,如果它损坏,可能会导致整个文件系统无法挂载,用户无法访问任何文件或目录。 +数据丢失:虽然文件数据可能仍然存储在磁盘上,但如果 root inode 损坏,系统可能无法定位这些数据,导致数据看似丢失。 +文件系统损坏:文件系统的元数据完整性对于文件系统的健康至关重要。root inode 损坏可能导致文件系统元数据不一致,进而导致整个文件系统损坏。 +恢复困难:恢复损坏的 root inode 可能非常困难,可能需要专业的数据恢复工具和专业知识。 + + +#### 荣誉准则 + +1. 在完成本次实验的过程(含此前学习的过程)中,我曾分别与 以下各位 就(与本次实验相关的)以下方面做过交流,还在代码中对应的位置以注释形式记录了具体的交流对象及内容: + 无 + +2. 此外,我也参考了 以下资料 ,还在代码中对应的位置以注释形式记录了具体的参考来源及内容: + + 我的参考资料:rCore-Tutorial-Book-v3 + +3. 我独立完成了本次实验除以上方面之外的所有工作,包括代码与文档。 我清楚地知道,从以上方面获得的信息在一定程度上降低了实验难度,可能会影响起评分。 + +4. 我从未使用过他人的代码,不管是原封不动地复制,还是经过了某些等价转换。 我未曾也不会向他人(含此后各届同学)复制或公开我的实验代码,我有义务妥善保管好它们。 我提交至本实验的评测系统的代码,均无意于破坏或妨碍任何计算机系统的正常运转。 我清楚地知道,以上情况均为本课程纪律所禁止,若违反,对应的实验成绩将按“-100”分计。 diff --git a/source/_posts/reganzm-s-SOS/lab5.md b/source/_posts/reganzm-s-SOS/lab5.md new file mode 100644 index 0000000000..eaa254e9f0 --- /dev/null +++ b/source/_posts/reganzm-s-SOS/lab5.md @@ -0,0 +1,22 @@ +#### 实验总结 + + +#### 问答作业 + + + +#### 荣誉准则 + +1. 在完成本次实验的过程(含此前学习的过程)中,我曾分别与 以下各位 就(与本次实验相关的)以下方面做过交流,还在代码中对应的位置以注释形式记录了具体的交流对象及内容: + 无 + +2. 此外,我也参考了 以下资料 ,还在代码中对应的位置以注释形式记录了具体的参考来源及内容: + + 我的参考资料: + rCore-Tutorial-Book-v3 + https://zh.wikipedia.org/wiki/%E9%93%B6%E8%A1%8C%E5%AE%B6%E7%AE%97%E6%B3%95 + + +3. 我独立完成了本次实验除以上方面之外的所有工作,包括代码与文档。 我清楚地知道,从以上方面获得的信息在一定程度上降低了实验难度,可能会影响起评分。 + +4. 我从未使用过他人的代码,不管是原封不动地复制,还是经过了某些等价转换。 我未曾也不会向他人(含此后各届同学)复制或公开我的实验代码,我有义务妥善保管好它们。 我提交至本实验的评测系统的代码,均无意于破坏或妨碍任何计算机系统的正常运转。 我清楚地知道,以上情况均为本课程纪律所禁止,若违反,对应的实验成绩将按“-100”分计。 diff --git "a/source/_posts/reganzm-s-SOS/\345\237\272\347\241\200\347\237\245\350\257\206.png" "b/source/_posts/reganzm-s-SOS/\345\237\272\347\241\200\347\237\245\350\257\206.png" new file mode 100644 index 0000000000..5221b73211 --- /dev/null +++ "b/source/_posts/reganzm-s-SOS/\345\237\272\347\241\200\347\237\245\350\257\206.png" @@ -0,0 +1,48 @@ +#### 总结 +一直以来对OS非常感兴趣,通过本次的“代码调试”,熟悉了整个项目架构,并对OS有了进一步的深刻认识。在调试过程中不仅熟悉了OS,还对Rust语言有了更深入的认识。 +本次实现的功能是打印任务信息:系统调用及调用次数,运行时间。 +整体思路:在syscall入口处调用set_task_info方法。每调用一次系统调用,更新一次syscall_times和time。 +踩的坑:需要注意Rust结构体与C结构体的区别,Rust编译器会对Rust中的字段进行重排序,以达到优化存储的目的。在OS中的结构体和user中的结构体字段要保持一致,否则会蛋疼:( +另外附图一张,表示我曾用心学习:) + +![笔记](./基础知识.png) + + +#### 第一题 +应用分别出现: +- PageFault in application, bad addr = 0x0 bad instruction = 0x804003a4 , kernel killed it. +- IllegalInstruction in application, kernel killed it. +使用的sbi版本是:RustSBI version 0.3.0-alpha.2 + + +#### 第二题 + +1.刚进入__restore时,a0代表kernel stack pointer , restore的两种使用场景:a.trap 恢复 b.创建新任务 + +2.处理了sstatus sepc sscratch。sstatus用于指定返回的特权级(SPP字段);sepc用于指定返回后执行哪条指令;sscratch存储着用户栈地址,U态程序要执行必须正确找到U态的栈地址。 + +3.application不会使用x4;x2已经被交换到了sscratch代表着用户栈指针 + +4.sp指向user stack , sscratch 指向kernel stack + + +5.__restore总状态切换在csrw sstatus,t0这行指令,sstatus中的SPP字段记录了陷入前的特权级,csrw sstatus,t0执行后,恢复到用户特权级。最后的指令sret ,指令返回用户程序,原因是该指令会从sepc中读取指令地址,并赋予pc寄存器,而U态的栈等已恢复好,sret临门一脚,步入U世界。 + +6.指令之前sp -> user stack , sscratch -> kernel stack ;指令后sp -> kernel stack, sscratch -> user stack。指令进入内核运行。并且用sscratch保存着U态的栈地址,从内核态返回即可用sscratch恢复用户态栈指针。 + +7. csrrw sp,sccratch, sp是程序从U态进入S态的关键指令,sp指向内核栈。 + + + +#### 荣誉准则 + +1. 在完成本次实验的过程(含此前学习的过程)中,我曾分别与 以下各位 就(与本次实验相关的)以下方面做过交流,还在代码中对应的位置以注释形式记录了具体的交流对象及内容: + 无 + +2. 此外,我也参考了 以下资料 ,还在代码中对应的位置以注释形式记录了具体的参考来源及内容: + + 我的参考资料:rCore-Tutorial-Book-v3 + +3. 我独立完成了本次实验除以上方面之外的所有工作,包括代码与文档。 我清楚地知道,从以上方面获得的信息在一定程度上降低了实验难度,可能会影响起评分。 + +4. 我从未使用过他人的代码,不管是原封不动地复制,还是经过了某些等价转换。 我未曾也不会向他人(含此后各届同学)复制或公开我的实验代码,我有义务妥善保管好它们。 我提交至本实验的评测系统的代码,均无意于破坏或妨碍任何计算机系统的正常运转。 我清楚地知道,以上情况均为本课程纪律所禁止,若违反,对应的实验成绩将按“-100”分计。 diff --git "a/source/_posts/sevetis-\344\270\200\344\272\214\351\230\266\346\256\265blog.md" "b/source/_posts/sevetis-\344\270\200\344\272\214\351\230\266\346\256\265blog.md" new file mode 100644 index 0000000000..8ffefe9d74 --- /dev/null +++ "b/source/_posts/sevetis-\344\270\200\344\272\214\351\230\266\346\256\265blog.md" @@ -0,0 +1,26 @@ +--- +title: sevetis-一二阶段blog +date: 2024-11-11 14:52:02 +tags: +--- + +## 前言 +- 一个大三学生, 偶然看到这个学习活动就来玩一下了. 之前学校操作系统课学了个寂寞, 正好来学多点补全知识, 并且练练rust. + +## rustling +- 这个还好, 以前有点点rust基础. 做着没什么困难, 顺便查漏补缺了一下. + +## rcore +- 二阶段开始, 内容一下多了起来, 文档看着也挺枯燥的, 有些地方感觉写得对小菜菜不太友好QAQ. +- lab1 + - 翻翻文档, 翻到了ch3. 懒得再细看文档了, 直接写代码做实验!看了一下要求, 直接加点东西就一次过了, 开心. +- lab2 + - ch4这个卡了我两周, 内存虚拟化这块真的很不会, 做着不明不白. 有点想摆烂了, 看了看群里聊天记录(光是看群聊信息都能学很多), 有点思路, 往大方向试了一试, 通过.(可能通过了还是不太明白) +- lab3 + - ch5不是很难, stride调度算法出了点小错误稍微卡了一下, 很快就过了. +- lab4 + - 听很多人说ch6最难. 文件系统我也不太会,但我认真看了文档和代码, 试了几次也过了, 没感觉太难. +- lab5 + - 做得最痛苦的实验. 这个实验主要实现死锁检测, 本身不是很难. 但我遇到了一些玄学问题, 有`sleep_blocking`的测例会在sleep卡住超时过不了, 找来找去找不到原因, 加上DDL到了, 心态小崩. 最后垂死挣扎, 卸了qemu9, 把它换成qemu7, 不怎么抱期望地跑了一下, 通过了!(据说有一些人用qemu9也可以通过, 但不知道为什么我不行.) + +#### 总的来说, 第二阶段学到了很多东西, 以前漏掉没学的操作系统知识被补上很多, 但更感觉有更多的东西要学. 同时有时做实验遇到困难怎么尝试都通过不了, 最后再坚持一下通过了, 很有成就感.(感觉实验测例有点简单..) diff --git a/source/_posts/stage-1-ProerLoneW.md b/source/_posts/stage-1-ProerLoneW.md new file mode 100644 index 0000000000..dc21287694 --- /dev/null +++ b/source/_posts/stage-1-ProerLoneW.md @@ -0,0 +1,26 @@ +--- +title: stage-1-ProerLoneW +date: 2024-11-13 21:17:20 +categories: + - report +tags: + - author: ProerLoneW + - repo: rust-rustlings-2024-autumn-ProerLoneW + - rust-learning +--- + +# rCore第一阶段总结报告 + +### 参加训练营的契机 + +​ 本人是大三信息安全专业学生,起因是在学期初老师在班级群中转发了本次训练营的相关推送,正好本学期在修操作系统专业课,再加上在过去的两年实际上没有学过过硬的技术,就希望能通过本次训练营收获到许多实际项目开发相关的东西,希望能做到技多不压身。 + + + +### 第一阶段回顾 + +​ 本阶段的主要任务是根据 `rust语言官方圣经` 来学习rust语法,并在简单的知识训练、算法实现中掌握基本的编程逻辑和rust语言的独特思维。在学习过程中最大的感受就是,这是一门非常“麻烦”(或者说拗口)、非常底层但却非常“安全”的语言。 + +​ 在进一步地阅读文档和代码实操的过程中,我也深深地体会到了rust语言的魅力,很多在其他语言中不需要关注的问题,都是rust使用者的家常便饭,例如所有权与借用、生命周期、模式匹配、并发和线程、内存管理、特征泛型、智能指针、宏等等全新的概念,在学习的过程中,也能十分自然地和以往所写过的语言进行对比,也能十分自然地联想到自己所学的专业知识,整个学习过程是痛苦而充满趣味和成就感的。 + +​ 最终也是断断续续地在国庆后完成了110分的编程题目,虽然很多地方都有囫囵吞枣、只求大概的不好处理,但还算是初步打开了rust语言的大门。 diff --git a/source/_posts/stage-2-ProerLoneW.md b/source/_posts/stage-2-ProerLoneW.md new file mode 100644 index 0000000000..da479c0c43 --- /dev/null +++ b/source/_posts/stage-2-ProerLoneW.md @@ -0,0 +1,43 @@ +--- +title: stage-2-ProerLoneW +date: 2024-11-13 23:33:51 +categories: + - report +tags: + - author: ProerLoneW + - repo: 2024a-rcore-ProerLoneW + - os-first-step +--- + +# rCore第二阶段总结报告 + +### 第二阶段回顾 + +​ 本以为第一阶段后将是一马平川,却不曾想第二阶段竟是噩梦的开始。本以为第二阶段也和第一阶段一样,只需断断续续抽出一周的零碎时间即可轻易完成,但只有亲身尝试过才会知道这种想法多么的错误,最后几乎是推掉了所有的学业任务,把一整周都投入在了rCore上才勉勉强强卡ddl写完了所有lab。 + +​ 第零章和到第二章可以说是第二阶段的环境配置和任务理解阶段,由于上一阶段仅仅是在mac电脑上轻松写代码,故在一开始的环境配置上还是耗费了小两天,在此过程中第一次接触到了windows的wsl,然后一步一步在实验指导书的指导下搭建了 `vscode + wsl + ubuntu20.02` 这样的开发环境,在阅读前面几章的文档内容后也对所学的知识、实验的相关方向有了大致的了解,并能够初步运行起来自己的rust-os。在第一章的学习过程中,我理解了应用程序的执行环境,对 `应用程序、函数调用、标准库、系统调用、操作系统、指令集和硬件平台` 这些概念又有了新的认识,有种学习汇编语言的感觉,另外也接触到了 `裸机编程、目标三元组` 这些比较新的东西。但也仅停留在有印象的层面,没能深入理解其中奥秘;第二章的内容比较全面,我了解到了应用程序执行过程和操作系统的特权级切换机制,了解了编写risc-v的链接脚本来控制内存布局、内嵌汇编、异常处理、上下文切换的实现,这些操作在代码中的实现,更是让我操作系统的课上所学映入了现实,第二章的批处理调度系统,也是一个很好的帮助我入门并理解整体的代码架构的案例。 + +​ 后面几章就没有那么多时间细细咀嚼了,通常都是扫两眼知识然后直奔作业,除了文件系统外,其他由于都在课上学过,因此在gpt的帮助下没有被卡住太久的时间。也很感谢用心设计该实验教程的学长/学姐,不仅让我们快速入门了os,还让我们快速了解了如何系统开发一个os的各个功能。 + +​ 在lab1实验就卡了很久——不会仔细品读知识中所蕴含的代码实现细节,也不会投机取巧地去看看测试用例和作业所给的提示,而仅仅是闭门造车,最终卡了许久才在群聊天记录中找到了问题的关键所在,也就是时间的记录问题,当然在写的过程中也遇到诸如生命周期等等问题,让语言基础不太牢固的我举步维艰。 + +​ lab2是虚拟存储多级页表实验,虽然在课上老听说页表的神奇和重要性,但从没有像本次实验这样深刻地接触过操作系统中的页表,最初做的时候由于考虑的太多又无法实现便导致一度停滞不前,后来在发呆的时候又仔细重新阅读了一下题目的要求,发现需要实现的东西都还挺简单的,而且测试用例也给的非常宽松因此很快的做完了,并没有想象中那么复杂。 + +​ lab3是有关进程的简单创建和调度,实现上并不困难,主要难度还是在于代码结构发生了较大变化,比如本来 `task_manager` 做的事情现在换成了 `process` 在做。 + +​ lab4是最痛苦的一次实验,在把ch5的代码移植过来后发现仅需要过三个测试文件即可便觉得它很简单,但真正想要得心应手地写出来需要对文件系统和代码实现有详细的理解,最终还是在听了ddl前的讲解才恍然大悟:linkat和create略像前一章所提到的fork和spawn,否则将根本无从下手然后白白浪费时间并放弃之前的一切努力。在给 `inode` 加上 `inode_id` 相关的方法后很快完成了这次实验。 + +​ lab5的内容相对比较熟悉,也是课上自认为十分简单的死锁检测问题,但代码框架阅读起来难度较大,最终将前面的 `sys_get_time` 搬过来后跟着题目的提示实现了资源分配图和死锁检测系统调用的实现 + + + +### 第二阶段收获 + +​ 这次的第二阶段学习就像一场不断挑战极限的马拉松,让我既感到疲惫不堪,又充满了意想不到的收获。在本阶段的学习中,我获得了关于操作系统核心机制的更深入理解,体验到了真实操作系统开发的复杂性和细致入微的编程要求,特别是在进程管理、内存管理、以及文件系统的设计上有了全新的认识。 + +​ 首先,通过批处理调度系统的实现,我理解了特权级切换、上下文保存与恢复等机制。这种直接操控硬件资源的编程体验帮助我更好地理解了操作系统在管理硬件与应用间的角色。其次,在实现文件系统的过程中,我也是初次了解了文件路径解析、inode 管理和文件描述符等底层概念,这不仅让我理解了文件系统的设计精髓,也磨炼了抽象和模块化编程的能力。 + +​ 同时,在死锁检测的实验中,资源分配图的设计让我更深刻地理解了进程间的依赖关系和并发控制策略,学习知识的最好方式绝对是动手实践。 + +​ 总的来说,这一阶段的收获不止是技术上的进步,更让我体会到了系统开发的全局性思维和精确性要求。这不仅提升了我的编程能力,也让我更有信心面对后续操作系统开发中的复杂问题。 + diff --git "a/source/_posts/\345\217\256\345\275\223\347\214\253\347\232\204\345\215\232\345\256\242.md" "b/source/_posts/\345\217\256\345\275\223\347\214\253\347\232\204\345\215\232\345\256\242.md" new file mode 100644 index 0000000000..991a87e09e --- /dev/null +++ "b/source/_posts/\345\217\256\345\275\223\347\214\253\347\232\204\345\215\232\345\256\242.md" @@ -0,0 +1,20 @@ +--- +title: 叮当猫的博客 +date: 2024-11-11 03:42:49 +categories: + - report +tags: + - author:Ding-dangmao +--- + +# 一阶段 Rustlings + +​ 机缘巧合之下,对我教育颇深的学长为我介绍了这个训练营,于是一段艰辛的历史就开始了 + +​ 初学rust,被他严格是语法体系给搞傻了,这也不给那也不给,对于写惯C++ 的我来说简直不可理喻。rust语法体系中不允许隐式类型转换,即使是在C++中的非窄化类型转换也不允许。更要命的一点是,所有变量默认全是按const不可变变量来处理的,这极大地降低了我的愉悦性,在C++中此类const操作时显示的,在这变为隐式。还有一点则是类型的特性不会自动从父类继承(C++是这么称呼的,rust学的不太行),必须我去一 一 写出,也是很难受。 + +​ 介绍完了令我不愉快的地方,rust的优势也很明显,不允许随意更改变量,不允许直接操作指针,变量的所有权等等,在重重限制下无疑降低了代码出错率,不过我还是喜欢C++。 + +# 二阶段rcore + +​ 这一阶段别提多痛苦了,一阶段语法就没学好,大半时间都在调语法错误,章节知识点介绍都挺好的,想一想也容易想出来,over,不过我还是喜欢C++,rust使我痛苦 , 重复可变借用我恨你 diff --git "a/source/_posts/\345\260\232\344\271\246\347\232\204rcore\347\254\224\350\256\260-\351\230\266\346\256\2651\357\274\2142.md" "b/source/_posts/\345\260\232\344\271\246\347\232\204rcore\347\254\224\350\256\260-\351\230\266\346\256\2651\357\274\2142.md" new file mode 100644 index 0000000000..d9e2a58f56 --- /dev/null +++ "b/source/_posts/\345\260\232\344\271\246\347\232\204rcore\347\254\224\350\256\260-\351\230\266\346\256\2651\357\274\2142.md" @@ -0,0 +1,43 @@ +--- +title: 尚书的rcore笔记-2024Fall阶段1,2 +date: 2024-11-10 23:03:52 +tags: + - author:origin76 + - repo:(https://github.com/LearningOS/2024a-rcore-origin76) + - + - +--- + +## 前言 + +其实在笔者去年大二的时候,就了解了一些系统赛和rCore的相关信息,当时人比现在少很多,笔者也因为课业压力很早就放弃了。今年虽然同样有些别的事要忙,但是也各抽出一周写了前两个阶段。 + +在此期间,充分感受到了rust的优势,如果换成cpp的话我可能早就放弃或者早就做完了(x,rust有一种被编译器推着前行的感觉,并且rcore的代码组织相当优秀,无论是抽象层还是语法设计,都十分值得学习。 + +## rustlings + +第一阶段是来自rust官方社区的rustlings语法练习,并且经过了一定的增加,加了一些用得到的数据结构算法内容。总体没有什么难度,也并不需要完全弄懂,起到了一个入门的作用。所有权,生命周期,泛型之前就在moderncpp中有所涉猎,所以理解起来没有遇到什么困难。 + +## rCore + +整个第二阶段的质量相当高,除了视频以外可以说感觉做下来不逊于6.1810,题目相当的有代表性并且需要读懂很多部分的代码才能获取正确的思路,起到了很好的引导作用。 + +### ch3 + +ch3的潜在难度在于内核栈较小导致无法直接传递较大的数组,导致只能通过引用来传递,即使这里并不涉及地址变换。如果在ch3中遇到了这个坑,就更能理解后续页表的意义。 + +### ch4 + +ch4中带领入门了段页式的页表和页帧。需要正确掌握好页表和物理页帧的对应关系,确保这两者是同步的而且是有效的。 + +### ch5 + +ch5的编码量较少,只需要找到正确的地方插入pcb的转移和下个task的获取即可。 + +### ch7 + +ch7入门了fs的抽象,从OSInode到Inode到diskInode到blockcache的抽象,层层深入,再加上用于查找的dirEnrty和fdtable,使得整个抽象层设计相当清晰。 + +### ch8 + +ch8的难度在于语义含糊,我们仅有两类资源,其中mutex每种只有一个,semaphore每种可以有多个。在此基础上需要正确的理解need和alloc对应的语义,并且在阻塞线程被唤醒时及时更新。 \ No newline at end of file diff --git "a/source/_posts/\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223_\346\235\216\345\244\247\345\215\216.md" "b/source/_posts/\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223_\346\235\216\345\244\247\345\215\216.md" new file mode 100644 index 0000000000..37be4c63bf --- /dev/null +++ "b/source/_posts/\347\254\254\344\270\200\351\230\266\346\256\265\346\200\273\347\273\223_\346\235\216\345\244\247\345\215\216.md" @@ -0,0 +1,23 @@ +--- +title: 2024S 开源操作系统训练营总结-李大华 +date: 2024-11-11 00:18:00 +categories: + - 2024秋冬季季开源操作系统训练营 +tags: + - author:cndoit18 + +--- + +# 第一阶段总结 + +rustlings很有用,年轻人的第一款cpp. + +# 第二阶段总结 + +老实说,文档很抽象,框架体验也比较一般…… + +希望越来越好 + +希望有更多的工具相关内容,例如gdb和cmake。os相关工作其实相对少,但是学习工具的使用对各方面都很有用。 + +文档要是能多一些图就更好了,理解起来实在有难处。