LOADING

加载过慢请开启缓存 浏览器默认开启

协程异步操作系统-学习记录汇总

相关资料

第三阶段选题协商: 训练营项目六第三阶段选题协商

Embassy

异步协程

最佳实践


学习记录

  • 完成了 Embassy文档 的初步学习,并尝试翻译了一部分

  • 对官网内容进行汉化

    • 官网内容较旧,翻译一下新的
  • 理解async,异步Rust,异步操作系统(进行中)

    • 教学视频
    • 200行代码将头Rust Futures
      • 运行代码
    • course.rs Rust圣经 中的 async 相关内容
      • 运行代码
    • Rust Runtime 理解、设计与实现(进行中)⭐
    • 更全面地理解此内容 ⭐
  • 嵌入式异步与 RTOS 的比较

  • Embassy 代码分析(进行中)

    • 大体理解并翻译其中的注释(进行中)
      • embassy-futures ⭐
      • embassy-boot
    • 理解Embassy代码设计(进行中)
  • 环境相关

    • rCore-N 环境的部署
    • rCore-tutorial 环境部署(进行中)⭐
    • 理解rCore-N的源代码
    • 理解rCore-N中与本项目相关的关键部分,如用户态中断,和其他与协程异步相关的内容。
  • 找到一个可以实践的方向 ⭐

学习记录

用户态中断

用户态中断(User Interrupt)指的是能够在用户态注册中断处理函数,并且也能够在用户态触发指定的中断处理函数。

相关内容:0x10.sh/user-interrupt

其他相关资料

x86 用户态中断 - Risc-V Extension N Implementation (gallium70.github.io)
用户态中断 - 知乎 (zhihu.com)
新CPU特性 - User space interrupt - 知乎 (zhihu.com)

CPU硬件、操作系统对并发的支持

单核情况下

  1. 时间片轮转:把CPU时间分割成很小的片段,然后轮流分配给各个运行中的进程使用。每个进程在被分配到一个时间片后,会执行一段时间的任务,然后让出CPU,接着下一个进程获得时间片并执行。这样,虽然在任何一个时刻,CPU只被一个进程使用,但由于时间片的切换速度非常快,使得人们感觉所有的进程都在同时运行。
  2. 中断技术:当CPU正在执行一个进程时,如果发生了中断(比如I/O请求、时钟中断等),CPU会暂停当前进程的执行,转而去处理中断请求。处理完中断后,CPU再回到被中断的进程继续执行。这种技术可以保证高优先级的任务(如I/O处理)能及时得到CPU的处理,从而提高了系统的响应速度和并发性。

多核情况下(对多任务处理)

在单核CPU中,多个线程或进程是并发执行的,即在同一时间段内交替运行,但在任一时刻只有一个线程或进程在执行。

在多核CPU中,可以实现并行执行,即在同一时刻,多个线程或进程在不同的核心上同时执行。一个线程只能挂在一个核上,并且每个核上的所有线程按照时间片轮转调度。

对于计算密集型任务,可以使用核心数个线程,就可以占满CPU资源,进而可以充分利用CPU。对于IO密集型任务(涉及到网络、磁盘IO的任务都是IO密集型任务),线程由于被IO阻塞,如果仍然用核心数个线程,CPU是跑不满的,于是可以使用更多个线程来提高CPU使用率。

既然有多个核心,那么当一个被正在运行的核挂起的线程可以在另一个核上继续运行。不过如果这种任务迁移非常频繁,实际上是对性能有害的,一般操作系统不会频繁地迁移任务。

同时每个核都有自己的缓存,需要一种机制来保证缓存的一致性。这通常通过缓存一致性协议(如MESI协议)来实现。

操作系统

个人理解,操作系统主要是如何更有效地利用CPU,以及负责调度上的问题。

多进程: 操作系统可以创建多个进程,每个进程独立运行,拥有自己的地址空间和资源。这些进程可以并发执行,即在同一时间段内交替运行。

多线程: 在一个进程内部,可以创建多个线程,这些线程共享进程的资源,但可以并发执行。这样,一个进程内的多个线程可以同时执行不同的任务。

中断机制: 操作系统使用中断机制来响应外部设备的请求,如键盘输入、鼠标点击等。当外部设备发出中断请求时,CPU会暂停当前任务,转而处理中断请求。处理完中断请求后,CPU再回到被中断的任务继续执行。

时间片轮转: 操作系统将CPU的运行时间划分为多个小的时间片,并依次分配给每个线程或进程。每个线程(或进程)在所分配的时间片内运行,当时间片用完后,系统内核会剥夺该线程(或进程)的CPU执行权,并将“执行权”交给下一个等待执行的线程或进程。

硬件上下文切换: 为了实现多道程序的分时共享,内核必须使用硬件上下文切换机制。当一个进程的时间片耗尽,内核会保存该进程的状态(即上下文),然后加载另一个进程的上下文,使得CPU可以继续执行新的进程。

其他

电路并行

在搜集整理资料的过程中发现一个问题,在物理上,电路本身就是并行的,并行是电路的天然属性。例子有FPGA
也许可以从其实现中获得更多启发,但本人未进行深入研究。

问题

轮询,硬件怎么做,为什么硬件做效率更高?

轮询:

轮询是一种CPU决策如何提供周边设备服务的方式。
在轮询过程中,由CPU定时发出询问,依序询问每一个周边设备是否需要其服务。
每个设备都有一个指示命令就绪的位,指示该设备的状态。
当此状态就绪即给予服务,服务结束后再问下一个周边,接着不断周而复始。

硬件轮询是由硬件直接进行的,它可以在硬件级别上快速检查设备状态,而无需占用CPU资源。
这种方式在处理高频请求或大量数据请求的设备时,效率更高。
硬件轮询可以减少系统的复杂性,因为它不需要复杂的中断处理和优先级管理。

有用户态中断之前,程序死循环了,怎么让它停下来。Ctrl + C是怎么做到的?
终端在接收到Ctrl + C后向前台进程发送SIGINT信号,而进程通常会在接收到这个信号后终止

相关资料:
关于硬件中断:一文讲透计算机的“中断”

选中断还是轮询方式?深究其中的区别

编程语言对并发的支持

当今常用的编程语言几乎均原生自带一套实现并发的方案,同时三方库也会提供很多很多的并发方式。

相关资料:

编程语言是如何实现并发的之并发模型篇 · BMPI

JAVA并发编程知识总结(全是干货超详细)

内核态下和用户态下的线程

进程地址空间独立。存有页表切换开销。
线程有独立的堆栈,切换时保存和恢复全部的寄存器。
内核与用户线程不在相同的地址空间,用户线程只有用户栈,内核线程只有线程栈。

相关资料:

线程用户态和内核态-腾讯云开发者社区

有栈协程与无栈协程

协程

协程是一种比线程更加轻量级的存在,一个线程可以有多个协程。当线程内的某一个协程运行时,其它协程必须挂起。由于协程切换是在线程内完成的,涉及到的资源比较少。可以简单理解为只是切换了寄存器和协程栈的内容。这样代价就非常小。

和传统的线程不同的是:线程是抢占式执行,当发生系统调用或者中断的时候,交由OS调度执行;而协程是通过yield 主动让出 CPU 所有权,切换到其他协程执行。

个人理解:

协程也有多种实现方式,如生成器,async/await等。

在有大量IO操作业务的情况下,我们采用协程替换线程,可以到达很好的效果,一是降低了系统内存,二是减少了系统切换开销,因此系统的性能也会提升。

在协程中尽量不要调用阻塞IO的方法,比如打印,读取文件,Socket接口等,除非改为异步调用的方式,并且协程只有在IO密集型的任务中才会发挥作用。

协程只有和异步IO结合起来才能发挥出最大的威力。

相关资料:
协程是什么?怎么来的?它有什么作用?_协程的作用
什么是协程?-腾讯云开发者社区
什么是协程?

有栈协程

有栈协程就是一种用户态线程

在 python, Golang 中就有这样的实现。

相关资料:

从头到尾理解有栈协程实现原理
手把手教你实现有栈协程和协程调度器

无栈协程

stacklesscoroutine

无栈协程是一种特殊类型的协程,它不需要为每个协程分配一个独立的栈空间。这种设计可以大大减少内存使用,使得可以创建大量的协程。

无栈协程的实现主要基于状态机(state machine)的概念。无栈协程的本质就是在另一个角度去看问题,即同一协程协程的切换本质不过是指令指针寄存器的改变。无栈协程常常使用生成器来实现,生成器只负责生成数据。

无栈协程的优点是内存使用更加高效,可以创建大量的协程。但是,由于无栈协程没有自己的栈,所以它们不能直接调用其他函数。这就意味着无栈协程需要使用特殊的编程技巧,如状态机,来实现复杂的逻辑。

在 JavaScript 中的 Promise, 以及 async/await 可以实现看起来像无栈协程的行为。

无栈协程C++实现

#include <iostream>

void coro_func() {
    static int state = 0;
    switch(state) {
        case 0: goto LABEL0;
        case 1: goto LABEL1;
        case 2: goto LABEL2;
    }
    LABEL0:
    for(;;) {
        state = 1;
        return;
        LABEL1:
        state = 2;
        std::cout << "Hello, World!" << std::endl;
        return;
        LABEL2:
    }
}

int main() {
    for(int i = 0; i < 10; ++i) {
        coro_func();
    }
}

注意存在一个无限循环进行轮询的,后续可以考虑去掉

相关资料:

浅谈有栈协程与无栈协程

如何在C++17中实现stackless coroutine以及相关的任务调度器
c/c++ 无栈协程原理是什么, 该如何实现?

异步

异步编程是一种编程模式,它允许操作在后台运行,而不会阻塞主线程的执行。这种模式在处理I/O操作,如网络请求或读写文件时特别有用,因为这些操作可能需要花费大量时间来完成,而在此期间,我们不希望主线程被阻塞。

相关资料:

从无栈协程到C++异步框架

#main.rs

use std::time::Duration;
use tokio::time;

async fn my_coroutine() {
    time::sleep(Duration::from_secs(1)).await;
    println!("Coroutine finished");
}

#[tokio::main]
async fn main() {
    my_coroutine().await;
}


#Cargo.toml
[dependencies]
tokio = { version = "1", features = ["full"] }

操作系统与异步

Linux原生异步IO原理与实现(Native AIO)

如何深刻地理解 Unix/Linux 中同步 IO 和异步 IO?

有关单线程如何实现异步?

异步IO - 廖雪峰的官方网站

进程间通讯方式

一文搞懂六大进程通信机制原理(全网最详细)

协程异步操作系统-学习记录一

协程异步操作系统-学习记录二