(转)通过汇编语言实现C协程
转自: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协程的更多相关文章
- python之协程与IO操作
协程 协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B ...
- Unity 协程与线程
协程是不同步的 协程 不是 线程,协同程序是 不同步 的 一个线程在程序中和其他线程是异步运行的,在多处理器机器中一个线程可以同时与所有其他线程的实时运行其代码,这使得线程编程能够解决很复杂的事情,因 ...
- 协程coroutine
协程(coroutine)顾名思义就是“协作的例程”(co-operative routines).跟具有操作系统概念的线程不一样,协程是在用户空间利用程序语言的语法语义就能实现逻辑上类似多任务的编程 ...
- 基于汇编的 C/C++ 协程 - 背景知识
近几年来,协程在 C/C++ 服务器中的解决方案开始涌现.本文主要阐述以汇编实现上下文切换的协程方案,并且说明其在异步开发模式中的应用. 本文地址:https://segmentfault.com/a ...
- 一个“蝇量级” C 语言协程库
协程(coroutine)顾名思义就是“协作的例程”(co-operative routines).跟具有操作系统概念的线程不一样,协程是在用户空间利用程序语言的语法语义就能实现逻辑上类似多任务的编程 ...
- C/C++协程的实现方式总结
1.利用 C 语言的 setjmp 和 longjmp,函数中使用 static local 的变量来保存协程内部的数据. 函数原型:int setjmp(jmp_buf envbuf); void ...
- 聊一聊Unity协程背后的实现原理
Unity开发不可避免的要用到协程(Coroutine),协程同步代码做异步任务的特性使程序员摆脱了曾经异步操作加回调的编码方式,使代码逻辑更加连贯易读.然而在惊讶于协程的好用与神奇的同时,因为不清楚 ...
- GO GMP协程调度实现原理 5w字长文史上最全
1 Runtime简介 Go语言是互联网时代的C,因为其语法简洁易学,对高并发拥有语言级别的亲和性.而且不同于虚拟机的方案.Go通过在编译时嵌入平台相关的系统指令可直接编译为对应平台的机器码,同时嵌入 ...
- Python(八)进程、线程、协程篇
本章内容: 线程(线程锁.threading.Event.queue 队列.生产者消费者模型.自定义线程池) 进程(数据共享.进程池) 协程 线程 Threading用于提供线程相关的操作.线程是应用 ...
随机推荐
- Disruptor 创建过程
1 Disruptor disruptor = new Disruptor<ValueEvent>(ValueEvent.EVENT_FACTORY, ringBufferSize, ex ...
- plsql 详细安装及汉化步骤
方法/步骤 双击运行plsqldev715 安装完成后我们装中文补丁: 双击运行‘Chinese’应用程序 找到PLSQL的安装目录添加进来 中文补丁安装完成后我们需要进行orcl的配置,配置好才 ...
- nginx学习之静态内容篇(五)
1.根目录和索引文件 server { root /www/data; location / { } location /images/ { } location ~ \.(mp3|mp4) { ro ...
- 启动/关闭Spring boot服务脚本
启动Spring boot服务脚本 #!/bin/bash cd /test java -jar test.jar &> ./test.log & echo "成功&q ...
- Python菜鸟之路:Python基础-操作缓存memcache、redis
一.搭建memcached和redis 略,自己去百度吧 二.操作Mmecached 1. 安装API python -m pip install python-memcached 2. 启动memc ...
- JVM垃圾回收时的可触及性
可触及的 1.从根节点可以触及到这个对象可复活的 1.一旦所有引用被释放,就是可复活状态 2.因为在finalize()中可能复活该对象不可触及的 1.在finalize()后,可能会进入不可触及状态 ...
- 聊聊数据库~5.SQL运维上篇
1.6.SQL运维篇 运维这块逆天只能说够用,并不能说擅长,所以这篇就当抛砖之用,欢迎补充和纠错 PS:再说明下CentOS优化策略这部分的内容来源:首先这块逆天不是很擅长,所以主要是参考网上的DBA ...
- Codeforces Round #254 (Div. 2)B. DZY Loves Chemistry
B. DZY Loves Chemistry time limit per test 1 second memory limit per test 256 megabytes input standa ...
- SAP basis 二
使用事务 SMW0 可以在数据库中创建自己的图像.选择选项"二进制数据". 可以按.GIF 格式保存图像. 使用表 SSM_CUST 中的关键字 "START_IMAGE ...
- python利用wxpy监控微信公众号
此次利用wxpy可以进行微信公众号的消息推送监测(代码超级简单),这样能进行实时获取链接.但是不光会抓到公众号的消息,好友的消息也会抓到(以后会完善的,毕竟现在能用了,而且做项目的微信号肯定是没有好友 ...