c協程の実現
17287 ワード
协程の概念は绍介しないで、はっきりしない学友は自分でgoogleすることができて、windowsとunix likeシステム自体は协程の支持を提供して、windowsの下でfiberを叫んで、unix likeシステムの下でucontextと言います.
ここで車輪の製造を繰り返すのは、1つは協程の実現をより明確に理解するためであり、2つはwindowsとunix likeシステムの下で統一的な協程インタフェースを提供するためである.
まずインタフェースを紹介します.簡単です.いくつかの関数しかありません.
主にuthread_について説明しますrun:uthread_runはコヒーレントな運転を開始し、コヒーレントな運転後すぐにst_を実行します.fun関数ここで注意すべき点はuthread_runの最初の2つのパラメータ、pとu、uは起動される協程であり、pはuの親協程である.
pが空の場合、uは実行後uthread_からrunで返されます.そうしないと、uが実行されたらuthread_が呼び出されます.switch(u,p)は、実行権をpに渡す.
uthead.c
コパスの起動と実行権の切り替えはc言語ではできません.以下はアセンブリコードで実現される切り替えと起動関数です.
switch.s
test.c
更新:
コラボレーションインタフェースを調整し、uthread_をサポートswtch間でデータを転送して返し、lua coroutineに近い方法で使用します.
インタフェースは次のとおりです.
新しいコードアドレス:https://github.com/sniperHW/kendylib
ここで車輪の製造を繰り返すのは、1つは協程の実現をより明確に理解するためであり、2つは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の最初の2つのパラメータ、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