协程的概念就不介绍了,不清楚的同学可以自己google,windows和unix like系统
本身就提供了协程的支持,windows下叫fiber,unix like系统下叫ucontext.在这里重复制造轮子,一是为了更清楚了解协程的实现,二是为了在windows和
unix like系统下都提供一套统一的协程接口.首先介绍下接口,很简单,只有几个函数:
#ifndef _UTHREAD_H#define _UTHREAD_Htypedef 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#include "uthread.h"#include #include 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_switchuthread_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_run1uthread_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_run2uthread_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#include #include #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_Htypedef 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
新代码地址: