ucontext-人人都可以实现的简单协程库
ucontext的介绍
http://blog.csdn.net/qq910894904/article/details/41911175
协程的介绍
https://en.wikipedia.org/wiki/Coroutine
风云的c库
http://blog.codingnow.com/2012/07/c_coroutine.html
https://github.com/cloudwu/coroutine/
腾讯的开源c++库
https://code.csdn.net/Tencent/libco/tree/master
微信的coroutine的应用
http://www.58maisui.com/2016/06/16/a-210/
http://blog.csdn.net/brainkick/article/details/48676403
http://opensource.tencent.com/libco.html
getcontext makecontext setcontext swapcontext介绍
http://blog.amalcao.me/blog/2014/07/10/cxie-cheng-zhi-ucontextpian-shang/
https://github.com/zfengzhen/Blog/blob/master/article/ucontext%E7%B0%87%E5%87%BD%E6%95%B0%E5%AD%A6%E4%B9%A0.md
所谓 “ucontext” 机制是 GNU C 库提供的一组用于创建、保存、切换用户态执行“上下文”(context)的API,可以看作是 “setjmp/long_jmp” 的“升级版”,主要包括以下四个函数:
|
结构体 ucontext_t
和上述4个函数声明一起定义在系统头文件<ucontext.h>
中,该类型的具体实现与体系结构相关,但规范要求其至少要包含以下字段:
|
其中 sigset_t
和 stack_t
定义在标准头文件 <signal.h>
中, uc_link
字段保存当前context结束后继续执行的context记录, uc_sigmask
记录该context运行阶段需要屏蔽的信号,uc_stack
是该context运行的栈信息, 最后一个字段uc_mcontext
则保存具体的程序执行上下文——如PC值、堆栈指针、寄存器值等信息——其实现方式依赖于底层运行的系统架构,是平台、硬件相关的。
下面具体来看每个函数的功能:
int makecontext(ucontext_t *ucp, void (*func)(), int argc, ...)
该函数用以初始化一个ucontext_t类型的结构,也就是我们所说的用户执行上下文。函数指针func指明了该context的入口函数,argc指明入口参数个数,该值是可变的,但每个参数类型都是int型,这些参数紧随argc传入。 另外,在调用makecontext之前,一般还需要显式的指明其初始栈信息(栈指针SP及栈大小)和运行时的信号屏蔽掩码(signal mask)。 同时也可以指定uc_link字段,这样在func函数返回后,就会切换到uc_link指向的context继续执行。int setcontext(const ucontext_t *ucp)
该函数用来将当前程序执行线索切换到参数ucp
所指向的上下文状态,在执行正确的情况下,该函数直接切入到新的执行状态,不再会返回。比如我们用上面介绍的makecontext初始化了一个新的上下文,并将入口指向某函数entry()
,那么setcontext成功后就会马上运行entry()
函数。int getcontext(ucontext_t *ucp)
该函数用来将当前执行状态上下文保存到一个ucontext_t结构中,若后续调用setcontext或swapcontext恢复该状态,则程序会沿着getcontext调用点之后继续执行,看起来好像刚从getcontext函数返回一样。 这个操作的功能和setjmp所起的作用类似,都是保存执行状态以便后续恢复执行,但需要重点指出的是:getcontext函数的返回值仅能表示本次操作是否执行正确,而不能用来区分是直接从getcontext操作返回,还是由于setcontext/swapcontex恢复状态导致的返回,这点与setjmp是不一样的。int swapcontext(ucontext_t *oucp, ucontext_t *ucp)
理论上,有了上面的3个函数,就可以满足需要了(后面讲的libgo就只用了这3个函数,而实际只需setcontext/getcontext就足矣了),但由于getcontext不能区分返回状态,因此编写上下文切换的代码时就需要保存额外的信息来进行判断,显得比较麻烦。 为了简化切换操作的实现,ucontext 机制里提供了swapcontext这个函数,用来“原子”地完成旧状态的保存和切换到新状态的工作(当然,这并非真正的原子操作,在多线程情况下也会引入一些调度方面的问题,后面会详细介绍)。 为了进一步理解swapcontext这个函数的设计目的,可以尝试利用getcontext/setcontext完成同样的功能,你需要怎样编写代码? 同时,也不妨思考一下下面这段代码的执行结果(该例出自维基百科Setcontext 条目):
|
小结
可以看出,用ucontext机制实现一个“协程”系统并不困难。 实际上,每个运行上下文(ucontext_t)就直接对应于“协程”概念,对于协程的“创建”(Create)、“启动” (Spawn)、“挂起” (Suspend)、“切换” (Swap)等操作,很容易通过上面的4个API及其组合加以实现,需要的工作仅在于设计一组数据结构保存暂不运行的context结构,提供一些调度的策略即可。 这方面的开源实现有很多,其中最著名的就是Go的前身,libtask库。
对于将“协程”映射到多OS线程执行的情形,就要稍稍复杂一些,但主要的问题是集中在共享任务队列的实现、调度线程间的互斥等,至于“协程”的映射问题,与单线程情况没有太大的区别。 对于这方面的开源借鉴,当然首推Go的运行时 —— 但由于标准Go实现没有使用GNU C库,而是自行设计了包括C编译器在内的整套工具链,因而就没有直接采用ucontext机制(尽管其内部实现机制与ucontext原理类似)。
以后有机会,会再分析一下GCC Go语言前端的运行时实现——libgo。 libgo的调度器部分基本用C开发并由GCC编译,“goroutine”(Go语言中相对于“协程”的概念)也直接以“ucontext”机制实现,其代码对于分析C语言下“协程”系统实现方法而言,具有较高的参考价值。
ucontext-人人都可以实现的简单协程库的更多相关文章
- 写个百万级别full-stack小型协程库——原理介绍
其实说什么百万千万级别都是虚的,下面给出实现原理和测试结果,原理很简单,我就不上图了: 原理:为了简单明了,只支持单线程,每个协程共享一个4K的空间(你可以用堆,用匿名内存映射或者直接开个数组也都是可 ...
- 一个“蝇量级” C 语言协程库
协程(coroutine)顾名思义就是“协作的例程”(co-operative routines).跟具有操作系统概念的线程不一样,协程是在用户空间利用程序语言的语法语义就能实现逻辑上类似多任务的编程 ...
- C高级 跨平台协程库
1.0 协程库引言 协程对于上层语言还是比较常见的. 例如C# 中 yield retrun, lua 中 coroutine.yield 等来构建同步并发的程序. 本文就是探讨如何从底层实现开发级别 ...
- 基于ASIO的协程库orchid简介
什么是orchid? orchid是一个构建于boost库基础上的C++库,类似于python下的gevent/eventlet,为用户提供基于协程的并发模型. 什么是协程: 协程,即协作式程序,其思 ...
- Stackful 协程库 libgo(单机100万协程)
libgo 是一个使用 C++ 编写的协作式调度的stackful协程库, 同时也是一个强大的并行编程库. 设计之初是为高并发分布式Linux服务端程序开发提供底层框架支持,可以让链接进程序的同步的第 ...
- libco协程库上下文切换原理详解
缘起 libco 协程库在单个线程中实现了多个协程的创建和切换.按照我们通常的编程思路,单个线程中的程序执行流程通常是顺序的,调用函数同样也是 “调用——返回”,每次都是从函数的入口处开始执行.而li ...
- CPU的最小执行单位是线程,协程不需要qt支持...直接用现成的协程库就行了
协程也就在I/O操作上才有优势,Qt事件循环,本事很多I/O已经是异步了,利用好异步(虽然都说异步有点反人类思维).因为CPU的执行最小单位是线程,协程也只是在其之上又调度而已. 我的意思是利用好异步 ...
- 协程库st(state threads library)原理解析
协程库state threads library(以下简称st)是一个基于setjmp/longjmp实现的C语言版用户线程库或协程库(user level thread). 这里有一个基本的协程例子 ...
- libaco: 一个极速的轻量级 C 非对称协程库 🚀 (10 ns/ctxsw + 一千万协程并发仅耗内存 2.8GB + Github Trending)
0 Name 简介 libaco - 一个极速的.轻量级.C语言非对称协程库. 这个项目的代号是Arkenstone
随机推荐
- wpf 中Listbox获取选中的值
布局代码: <ListBox Name="listBox1" Width="120" Height="52" SelectionC ...
- 有哪些 Bootstrap 的学习案例?
bootstrap经典实用案例 bootstrap经典实用案例(非常详细),从菜鸟到高手的过程是艰辛的,你渴望救助.这本教程就是你无言的助手,默默的帮你到永远. 带奋斗一起飞翔,因为有了它,让我拥有理 ...
- Spring 单元测试 RequestContextHolder.getRequestAttributes()).getRequest(); 为空的原因
原因是因为单元测试时没有加载 web.xml 中的: <!-- spring在service层获取session和request需要创建的监听器 --> <listener> ...
- BZOJ3601. 一个人的数论(狄利克雷卷积+高斯消元)及关于「前 $n$ 个正整数的 $k$ 次幂之和是关于 $n$ 的 $k+1$ 次多项式」的证明
题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=3601 题解 首先还是基本的推式子: \[\begin{aligned}f_d(n) &a ...
- Git命令简单总结
集中式vs分布式 svn集中式:版本库是集中存放在中央服务器的,需要联网才能工作 git 分布式:每个人的电脑上都是一个完整的版本库 和集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为 ...
- Windows Forms Application Creation and Initialization
Windows Forms Application Creation and Initialization This topic details the steps performed after a ...
- Security Permissions Caching
Security Permissions Caching Security permission caching is implemented in Security Adapters - class ...
- NO 18---- webpack 4.x 使用遇到的问题以及开发配置
最近在项目中用webpack的过程中老是出现问题,很是纳闷,按理说一直这样使用没有问题啊,经过我研究后发现,是因为在webpack更新到4.x之后,操作方式与之前相比变化很大.而我们使用npm默认安装 ...
- Memcached服务器上实现多个实例(约约问题排查)
约约测试服上出行一个问题,司机收车失败. (1)经查看代码是null指针异常. 针对,之前,同套代码发布到华威测试服,未出现该问题,遂认定不是代码问题. (2)打印异常信息,获取null值异常的收车司 ...
- 工作小应用:EXCEL查找两列重复数据
工作案例:excel存在A列.B列,需要找出B列没有A列的数据,具体做法如下(以office2007做案例): 1.点击 公式-定义名称 ,选中A列,填写名称“AAA”,选中B列,填写名称“BBB”: ...