认识协程1

基本概念

协程可以理解成能暂停和恢复的函数。
网上那些轻量级线程、用户态线程的描述更贴近于有栈协程。

有栈协程和无栈协程

有栈协程和无栈协程的主要区别在于它们对栈空间的处理方式。栈是一个数据集合,用于存储函数调用的局部变量和返回地址等信息。
有栈协程为每个协程分配独立的栈空间。这意味着每个协程都有自己的数据存储空间,可以自由地在其栈上进行函数调用、局部变量存储等操作,而不会干扰到其他协程的执行。这种设计使得有栈协程的调用机制更类似于线程的调度,需要保存和恢复相关上下文(主要是寄存器状态),从而保证了整个协程运行的安全性。形象地说,有栈协程就像是拥有独立房间的客人,他们可以在自己的房间里自由活动,互不干扰。
无栈协程则不拥有独立的栈空间,而是所有协程共享系统栈空间。这意味着无栈协程需要计算每一个协程在栈上的位置,以确保它们不会相互覆盖或破坏数据。无栈协程通常使用生成器来实现,生成器只负责生成数据,而不具备数据栈。无栈协程在运行时需要仔细管理栈空间的使用,以避免冲突和错误。这可以形象地理解为无栈协程是共用一个大厅的客人,他们必须非常小心地行动,以免踩到彼此的脚或碰撞到对方的物品。
从性能和效率的角度看,有栈协程和无栈协程在理论上相差不大。有栈协程的效率相对较高,因为它们可以直接在独立的栈上进行操作,无需进行复杂的栈空间管理。然而,无栈协程通过共享栈空间可以减少内存消耗,这在某些资源受限的场景下可能是一个优势。此外,无栈协程的实现通常更加简单和灵活,因为它们不需要处理栈空间的分配和释放等复杂问题。
总结来说,有栈协程和无栈协程的主要区别在于栈空间的处理方式。有栈协程为每个协程提供独立的栈空间,保证了安全性和灵活性;而无栈协程则共享系统栈空间,需要仔细管理以避免冲突和错误。在选择使用哪种协程时,需要根据具体的应用场景和需求进行权衡。

栈作为常用数据结构的一种,特性就不重复了。
这里主要说在linux进程地址空间布局的结构和作用。在程序运行过程中,栈保存了一个函数调用所需要维护的信息,被称为堆栈帧,一般包括以下几个内容:
1、函数的返回地址和参数
2、临时变量:包括函数的非静态局部变量及编译器自动生成的其它临时变量
3、保存的上下文:包括在函数调用前后需要保存不变的寄存器的值
一个函数的堆栈帧用 [E|R]SP,[E|R]BP ,这两个寄存器划定范围,[E|R]SP始终指向栈顶的位置称为栈指针寄存器,[E|R]BP指向堆栈帧的一个固定位置,[E|R]BP 又被称为帧指针,一般函数中的局部变量靠 [E|R]BP 加上偏移量寻找。
[E|R]SP表示esp或者rsp寄存器,esp表示32位x86架构下的栈指针寄存器,rsp表示64位x86架构下的栈指针寄存器,同理于[E|R]BP。 帧指针并不是必须的, x86-64过程中的栈帧通常有固定的大小,在调用过程中栈指针保持固定的位置,使得可以通过相对于栈指针的偏移量来访问数据^1。
举个例子来描述一下栈的增长:
有一个foo函数

int foo(int m, int n){
  int a = 0; // #i
  ....
}
堆栈帧增长过程:

1、把所有或者一部分参数压入栈中,如果有其他参数没有入栈,则使用某些寄存器传递
2、把当前指令的下一条指令压入栈中
3、跳转到函数体执行
4、把[e|r]bp压入栈中,指向上一个函数堆栈帧中帧指针的位置
5、保存调用前后需要保存不变的寄存器的值
6、将局部变量压入栈中

函数调用返回后,相应的函数堆栈帧也会弹出,弹出的流程不细究
在这里插入图片描述

线程

一个标准的线程由线程id、程序计数器(PC)、寄存器集合和栈组成
每个线程类似一个独立进程,不同的是线程之间共享地址空间,能够访问到相同的数据。
在这里插入图片描述

线程的上下文切换

有两个线程运行在一个处理器上,从运行一个线程T1切换到另一个线程T2时,需要切换上下文。对于进程而言,状态都是保存在进程控制块PCB中,现在对线程切换也需要一个线程控制块TCB来保存每个线程的状态,但是和进程的切换相比,线程在上下文切换的时候地址空间保持不变,不需要切换当前使用的页表。

协程的理论依据

有栈协程就是模拟出一个协程的栈空间,当需要进行执行流的的上下文切换时,主线程只需要交换栈空间和恢复协程的一些相关寄存器状态就可以实现一个用户态的线程切换,没有了线程切换到内核态的开销。

相较于有栈协程自己管理的函数运行栈空间(独立栈或者共享栈),无栈协程不需要对这些空间做管理,但是无栈协程需要一个frame(协程帧)保存该协程的运行状态。协程帧主要保存参数和局部变量的值,在让出执行权的时候能保存运行状态和执行流节点。像邓子峰的协程视频中使用fib_frame保存ab变量值还有state状态,每次重新进入fib根据state值跳转到对应的执行流。

ucontext

在类unix环境中,<ucontext.h>头文件定义了两结构体mcontext和ucontext_t,四个函数getcontext、setcontext、makecontext和swapcontext。基于此我们可以在一个进程中实现用户级的线程切换。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值