前言

前段时间研读云风的coroutine库,为了加深印象,做个简单的笔记。不愧是大神,云风只用200行的C代码就实现了一个最简单的协程,代码风格精简,非常适合用来理解协程和用来提升编码能力。

协程简介

协程是用同步的写法达到异步的性能。其基本原理是在IO等待时切换出去,在适当的时刻切换回来,最大程度利用CPU。协程可以理解为一个用户级的线程,一个线程里跑多个协程。并且,不管协程数量多少,都是串行运行的,就是说不存在同一时刻属于一个线程的不同协程同时运行。因此避免了多线程编程可能导致的同步问题。

协程的行为有点像函数调用,但也有不同,对于函数调用来说,假如函数A调用函数B,则必须等待函数B执行完毕后才能重新返回A,但对于协程来说,如果再协程A中切换到协程B,协程B可以选择在某个点重新回到A的执行流,同时允许在某个时刻重新从A回到B之前运行到的那个点。这在函数中是不可能实现的,因为函数只能一路走到底。

ucontext实现协程切换

既然允许协程中途中切换以及后期从新从切换点进入继续执行,说明必须有数据结构保存每个协程的上下文信息。云风携程库运用Linux中的ucontext实现协程见切换,而Linux包含以下几个系统函数对ucontext_t进行初始化、设置,以及基于ucontext_t切换协程:

getcontext()  : 获取当前context
setcontext() : 切换到指定context
makecontext() : 设置函数指针和堆栈到对应context保存的sp和pc寄存器中,调用之前要先调用 getcontext()
swapcontext() : 保存当前context,并且切换到指定context

主要数据结构

源码设计的数据结构有如下两个,coroutine协程保存自身的上下文信息、主体函数和栈信息等。每个协程需要自己主动让出CPU,至于交给谁处理,由schedule调度器决定,调度器管理协程,包括保存和切换协程。

struct schedule {
char stack[STACK_SIZE]; //栈空间
ucontext_t main; //当前上下文
int nco; //协程数
int cap; //协程容量
int running; //是否正在运行
struct coroutine **co; //协程数组
}; struct coroutine {
coroutine_func func; //协程运行主体函数
void *ud; //func的参数
ucontext_t ctx; //该协程的上下文信息
struct schedule *sch; //对应的调度器
ptrdiff_t cap; //协程大小
ptrdiff_t size; //协程实际大小
int status; //运行状态
char *stack; //栈
};

保存现场

协程从运行状态COROUTINE_RUNNING到暂停状态COROUTINE_SUSPEND时需要保存运行栈,即调用coroutine_yield 之后挂起协程让出CPU的过程。下面是保存栈方法:

static void
_save_stack(struct coroutine *C, char *top) {
//获取当前栈底,top 是栈顶,top-dummy 即该协程的私有栈空间
char dummy = ;
assert(top - &dummy <= STACK_SIZE);
//如果协程私有栈空间大小不够放下运行时的栈空间,则要重新扩容
if (C->cap < top - &dummy) {
free(C->stack);
C->cap = top-&dummy;
C->stack = malloc(C->cap); //每一个协程都会开辟这块栈空间
}
C->size = top - &dummy;
memcpy(C->stack, &dummy, C->size);
}

top 代表当前协程运行栈的栈顶,从 coroutine_yield 我们知道 top = S->stack + STACK_SIZE,原因是协程初始化时设置如下:

getcontext(&C->ctx);
C->ctx.uc_stack.ss_sp = S->stack;
C->ctx.uc_stack.ss_size = STACK_SIZE;
C->ctx.uc_link = &S->main;
makecontext(&C->ctx, (void (*)(void)) mainfunc, , (uint32_t)ptr, (uint32_t)(ptr>>));

即表示协程栈的栈顶设置为S->stack,mainfunc的运行使用S->stack作为栈顶,大小为STACK_SIZE,由此可见,schedule 的 stack[STACK_SIZE] 是子协程运行的公共栈空间,但是每个协程的栈不一样,所以需要单独建立一个私有栈空间来保存执行现场。

https://blog.csdn.net/u011228889/article/details/79759834

云风协程库coroutine源码分析的更多相关文章

  1. cloudwu/coroutine 源码分析

    1 与其它协程库使用对比 这个 C 协程库是云风(cloudwu) 写的,其接口风格与 Lua 协程类似,并且都是非对称 stackful 协程.这个是源代码中的示例: #include " ...

  2. 文件解析库doctotext源码分析

    doctotext中没有make install选项,make后生成可执行文件 在buile目录下面有.so动态库和头文件,需要的可以从这里面拷贝 build/doctotext就是可执行程序.   ...

  3. Python之contextlib库及源码分析

    Utilities for with-statement contexts __all__ = ["contextmanager", "closing", &q ...

  4. Duilib源码分析(一)整体框架

    Duilib界面库是一款由杭州月牙儿网络技术有限公司开发的界面开源库,以viksoe项目下的UiLib库的基础上开发(此后也将对UiLib库进行源码分析):通过XML布局界面,将用户界面和处理逻辑彻底 ...

  5. python 协程库gevent学习--源码学习(一)

    总算还是要来梳理一下这几天深入研究之后学习到的东西了. 这几天一直在看以前跟jd对接的项目写的那个gevent代码.为了查错,基本上深入浅出了一次gevent几个重要部件的实现和其工作的原理. 这里用 ...

  6. python 协程库gevent学习--gevent源码学习(二)

    在进行gevent源码学习一分析之后,我还对两个比较核心的问题抱有疑问: 1. gevent.Greenlet.join()以及他的list版本joinall()的原理和使用. 2. 关于在使用mon ...

  7. 写个百万级别full-stack小型协程库——原理介绍

    其实说什么百万千万级别都是虚的,下面给出实现原理和测试结果,原理很简单,我就不上图了: 原理:为了简单明了,只支持单线程,每个协程共享一个4K的空间(你可以用堆,用匿名内存映射或者直接开个数组也都是可 ...

  8. Stackful 协程库 libgo(单机100万协程)

    libgo 是一个使用 C++ 编写的协作式调度的stackful协程库, 同时也是一个强大的并行编程库. 设计之初是为高并发分布式Linux服务端程序开发提供底层框架支持,可以让链接进程序的同步的第 ...

  9. 一个“蝇量级” C 语言协程库

    协程(coroutine)顾名思义就是“协作的例程”(co-operative routines).跟具有操作系统概念的线程不一样,协程是在用户空间利用程序语言的语法语义就能实现逻辑上类似多任务的编程 ...

随机推荐

  1. Spring——JdbcTemplate

    一.JdbcTemplate介绍: 为了使 JDBC 更加易于使用,Spring 在 JDBCAPI 上定义了一个抽象层, 以此建立一个JDBC存取框架,Spring Boot Spring Data ...

  2. Centos7下设置MySql自动启动

    原文链接:http://www.cnblogs.com/Sungeek/p/9687565.html 1.将服务文件拷贝到init.d下,并重命名为mysql cp /usr/local/mysql/ ...

  3. linux 查看系统性能

    1. 查看内存和CPU信息 cat /proc/cpuinfo                   cpu信息 cat /proc/meminfo |grep MemTotal    内存信息 查看物 ...

  4. [CSP-S模拟测试]:Drink(模拟)

    题目传送门(内部题10) 输入格式 输入第一行三个数$N,M,Q$分别表示棋盘的行数.列数和操作个数.接下来$N$行每行$M$个数表示一开始棋盘上宝物的价值.接下来$Q$行每行$3$个数$x,y,c$ ...

  5. DB 分库分表的基本思想和切分策略

    DB 分库分表的基本思想和切分策略 一.基本思想 Sharding的基本思想就要把一个数据库切分成多个部分放到不同的数据库(server)上,从而缓解单一数据库的性能问题.不太严格的讲,对于海量数据的 ...

  6. Linux :vim 模式下的常用命令

    [参考文章]:vim 复制一整行 复制多行 1. 查找命令 ?text    查找text,按n健查找下一个,按N健查找前一个 /text     反向查找text,按n健查找下一个,按N健查找前一个 ...

  7. 私有npm计划

    为什么要建立私有npm 提高代码复用程度,增加团队沉淀 剥离项目依赖,工程更加轻量 引用全量更新,支持版本降级 建立模块文档,降低上手难度 全员把关代码质量,无需重复测试 构建工具已成趋势,优化发布流 ...

  8. ubuntu 18.04 64bit下如何安装安卓虚拟机anbox?

    一. 安装snapd sudo apt-get install snapd 二. 安装adb sudo apt-get install adb 三. 安装必要的内核模块 wget https://la ...

  9. pandas中的reset_index()

    数据清洗时,会将带空值的行删除,此时DataFrame或Series类型的数据不再是连续的索引,可以使用reset_index()重置索引. import pandas as pd import nu ...

  10. mysql|full join 多表联查,系统报错,无法解答!

    查询语句: select 分数 from cfull join don c.姓名=d.姓名 报错: [Err] 1054 - Unknown column 'c.姓名' in 'on clause' ...