From 06c8137e322d3f1a672a0f6d897972f0f6f0ef57 Mon Sep 17 00:00:00 2001 From: Aoi979 Date: Sun, 10 Nov 2024 10:25:29 +0800 Subject: [PATCH 1/4] blog --- source/_posts/2024A-rCore-Blog-Aoi979.md | 36 ++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 source/_posts/2024A-rCore-Blog-Aoi979.md diff --git a/source/_posts/2024A-rCore-Blog-Aoi979.md b/source/_posts/2024A-rCore-Blog-Aoi979.md new file mode 100644 index 00000000000..46c944b356a --- /dev/null +++ b/source/_posts/2024A-rCore-Blog-Aoi979.md @@ -0,0 +1,36 @@ +--- +title: 2024A-rCore-Blog-Aoi979 +date: 2024-11-10 02:36:55 +tags: + - author: Aoi979 + - repo: https://github.com/LearningOS/2024a-rcore-Aoi979 + - rust +--- + + + +## 总结 + +- ch1~3 + + 在学习汇编语言的过程中,我深刻体会到了“切换上下文”的概念。通过阅读trap.s和switch.s的代码,我不仅学到了编程技巧,还对操作系统和计算机的深层次工作原理有了更为深刻的理解。 + +- ch4 + + 地址空间的章节对我而言是一大挑战。尽管在理论学习中已经有所接触,但真正深入到代码层面时,我仍然感到些许迷茫。理解MapArea和MemorySet的过程颇费周折,最初我错误地将rCore视为单页表系统,导致对许多操作感到困惑。经过反复推敲和深入学习,我终于克服了这一障碍,这一部分的学习经历让我受益匪浅。 + +- ch5 & ch8 + + 与第四章相比,第五章和第八章的编程题目难度有所降低。这两章引入了进程的概念,并为后续的并发章节做了良好的铺垫。我开始对之前觉得理所当然存在的进程和线程有了更深刻的理解。在Linux系统中,进程和线程只是共享程度不同的任务。相较于理论书籍,代码的直观性让我对这些概念有了更加清晰的认识。第八章中,我花费了一些时间来理解银行家算法,一旦掌握,编程题目便迎刃而解。 + +- ch6 + + 这一章对我来说是整个学习阶段中难度最大的。在理论学习时,我对文件系统章节的理解不够深入,面对文档中逐步引入的抽象概念,我感到十分困惑。在反复阅读文档和代码后,我终于在微信训练营群友的帮助下完成了作业。我希望rCore在未来的讲解中能够更加细致地涵盖这一部分。 + +- ch7 + + 很轻量的章节,为系统引入了管道,实现了进程间的通信,因为没有编程作业,就没上面的章节反复翻阅文档式的去理解各个细节 + + ## 感悟 + + 回顾这一阶段的学习,我感到非常充实。尽管在第六章的学习过程中遇到了不小的挑战,但这段经历对我的成长无疑是宝贵的。无论我能否在接下来的学习中继续坚持,这一阶段所学到的知识和技能都将对我产生长远的影响。 From 0bb108e784c7d8e3ef9452a56aa2a8750997845e Mon Sep 17 00:00:00 2001 From: Aoi979 Date: Sun, 10 Nov 2024 14:19:47 +0800 Subject: [PATCH 2/4] blog --- source/_posts/2024A-rCore-Blog-Aoi979.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source/_posts/2024A-rCore-Blog-Aoi979.md b/source/_posts/2024A-rCore-Blog-Aoi979.md index 46c944b356a..617e823d488 100644 --- a/source/_posts/2024A-rCore-Blog-Aoi979.md +++ b/source/_posts/2024A-rCore-Blog-Aoi979.md @@ -9,7 +9,13 @@ tags: -## 总结 + + +## 第一阶段总结 + +对Rust这门编程语言早有耳闻,在活动开始之前就学过一遍rust,不过因为用的不多也没有很熟练,第一阶段的Rust练习巩固了基础,我平时是写C/C++的,真正写起来的时候非常强烈的感受到Rust 的设计与C++之间的差异,Rust对安全的要求非常严格,还有就是这个编译器很强大,虽然过不了编译挺烦人的,但编译器又能教你改正错误,也算是Rust的一个魅力了 + +## 第二阶段总结 - ch1~3 From 67d1cae7a699726fdc1a4909f2b4a8c25af92b80 Mon Sep 17 00:00:00 2001 From: Aoi979 Date: Thu, 5 Dec 2024 21:43:46 +0800 Subject: [PATCH 3/4] blog --- ...256\265\346\200\273\347\273\223-Aoi979.md" | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 "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\211\351\230\266\346\256\265\346\200\273\347\273\223-Aoi979.md" 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\211\351\230\266\346\256\265\346\200\273\347\273\223-Aoi979.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\211\351\230\266\346\256\265\346\200\273\347\273\223-Aoi979.md" new file mode 100644 index 00000000000..bd35647d1e3 --- /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\211\351\230\266\346\256\265\346\200\273\347\273\223-Aoi979.md" @@ -0,0 +1,21 @@ +--- +title: 2024秋冬季开源操作系统训练营第三阶段总结-Aoi979 +date: 2024-12-05 21:38:26 +tags: + - author: Aoi979 + - repo: https://github.com/Aoi979/oscamp +--- +# 第三阶段总结 + +### 第一周 + +学习了Unikernel以及组件化内核的思想,组件化内核通常以Unikernel为起点通过体系结构无关的模块进行扩展得到其他模式内核,减少重复造轮子。第三次作业学的内存分配算法需要理解(不过感觉以后用不上想必会忘的灰飞烟灭) + +### 第二周 + +从Unikernel跨越到了宏内核,又到了rcore阶段就接触的宏内核了,同样,是展示如何以组件化的方式把unikernel构建成其他类型的kernel,同时也展示了各类kernel间本质的差异,这周作业是完成几个syscall + +### 第三周 + +最吸引我的部分,起初我都不知道什么是hypervisor,好奇过虚拟机是怎么运行的,认识hypervisor后至少从原理上大致明白了,进入vm和退出vm的地方设计比较巧妙,不过牵扯到汇编的部分我都觉得很妙就是了2333,还没形成汇编的思维模式吧,第一个作业比较有意思,需要了解进出虚拟机的流程,处理完特权指令后还要对保存虚拟机状态的相关内存进行操作以满足作业要求,后一个作业直接用现成的load_vm_image就可以了 + From 3088d2a8f42cd090b348e90f69b23f0b599b15cb Mon Sep 17 00:00:00 2001 From: Aoi979 Date: Sat, 21 Dec 2024 23:02:53 +0800 Subject: [PATCH 4/4] blog --- ...256\265\346\200\273\347\273\223-Aoi979.md" | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 "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\345\233\233\351\230\266\346\256\265\346\200\273\347\273\223-Aoi979.md" 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\345\233\233\351\230\266\346\256\265\346\200\273\347\273\223-Aoi979.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\345\233\233\351\230\266\346\256\265\346\200\273\347\273\223-Aoi979.md" new file mode 100644 index 00000000000..9e1348b7497 --- /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\345\233\233\351\230\266\346\256\265\346\200\273\347\273\223-Aoi979.md" @@ -0,0 +1,149 @@ +--- +title: 2024秋冬季开源操作系统训练营第四阶段总结-Aoi979 +date: 2024-12-21 22:54:33 +tags: + - author:Aoi979 + - coroutine +--- + +第四阶段选择了基于协程的操作系统,但并没有选择引入协程到操作系统的方向,不过也有相关思考,这段时间主要学习了rust的协程以及异步运行时的功能以及如何设计 + +## 协程只是可以挂起和恢复的函数 + +函数只有2个行为:调用和返回,函数返回后,栈上所拥有的状态会被全部销毁,协程则可以挂起,协程挂起时可以保留协程上下文,恢复则恢复协程的上下文,协程的上下文取决于协程内的局部变量等,反正是比线程上下文小,当协程像函数一样返回,协程也要被销毁 + +## Cpp 协程和Rust协程的对比 + +cpp和rust同为无栈协程,但设计不同 + +- ## 对于协程的调用者 + +cpp的做法是协程的调用者会得到来自promise_type结构体内get_return_object方法返回的对象,这里一般通过form_promise构造协程句柄coroutine_handle,调用者可以通过协程句柄resume协程和destroy协程,cpp的协程不是lazy的,这点和rust不一样,相当于拿到future后直接开始poll,不过cpp不需要waker,cpp的handle不需要程序员提供函数也不需要提供指针,已经构造好了,而rust需要rawwaker,需要一个函数表定义行为,再传入任务对象的指针,让waker能够操作任务对象,自然而然就能调度这个任务了,cpp只需要管理好何时resume,但rust是loop poll,程序员管理何时把future重新加入被poll的队列 + +rust则是每一个async函数都会自动生成并返回一个future对象,通过future trait可以看到主要是通过poll来执行协程内代码以及状态切换,这里贴一下tokio教学文档里的例子 + +```rust +impl Future for MainFuture { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) + -> Poll<()> + { + use MainFuture::*; + + loop { + match *self { + State0 => { + let when = Instant::now() + + Duration::from_millis(10); + let future = Delay { when }; + *self = State1(future); + } + State1(ref mut my_future) => { + match Pin::new(my_future).poll(cx) { + Poll::Ready(out) => { + assert_eq!(out, "done"); + *self = Terminated; + return Poll::Ready(()); + } + Poll::Pending => { + return Poll::Pending; + } + } + } + Terminated => { + panic!("future polled after completion") + } + } + } + } +} + +``` + +rust中.await会变成对其future的poll,而waker则需要在对最外层future的poll时构造进context作为形参,通过poll的结果决定是否挂起,但是rust协程没有恢复这个操作,rust的协程是通过waker把任务重新调度回来再poll + +cpp使用Awaiter来控制协程,符合直觉的操作,没有rust那么绕,resume就是直接重新进入协程 + +```rust +enum State { + Start, + YieldValue, + FinalSuspend, + Done +}; + +struct CoroutineStateMachine { + State current_state = Start; + int a = 1, b = 1; // + + // promise_type的引用,协程的promise接口 + promise_type& promise; + + void resume() { + try { + switch (current_state) { + case Start: + // 执行 initial_suspend + if (promise.initial_suspend()) { + current_state = YieldValue; + return; // 挂起 + } + // 进入协程主体 + [[fallthrough]]; + + case YieldValue: + while (a < 1000000) { + // co_yield a + promise.yield_value(a); + current_state = YieldValue; + std::tie(a, b) = std::make_tuple(b, a + b); + return; // 挂起 + } + // co_return + promise.return_void(); + current_state = FinalSuspend; + [[fallthrough]]; + + case FinalSuspend: + // 执行 final_suspend + if (promise.final_suspend()) { + current_state = Done; + return; // 挂起 + } + // 结束 + [[fallthrough]]; + + case Done: + return; // 协程结束 + } + } catch (...) { + // 异常处理 + if (!promise.initial_await_resume_called()) { + promise.unhandled_exception(); + } + } + } +}; + +``` + +- ## 内存布局 ## + +cpp是全都开堆上,而rust的future可自由选择在栈上还是堆上,对于自引用的结构体,当其被协程捕获作为协程的局部变量时,不允许转移所有权,我们使用Pin来进行保障,因为引用所代表的地址已经被标记为无效 + +## 异步运行时设计 + +协程是用户态的任务调度机制,而线程是内核的任务机制,做的事和内核一样,不断执行不同任务,不过是协作式调度,我们需要手动挂起来让其他任务运行,设想单线程环境下的任务调度,我们需要把任务存储在一个集合中,一个接一个运行协程,协程挂起时就再放入集合中。初步想法是这样的,但我们又不在内核态,完全可以把任务交给内核,而不是协程一挂起就重新放回集合,当集合为空时我们的线程就可以休息(多线程环境下其他线程使协程重新加入集合并把休眠的线程唤醒[把阻塞任务丢给了专门的线程])或是主动检查异步任务是否准备完成(阻塞调用不在用户态[需要内核的异步支持]) + +这样我们的线程可以把阻塞都丢给其他线程或是内核执行,而本身只需要处理更多的任务,提高并发量 + +和线程一样,我们也需要一个调用在协程里创建一个协程,我们需要spawn,有时候我们需要等待另一个协程的结果(仍然不会阻塞,因为外层的协程也挂起了),我们要为此添加JoinHandle,如果我们持有一个线程池来执行任务,spawn出的协程就需要一个面对线程池调度的waker,当资源准备好时加入线程池所用的任务队列,但当我们对其JoinHandle进行.await的话我们需要把当前协程的waker和其默认的waker进行替换,因为我们需要这个原本自由的协程在我们等待他的协程上恢复而不是在线程池中恢复后,任务的返回值不被关心,等待自然drop,使用线程池我们可以把同步调用异步化,让阻塞在单独的线程上运行,如果使用io_uring的话就更轻松了 + +## io_uring + +io_uirng是真正的异步io,通过他我们可以实现上述的第二种方案,当异步任务都处理完了我们的线程就检查完成队列,然后重新加入集合中,进行poll,此时资源已经准备好,不会造成任何阻塞 + +## 对协程引入操作系统的想法 + +考虑多核情况下,每个核心运行一个协程执行器,对于Mutex或信号量如果资源申请失败那么直接把任务挂起,把waker和任务指针记录下来,当资源被释放时,自动寻找等待队列的第一个元素,通过waker提供的函数表操作任务指针,使其重新加入对应核心的任务执行队列中。协程在内核态给我感觉是和序列生成器一般,能在一些部分减少时间片的浪费,提高任务处理的效率