实现c协程

协程的概念就不介绍了,不清楚的同学可以自己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

 

posted @ 2012-06-19 11:43  sniperHW  阅读(11684)  评论(22编辑  收藏  举报