转自:http://www.cnblogs.com/sniperHW/archive/2012/06/19/2554574.html

协程的概念就不介绍了,不清楚的同学可以自己google,windows和unix like系统
本身就提供了协程的支持,windows下叫fiber,unix like系统下叫ucontext.

在这里重复制造轮子,一是为了更清楚了解协程的实现,二是为了在windows和
unix like系统下都提供一套统一的协程接口.

首先介绍下接口,很简单,只有几个函数:

#ifndef _UTHREAD_H
#define _UTHREAD_H typedef void (*start_fun)(void *);
typedef struct uthread* uthread_t; uthread_t uthread_create(void *stack,uint32_t stack_size); void uthread_destroy(uthread_t*); void uthread_run(uthread_t p,uthread_t u,start_fun st_fun,void *arg); void uthread_switch(uthread_t from,uthread_t to); #endif

下面主要介绍uthread_run:
uthread_run启动一个协程的运行,协程运行后会马上执行st_fun函数.
这里需要注意的地方是uthread_run的前两个参数,p和u,u是将被启动
的协程,而p是u的父协程.

如果p为空,则u执行完后将从uthread_run中返回,否则u执行完将调用
uthread_switch(u,p),将执行权交回给p.

uthead.c

#include <stdint.h>
#include "uthread.h"
#include <stdlib.h>
#include <stdio.h> struct uthread
{
//0:ebp,1:esp,2:ebx,3:edi,4:esi
uint32_t reg[5];
uint32_t parent_reg[5];
struct uthread *parent;//如果_parent非空,则_start_fun结束后返回到_parent中
uint32_t __exit;//主函数调用完成设置
void *stack;
start_fun _start_fun;
void *start_arg;
uint32_t stack_size;
}; uthread_t uthread_create(void *stack,uint32_t stack_size)
{
uthread_t u = calloc(1,sizeof(*u));
u->stack = stack;
u->stack_size = stack_size;
u->reg[0] = (uint32_t)stack+stack_size;
u->reg[4] = (uint32_t)stack+stack_size;
u->__exit = 0;
return u;
} extern void uthread_run1(uthread_t u,start_fun st_fun,void *arg);
extern void uthread_run2(uthread_t p,uthread_t u,start_fun st_fun,void *arg); void uthread_run(uthread_t p,uthread_t u,start_fun st_fun,void *arg)
{
u->parent = p;
if(u->parent)
{
uthread_run2(p,u,st_fun,arg);
if(u->__exit)
uthread_switch(u,p);
}
else
{
uthread_run1(u,st_fun,arg);
}
} void uthread_destroy(uthread_t *u)
{
free(*u);
*u = 0;
}

协程的启动和执行权切换无法用c语言完成,下面是用汇编代码实现的切换和启动函数:

switch.s

.align    4
.globl uthread_switch
.globl _uthread_switch
uthread_switch:
_uthread_switch: ##arg from to
movl 4(%esp), %eax
movl %ebp, 0(%eax)
movl %esp, 4(%eax)
movl %ebx, 8(%eax)
movl %edi, 12(%eax)
movl %esi, 16(%eax)
movl 8(%esp), %eax
movl 0(%eax), %ebp
movl 4(%eax), %esp
movl 8(%eax), %ebx
movl 12(%eax),%edi
movl 16(%eax),%esi
ret
.align 4
.globl uthread_run1
.globl _uthread_run1
uthread_run1:
_uthread_run1: ##arg u_context
movl 4(%esp),%eax
movl %ebp,20(%eax) #save register
movl %esp,24(%eax)
movl %ebx,28(%eax)
movl %edi,32(%eax)
movl %esi,36(%eax)
movl 12(%esp),%ecx #get arg
movl 8(%esp),%edx #get st_fun
movl 0(%eax),%esp #change stack
movl %esp,%ebp
pushl %eax
pushl %ecx #push arg
call *%edx #call st_fun
popl %eax
popl %eax
movl 20(%eax),%ebp #restore register
movl 24(%eax),%esp
movl 28(%eax),%ebx
movl 32(%eax),%edi
movl 36(%eax),%esi
movl $1,44(%eax)
ret
.align 4
.globl uthread_run2
.globl _uthread_run2
uthread_run2:#param p,u,start_fun,arg
_uthread_run2:
movl 4(%esp),%eax #get p
movl %ebp,0(%eax) #save register of p
movl %esp,4(%eax)
movl %ebx,8(%eax)
movl %edi,12(%eax)
movl %esi,16(%eax)
movl 8(%esp),%eax #get u
movl 16(%esp),%ecx #get arg
movl 12(%esp),%edx #get st_fun
movl 0(%eax),%esp #change stack
movl %esp,%ebp
pushl %eax
pushl %ecx #push arg
call *%edx #call st_fun
popl %eax
popl %eax
movl $1,44(%eax)
movl 40(%eax),%eax #get parent
movl 0(%eax),%ebp #restore register
movl 4(%eax),%esp
movl 8(%eax),%ebx
movl 12(%eax),%edi
movl 16(%eax),%esi
ret

test.c

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "uthread.h"
struct pair
{
uthread_t self;
uthread_t other;
}; void fun2(void *arg)
{
int i = 0;
struct pair *p = (struct pair*)arg;
printf("fun2\n");
uthread_switch(p->self,p->other);
printf("fun2 end\n");
} void fun1(void *arg)
{
int i = 0;
struct pair *p = (struct pair*)arg;
char *s = malloc(4096);
uthread_t u2 = uthread_create(s,4096);
struct pair _p = {u2,p->self};
uthread_run(p->self,u2,fun2,&_p);
printf("here\n");
uthread_switch(p->self,u2);
printf("fun1 end\n");
} int main()
{
char *s = malloc(4096);
uthread_t u1 = uthread_create(s,4096);
struct pair p = {u1,0};
uthread_run(0,u1,fun1,&p);
printf("return here\n");
return 0;
}

更新:

调整了协程接口,支持在uthread_swtch间传递和返回数据,使用方式更接近lua coroutine

接口如下:

#ifndef _UTHREAD_H
#define _UTHREAD_H typedef void* (*start_fun)(void *);
typedef struct uthread* uthread_t; uthread_t uthread_create(void *stack,uint32_t stack_size); void uthread_destroy(uthread_t*); void uthread_make(uthread_t u,uthread_t p,start_fun st_fun); void* uthread_swtch(uthread_t from,uthread_t to,void *arg); #endif

新代码地址:
https://github.com/sniperHW/kendylib

(转)通过汇编语言实现C协程的更多相关文章

  1. python之协程与IO操作

    协程 协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B ...

  2. Unity 协程与线程

    协程是不同步的 协程 不是 线程,协同程序是 不同步 的 一个线程在程序中和其他线程是异步运行的,在多处理器机器中一个线程可以同时与所有其他线程的实时运行其代码,这使得线程编程能够解决很复杂的事情,因 ...

  3. 协程coroutine

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

  4. 基于汇编的 C/C++ 协程 - 背景知识

    近几年来,协程在 C/C++ 服务器中的解决方案开始涌现.本文主要阐述以汇编实现上下文切换的协程方案,并且说明其在异步开发模式中的应用. 本文地址:https://segmentfault.com/a ...

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

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

  6. C/C++协程的实现方式总结

    1.利用 C 语言的 setjmp 和 longjmp,函数中使用 static local 的变量来保存协程内部的数据. 函数原型:int setjmp(jmp_buf envbuf); void  ...

  7. 聊一聊Unity协程背后的实现原理

    Unity开发不可避免的要用到协程(Coroutine),协程同步代码做异步任务的特性使程序员摆脱了曾经异步操作加回调的编码方式,使代码逻辑更加连贯易读.然而在惊讶于协程的好用与神奇的同时,因为不清楚 ...

  8. GO GMP协程调度实现原理 5w字长文史上最全

    1 Runtime简介 Go语言是互联网时代的C,因为其语法简洁易学,对高并发拥有语言级别的亲和性.而且不同于虚拟机的方案.Go通过在编译时嵌入平台相关的系统指令可直接编译为对应平台的机器码,同时嵌入 ...

  9. Python(八)进程、线程、协程篇

    本章内容: 线程(线程锁.threading.Event.queue 队列.生产者消费者模型.自定义线程池) 进程(数据共享.进程池) 协程 线程 Threading用于提供线程相关的操作.线程是应用 ...

随机推荐

  1. 安装PyTorch 0.4.0

    https://blog.csdn.net/sunqiande88/article/details/80085569 https://blog.csdn.net/xiangxianghehe/arti ...

  2. python 深复制与浅复制------copy模块

    模块解读: 浅复制: x = copy.copy(y)深复制: x = copy.deepcopy(y)(注:模块特有的异常,copy.Error) 深copy与浅copy的差别主要体现在当有混合对象 ...

  3. D-hdu 1465 不容易系列之一(递推)

    hdu 1465 不容易系列之一(错排) 不容易系列之一 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K ( ...

  4. Brain Network (easy)(并查集水题)

    G - Brain Network (easy) Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & ...

  5. EasyNVR流媒体直播之:零基础实现摄像头的全平台直播 (二)公网直播的实现

    接上回(https://blog.csdn.net/xiejiashu/article/details/81276870),我们实现内网直播,可以实现直播的web观看,该篇博文我们将实现公网的直播. ...

  6. vue实现简易留言板

    首先引入vue.js <script src="vue.js"></script> 布局 <div id="div"> &l ...

  7. This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its 错误解决办法

    This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary log ...

  8. 页游手游服务器(一)c实现拓展lua网络

    把工作几年服务器相关的部分内容,通过服务器解决方案,做一次总结.整个实现的主体是lua脚本,lua实现主要缺少的两大块:1网络部分2数据库部分这两部分必须通过c/c++做扩展先来做net,主要是服务器 ...

  9. 初步jmeter安装与使用

    前言,最近公司做了面向全国用户的教育平台,由于测试人员以功能测试为主,于是接口代码压测就被开发揽了,这就开始倒腾jmeter了,其实我想对于java,我更愿意用Python的工具,毕竟我爬虫时用的Py ...

  10. nc传文件

    nc传文件 先启动接收方 nc -l -p 9999 > index.lua 后启动发送方 nc 192.168.1.1 9999 < index.lua