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
随机推荐
- POJ-2299 Ultra-QuickSort (树状数组)
题目链接:Ultra-QuickSort 题意: 给出了一个序列,序列中有n个数,现在每次操作能交换相邻的两个数,要求操作几次可以将这个序列转换为一个从小到大排序的序列. 题解: 我的解法是先把所有的 ...
- python面试题(二)
最近参加了几场招聘,发现好多人的一些基础知识不是很扎实,做的题很多都是错误的,因此找了一些我们公司面试过程中的一些最基本的面试题供大家参考,希望各位都能找到一个好的工作.今天给大家先分享的是关于Pyt ...
- 总结几种常见web攻击手段及其防御方式
本文简单介绍几种常见的攻击手段及其防御方式 XSS(跨站脚本攻击) CSRF(跨站请求伪造) SQL注入 DDOS web安全系列目录 总结几种常见web攻击手段极其防御方式 总结几种常见的安全算法 ...
- hdu1754 I Hate It(线段树单点更新,区间查询)
传送门 有更新单个学生成绩和查询某个区间内学生成绩最大值两种操作 线段树代码 #include<bits/stdc++.h> using namespace std; +; using n ...
- JMeter各个基础组件简介
刚从LoadRunner转到JMeter,对JMeter的各种概念比较懵.在这里记录下.欢迎大家关注我的个人微信号:测试杂货铺. JMeter的各个功能都是它的组件来完成或实现的,下面来对JMeter ...
- Git报错:Your branch is ahead of 'origin/master' by 1 commit
. commit之后,用git status,打印信息为: # On branch master # Your branch is ahead of 'origin/master' by 1 c ...
- PSO算法的改进(参数)
## 基本PSO的改进 虽然粒子群在求解优化函数时,表现了较好的寻优能力:通过迭代寻优计算,能够迅速找到近似解:但基本的PSO容易陷入局部最优,导致结果误差较大. 两个方面:1.将各种先进理论引入到P ...
- github在版本库中删除某个文件的所有历史记录
github的目的就是版本控制,记录每一个版本的变动.然而有的时候我们往往希望从版本库中彻底删除某个文件,不再显示在历史记录中.例如不小心上传了一堆错误的文件,或者不小心上传了帐号.密码,那么这个时候 ...
- 前端_html
目录 HTML介绍 标签说明 常用标签 <!DOCTYPE>标签 <head>内常用标签 <body>内常用标签 特殊字符 其他:各种各样的标签 HTML的规范 H ...
- Django之自带认证
自带登录实例 {% extends "layout/base.html" %} // 所有link {% block body %} <div id="contai ...