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