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” 的“升级版”,主要包括以下四个函数:

void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);
int swapcontext(ucontext_t *oucp, ucontext_t *ucp);
int getcontext(ucontext_t *ucp);
int setcontext(const ucontext_t *ucp);

结构体 ucontext_t 和上述4个函数声明一起定义在系统头文件<ucontext.h> 中,该类型的具体实现与体系结构相关,但规范要求其至少要包含以下字段:

typedef struct ucontext {
struct ucontext *uc_link;
sigset_t uc_sigmask;
stack_t uc_stack;
mcontext_t uc_mcontext;
...
} ucontext_t;

其中 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 条目):

#include <stdio.h>
#include <ucontext.h>
#include <unistd.h> int main(int argc, char *argv[]) {
ucontext_t context; getcontext(&context);
puts("Hello world");
sleep(1);
setcontext(&context);
return 0;
}

小结

可以看出,用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-人人都可以实现的简单协程库的更多相关文章

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

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

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

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

  3. C高级 跨平台协程库

    1.0 协程库引言 协程对于上层语言还是比较常见的. 例如C# 中 yield retrun, lua 中 coroutine.yield 等来构建同步并发的程序. 本文就是探讨如何从底层实现开发级别 ...

  4. 基于ASIO的协程库orchid简介

    什么是orchid? orchid是一个构建于boost库基础上的C++库,类似于python下的gevent/eventlet,为用户提供基于协程的并发模型. 什么是协程: 协程,即协作式程序,其思 ...

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

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

  6. libco协程库上下文切换原理详解

    缘起 libco 协程库在单个线程中实现了多个协程的创建和切换.按照我们通常的编程思路,单个线程中的程序执行流程通常是顺序的,调用函数同样也是 “调用——返回”,每次都是从函数的入口处开始执行.而li ...

  7. CPU的最小执行单位是线程,协程不需要qt支持...直接用现成的协程库就行了

    协程也就在I/O操作上才有优势,Qt事件循环,本事很多I/O已经是异步了,利用好异步(虽然都说异步有点反人类思维).因为CPU的执行最小单位是线程,协程也只是在其之上又调度而已. 我的意思是利用好异步 ...

  8. 协程库st(state threads library)原理解析

    协程库state threads library(以下简称st)是一个基于setjmp/longjmp实现的C语言版用户线程库或协程库(user level thread). 这里有一个基本的协程例子 ...

  9. libaco: 一个极速的轻量级 C 非对称协程库 🚀 (10 ns/ctxsw + 一千万协程并发仅耗内存 2.8GB + Github Trending)

    0 Name 简介 libaco - 一个极速的.轻量级.C语言非对称协程库. 这个项目的代号是Arkenstone 

随机推荐

  1. IDEA/Git 设置多个push远程仓库或者同时提交多个push仓库

    注:写在最上面的这个提交地址将会是唯一的pull地址 具体解决办法: 在隐藏文件.git 下有个config文件,打开,在最后一行添加以下信息 [remote "all"] url ...

  2. angularjs 常用方法

    一 angular的copy和extend 1.angular.extend() angular.extend():依次将第二个参数及后续的参数的第一层属性(不管是简单的属性还是对象)拷贝,赋给第一个 ...

  3. 菜鸟vimer成长记——第4.0章、Vim插件管理利器-Vundle

    定义 Vundle是vim bunler和简称,它是一个vim插件管理器. Vim本身缺乏对插件的有效管理,安装插件并配置.vimrc文件非常不便.gmarik受到Ruby的bunler的启发,开发了 ...

  4. 推荐11个实用Python库

    1.delorea 非常酷的日期/时间库 from delorean import Delorean EST = "US/Eastern"d = Delorean(timezone ...

  5. Bitcoin区块链攻击方式

    目录 重放攻击-- 非人为攻击 其他攻击 重放攻击-- 非人为攻击 重放攻击 Replay Attach 攻击者重复发送相同的数据库包到目的主机,用以欺骗系统 用支付宝付款信息重复项商家索取商品 比特 ...

  6. Revit开发小技巧——撤销操作

    最近开发Revit命令需要限制某些操作,思路是监控用户操作,如果达到限制条件,将操作回退.思路有两种: 1.调用WindowsAPI,发送快捷命令Ctrl+Z. 2.通过Revit底层提供DLL找到回 ...

  7. 你这一辈子要用到的C数学函数都在这

       两数相加  #include <stdio.h> int main(void){ int a = 10;  //定义变量a, 把10 赋值给a int b = 20;  //定义变量 ...

  8. MapReduce任务学习系列

    首先放一张官方图片,大致了解下整个MapReduce的处理过程. 抛出如下疑问: 1.MapReduce的基本原理是什么?即利用什么机制来实现的任务拆分处理? 2.MapReduce任务执行过程是什么 ...

  9. 从零开始的Python学习Episode 20——面向对象(3)

    面向对象之封装 封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别:将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体. 隐藏 在python中用双下划线开 ...

  10. Workbook对象的方法总结(二)

    (1).Worksheet 对象有 row_dimensions 和 column_dimensions 属性,控制行高和列宽. 例如: >>> sheet.row_dimensio ...