zoukankan      html  css  js  c++  java
  • (转)通过汇编语言实现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

  • 相关阅读:
    hdu 4614 线段树 二分
    cf 1066d 思维 二分
    lca 最大生成树 逆向思维 2018 徐州赛区网络预赛j
    rmq学习
    hdu 5692 dfs序 线段树
    dfs序介绍
    poj 3321 dfs序 树状数组 前向星
    cf 1060d 思维贪心
    【PAT甲级】1126 Eulerian Path (25分)
    【PAT甲级】1125 Chain the Ropes (25分)
  • 原文地址:https://www.cnblogs.com/heluan/p/9899824.html
Copyright © 2011-2022 走看看