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 "); uthread_switch(p->self,p->other); printf("fun2 end "); } 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 "); uthread_switch(p->self,u2); printf("fun1 end "); } 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 "); 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