coroutine一般翻译过来就是协程,类似于线程可以切换,而跟线程是由操作系统调度器来实现切换不一样,协程由用户程序自己调度进行切换。我以前也看过协程相关的内容,但没有自己去实现过。最近搞OpenStack,OpenStack各个模块都是单线程模型,但是用了eventlet的绿色线程,eventlet也是Python的协程实现库。这篇文章我并不打算剖析Python协程库的实现,而是分析一个基于Linux下ucontext组件的C语言实现,原作者是云风,我以前也看过这个实现,只是现在忘了,没有自己写过或者分析过代码,只是看看好像永远是似懂非懂。后来yanyiwu又fork了一个实现并做些修改,据说更易懂,我就直接拿他修改后的版本分析就ok了,这里对他们表示感谢。

这个简单的实现包含三个文件,分别是头文件coroutine.h,协程实现文件coroutine.c和测试主程序main.c,我给代码加了点注释,并编译运行。

coroutine.h源码:


coroutine.h里面是一些宏定义和函数声明:

coroutine_func:一个函数指针,声明了coroutine的函数原型;

coroutine_open:要使用该协程库时第一个被调用的函数,它返回一个调度器结构体;

coroutine_close:关闭协程调度器,最后被调用不解释;

coroutine_new:将一个函数还有需要传递的参数加入到协程的调度器里边;

coroutine_yield:退出当前运行的协程;

coroutine_resume:恢复具有特定id值的协程;

coroutine_running:返回正在运行的协程id,-1表示没有正在运行的协程;

schedule_status:返回1表示还有等待运行的协程,返回0表示所有协程都已运行完毕;

主要的实现都在coroutine.c文件,源码如下:

对coroutine.c源码我们暂时不作分析,一会儿分析main.c时自然会讲到它。

main.c源码如下:

我们来分析下main.c的代码。先看下main函数,调用了coroutine_open函数,返回一个调度器结构体,然后调用test函数并把调度器结构体当作参数,最后调用coroutine_close函数关闭调度器。显然,test函数就是接脏活累活的地方了。看下test函数里的35,36行,调用了coroutine_new创建两个协程,分别使用了函数foo和foo2,参数分别为arg1和arg2,并返回了协程id,分别为co1和co2。接着是一个while循环,看下代码:

while (schedule_status(S)) {

coroutine_resume(S,co1);
                 coroutine_resume(S,co2);
}

可以看出,当schedule_status返回为1时,将对协程co1和co2分别调用 coroutine_resume函数,schedule_status返回0时test函数退出。这回,我们不得不去看coroutine_resume函数了:

coroutine_resume函数有两个参数,分别为调度器结构体和协程id。该函数首先根据协程id从调度器中获取对应的协程结构体,然后对状态status作判断,可能的状态为COROUTINE_READY和COROUTINE_SUSPEND。

status为COROUTINE_READY(协程第一次被调度)时:

调用getcontext获取当前(注意,当前不是传进来id所对应的协程)协程的上下文,保存在传进来的id所对应的协程结构体中类型为ucontext_t的变量ctx,接着修改ctx结构体的栈指针和栈大小,并把该协程退出时要执行的协程上下文设置成调度器结构体内类型为ucontext_t的变量main,然后将调度器结构体里running变量设置为要将要执行的协程的id,将要执行的协程的状态status设置为COROUTINE_RUNNING,再调用makecontext修改要执行协程上下文,参数为要执行的协程上下文变量、mainfunc函数地址、mainfunc参数个数、给mainfunc传递的参数,因此后续该协程执行时,就会调用mainfunc函数,最后调用swapcontext,该函数将当前协程的上下文内容保存在调度器结构体的main变量中,并激活要执行的协程上下文,于是mainfunc函数被调用了。

status为COROUTINE_SUSPEND时:

将调度器结构体里的变量running设置成传进来的参数id,将该id对应的协程状态status设置成COROUTINE_RUNNING,调用swapcontext保存当前协程上下文,激活执行参数id对应的协程。当协程为这个状态时,肯定是曾经被调度过了,即经历过了COROUTINE_READY阶段,其栈指针已经被修改过,因此不需要再次修改而直接激活执行。

不难看出,每个协程第一次被调度时,都调用了makecontext函数并把mainfunc函数设置成该协程执行时就去调用的函数,因此我们知道,协程co1和co2所对应函数foo和foo2都是在mainfunc中被调用。我们再看下foo和foo2的实现:

这两个函数中都有一个for循环,每循环一次就调用coroutine_yield函数,该函数首先将当前协程的状态status改为COROUTINE_SUSPEND,将调度器结构体里running变量设置为-1,再调用swapcontext将协程上下文保存在当前协程的结构体变量ctx中,激活调度器结构体里main变量对应的协程上下文,这里实际上是切换到了主协程。

说到这里,估计有些同学还是不明不白的,我根据自己的理解具体来解释一下流程:

while循环里边对协程co1调用coroutine_resume时,由于第一次调用进入COROUTINE_READY分支,这时候getcontext获取主协程(不知道描述对不对)的上下文,然后修改栈后作为协程上下文保存在co1对于的协程结构体中,然后mainfunc中执行co1对于的函数foo,在foo中调用了coroutine_yield,这时co1被设置成COROUTINE_SUSPEND,切换到刚才保存的主协程中,这是test函数里边的coroutine_resume又被调用,不过这时是对co2,同样的命运,co2对应的foo2被调度执行,没想到foo2函数也自动将自己设置成COROUTINE_SUSPEND,这时又切换到了主协程中,test中又一次循环开始,coroutine_resume对co1调用,只是这次进入COROUTINE_SUSPEND分支,这回不用设置什么栈了,直接换成执行协程co1,对co2也一样,不再赘述。

那么问题又来了,co1和co2什么时候彻底结束、主程序得以退出呢?

foo和foo2中的for循环次数是有限的,当循环条件不满足时,coroutine_yield函数不会被调用,这时mainfunc中的调用:“C->func(S, C->arg); ”结束,之后的语句“C->status = COROUTINE_DEAD;”被调用,将对应的协程状态status设置为COROUTINE_DEAD。当两个协程状态都为COROUTINE_DEAD时,schedule_status函数返回0,主协程中的while循环退出,主程序就退出了。再看下程序的执行结果,一切都变得明了了。

运行结果:

转载于:https://www.cnblogs.com/woshiweige/p/4518428.html

使用ucontext组件实现的coroutine代码分析相关推荐

  1. 使用Hadoop和ELK进行业务代码分析!分分钟捉到Bug!

    大数据是计算领域的新高地,它有望提供一种方法来应对二十一世纪不断增长的数据生成.越来越多的大数据爱好者正在涌现,越来越多的公司正在采用各种大数据平台,并希望提出以客户为中心的解决方案,帮助他们在竞争激 ...

  2. Pixhawk代码分析-准备

    Pixhawk代码分析准备篇 实验平台 Software Version:ArduCopter(Ver_3.3) Hardware Version:pixhawk IDE:eclipse Juno ( ...

  3. 完整全面的Java资源库(包括构建、操作、代码分析、编译器、数据库、社区等等)...

    构建 这里搜集了用来构建应用程序的工具. Apache Maven:Maven使用声明进行构建并进行依赖管理,偏向于使用约定而不是配置进行构建.Maven优于Apache Ant.后者采用了一种过程化 ...

  4. 20155320《网络对抗》Exp4 恶意代码分析

    20155320<网络对抗>Exp4 恶意代码分析 [系统运行监控] 使用schtasks指令监控系统运行 首先在C盘目录下建立一个netstatlog.bat文件(由于是系统盘,所以从别 ...

  5. php代码静态分析工具,Wpbullet:一款针对WordPress(PHP)的静态代码分析工具

    今天给大家介绍的是一款名叫Wpbullet的工具,广大安全研究人员可以使用这款工具来对WordPress.插件.主题以及其他PHP项目进行静态代码分析. 工具安装 大家可以直接从Wpbullet的Gi ...

  6. 20155301 Exp4 恶意代码分析

    20155301 Exp4 恶意代码分析 实践目标 (1) 是监控你自己系统的运行状态,看有没有可疑的程序在运行. (2) 是分析一个恶意软件,就分析Exp2或Exp3中生成后门软件:分析工具尽量使用 ...

  7. 在SDLC中使用静态代码分析的最佳实践

    http://vultrace.cn更多精彩,尽在个人博客. 文章翻译自ncc group的论文,论文超长预警,请耐心观看. Best Practices for the use of Static ...

  8. ccflow 代码分析

    ccflow 代码分析 目录 概述 需求: 设计思路 实现思路分析 1. 参考资料和推荐阅读 Survive by day and develop by night. talk for import ...

  9. Linux内核分析2:一个简单的时间片轮转多道程序内核代码分析

    Lab2:一个简单的时间片轮转多道程序内核代码 席金玉   <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-100002900 ...

最新文章

  1. 人工智能与大数据开发的12个注意事项
  2. golang 切片 接口_一日看尽golang高级语法之slice
  3. Sereja and Brackets CodeForces - 380C (树状数组+离线)
  4. [005] .NET 的执行模型
  5. IE6/IE7/Firefox浏览器不兼容原因及解决办法
  6. springmvc是什么_SpringBoot与SpringMVC的区别是什么?
  7. 用python重构策略模式
  8. [死亡笔记]某傻逼的错误实况
  9. html5个人简历代码模板,个人简历HTML模板
  10. excalidraw 使用中文手写体
  11. 怎样把word文档里的html格式去掉,word文档去除格式
  12. 《BJBR虚拟仿真解决方案(描述精选)》(Yanlz+SteamVR+5G+AI+VR+AR+MR+HR+BR+CR+DR+ER+FR+GR+人工智能+人机交互+立钻哥哥+==)
  13. 彼得·林奇的25条黄金规则
  14. python手机自动化测试脚本语言_python脚本语言? Appium移动自动化测试
  15. Vue2.0源码解析 - 知其然知其所以然之Vue.use
  16. 火柴棒游戏(暴力枚举)C++
  17. 我的世界服务器物品管道,我的世界漏斗管道怎么做 教你连接漏斗箱子
  18. Leetcode-1094. 拼车
  19. RDO LOST / loss rdo
  20. 基于python马蜂窝网的旅游景点推荐系统

热门文章

  1. java 反射 代码_java反射机制学习代码
  2. 微信公众平台网站开发JS_SDK遇到的bug——wx.config注册提示成功,但部分接口注册失败问题
  3. java 反射 类变量_java反射机制取出model类的所有变量,以及value
  4. 计算机组成原理左规右规,计算机组成原理 第四章(严军勇)-2003-2012年.ppt
  5. 队列的顺序数组c语言代码,队列-队列的顺序表示和实现
  6. 排序算法基本介绍及python实现(含详细注释)
  7. C++(STL):20---deque容器访问元素
  8. 推荐算法--推荐系统架构(06)
  9. 职校学计算机对口高考可以考幼师吗,幼师专业对口高考考那些
  10. 我的世界一个程序导致JAVA,Java地位无可动摇的12个原因