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\345\233\233\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\345\233\233\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..4736f7dff6 --- /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\345\233\233\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,96 @@ +--- +title: 2024秋冬季开源操作系统训练营四阶段学习总结报告-颜熙炆 +date: 2024-12-21 22:59:33 +categories: + - report +tags: + - author: ZIYAN137 +--- + +# 第一周: + +## 有栈协程与无栈协程 + +协程这块的概念可能比较混乱,不太能说得清,但是计科毕竟大多数情况下也不是深究定义的,所以纤程、绿色线程、协程大体上是可以混为一谈的,以下我们统称为协程。 +尽管名称可能不同,但它们都可以被划分为两大类,一类是有栈(stackful)协程,一类是无栈(stackless)协程。 +此处的有栈和无栈指的不是指协程在运行时是否需要栈,(毕竟不能回到“远古时代”的面条式编程)对于大多数语言来说,一个函数调用另一个函数,总是存在调用栈的;而是指协程是否可以在其任意嵌套函数中被挂起。有栈协程是可以的,而无栈协程则不可以。 + + +### 有栈协程 + +实现一个协程的关键点在于如何保存、恢复和切换上下文。 +在有栈协程中,我们将函数作为协程,保存上下文也就是保存从这个函数及其嵌套函数的栈帧存储的值。恢复上下文则是将这些值重新写回对应的栈帧和寄存器当中。切换上下文也就是保存当前的上下文,恢复下一个要执行的函数的上下文。有栈协程就是这么地朴素,也是我这种刚听说协程的萌新最易于理解的一种协程实现。 + +### 无栈协程 + +相比于无栈协程直接切换栈帧的思路,无栈协程则没有改变调用栈。而是使用了生成器(一种特殊的迭代器,能够在函数的执行过程中保存状态,并在需要时恢复执行)。这种特性可以用来模拟协程切换的行为,从而实现上下文切换。 + +无栈协程就是把代码转换成状态机,我们可以将 Future 视为一种协程。 + +rust的 Future 是通过状态机的形式来实现异步操作的。每个 Future 都是一个状态机,表示一个可能尚未完成的计算。 +它通过轮询(polling)的方式推进计算过程,直到完成。 +poll 方法会被异步运行时反复调用,当 Future 返回 Poll::Pending 时表示还没准备好,当返回 Poll::Ready 时表示完成。 +所以,对future的运算实际上也就可以视为是在执行协程。 + +它不依赖操作系统或运行时的栈切换,而是通过将状态信息嵌入到 Future 的数据结构中。这样可以在编译时生成高效的代码来管理异步操作。 + +### rust的协程 + +rust在古早版本(1.0之前)曾经有过一个有栈协程的方案——绿色线程。但是由于不符合零成本抽象的思想被移除了。此外,对于rust而言,绿色线程需要尽可能地减小预分配的堆栈大小,进而降低内存上的开销,毕竟需要比操作系统的线程更加轻量级,否则为什么不直接使用操作系统的线程呢? +之后,rust使用了无栈协程的方案,虽然增加了开发上的复杂度,但是良好地解决了并发问题。 + +## 其他 +阅读了: + +https://os.phil-opp.com/async-await/ + +两百行实现绿色线程: +https://zhuanlan.zhihu.com/p/100058478 + + +# 第二周: + +基本上就读读tokio和smol的源码,但是说实话,没太看明白,但是smol确实会更简单易懂一点。剩下的就是在读io_uring + +## io_uring机制 + +io_uring的设计主要围绕着环形缓冲区实现,SQ和CQ都是环形的,并且大小都是2的n次方(位运算奇技淫巧 index % size == index & (size - 1),当且仅当size为2的n次方时成立)。并且由于 UInt 的回环,所以可以直接tail - head,这种设计的一个优势是可以利用环的完整大小,而无需额外管理“环已满”标志。 + +应用程序与内核通过io_uring交互的过程大致如下: + +应用程序通过提交队列SQ将SQE(IO请求)提交给内核,内核操作完成之后通过完成队列CQ将CQE(完成的IO请求)写回给应用程序,应用程序自行处理已完成的事件。 + +SQ 和 CQ 是内核与应用程序共享的内存区域,这样子,我们避免了频繁的用户态和内核态之间的切换,并且支持批量地提交请求,也减少了系统调用的次数,提高了性能。此外,由于 io_uring 可以直接访问用户态提供的缓冲区,避免不必要的内存拷贝操作。 + +## 其他 +看了io_uring的一些资料: + +https://kernel.dk/io_uring.pdf + +https://zhuanlan.zhihu.com/p/361955546 + +稍微看了看: + +https://tony612.github.io/tokio-internals/01.html + + +# 第三周: + +## 实现运行时 + +大体上是参考了 [async-rt-book](https://toetoe55.github.io/async-rt-book/Introduction.html) 和 [maglev](https://github.com/ringbahn/maglev) 这两个写出来的 + +仓库在[my_runtime](https://github.com/ZIYAN137/my_runtime.git) + +## 其他 + +读了点与异步协程运行时以及IO_Uring有关的一些资料 + +一个实验性质的运行时: https://github.com/ringbahn/maglev + +训练营内的一位同学的博客:https://zhuanlan.zhihu.com/p/12850908116 + +rust与io_uring: https://zhuanlan.zhihu.com/p/346219893 + +无栈协程: https://mthli.xyz/coroutines-in-c/ +