zoukankan      html  css  js  c++  java
  • [ZZ]实现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
    ");
        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
    复制代码

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

  • 相关阅读:
    day01-java开发前奏
    ASP.NET MVC RDLC-导出
    SAS学习目标层次
    Chapter003[SAS DATA步之全解密-02]
    Chapter002[SAS DATA步之全解密-01]
    Chapter001[SAS LICENCE 获取方法]
    VB.NET中如何在字符串中使用双引号
    ASP.NET数据处理进度条
    GridView内容详解(转载)
    js正则表达式限制文本框只能输入数字,小数点,英文字母,汉字等各类代码
  • 原文地址:https://www.cnblogs.com/ym65536/p/5308684.html
Copyright © 2011-2022 走看看