php setjmp longjmp,setjmp与longjmp实现协程切换....

最近关注协程切换!所以特别到libdill"借"了点代码,然后自己实现了一个简单的。

由于简单实现,基本使用的都是宏。能不依赖尽量不依赖!

编译命令:cc -o main main.c -Wall -O3

运行命令: ./main

Main.h

/* 64位编译器的栈保存操作*/

#if defined(__x86_64__) && (defined(__GNUC__) || defined(__clang__))

#include

#define setjmp(ctx) ({\

int ret;\

asm("lea LJMPRET%=(%%rip), %%rcx\n\t"\

"xor %%rax, %%rax\n\t"\

"mov %%rbx, (%%rdx)\n\t"\

"mov %%rbp, 8(%%rdx)\n\t"\

"mov %%r12, 16(%%rdx)\n\t"\

"mov %%r13, 24(%%rdx)\n\t"\

"mov %%r14, 32(%%rdx)\n\t"\

"mov %%r15, 40(%%rdx)\n\t"\

"mov %%rsp, 48(%%rdx)\n\t"\

"mov %%rcx, 56(%%rdx)\n\t"\

"LJMPRET%=:\n\t"\

: "=a" (ret)\

: "d" (ctx)\

: "memory", "rcx", "rsi", "rdi", "r8", "r9", "r10", "r11", "cc");\

ret;\

})

#define longjmp(ctx) \

asm("movq 56(%%rdx), %%rcx\n\t"\

"movq 48(%%rdx), %%rsp\n\t"\

"movq 40(%%rdx), %%r15\n\t"\

"movq 32(%%rdx), %%r14\n\t"\

"movq 24(%%rdx), %%r13\n\t"\

"movq 16(%%rdx), %%r12\n\t"\

"movq 8(%%rdx), %%rbp\n\t"\

"movq (%%rdx), %%rbx\n\t"\

".cfi_def_cfa %%rdx, 0 \n\t"\

".cfi_offset %%rbx, 0 \n\t"\

".cfi_offset %%rbp, 8 \n\t"\

".cfi_offset %%r12, 16 \n\t"\

".cfi_offset %%r13, 24 \n\t"\

".cfi_offset %%r14, 32 \n\t"\

".cfi_offset %%r15, 40 \n\t"\

".cfi_offset %%rsp, 48 \n\t"\

".cfi_offset %%rip, 56 \n\t"\

"jmp *%%rcx\n\t"\

: : "d" (ctx), "a" (1))

/* 32位编译器的栈保存操作*/

#elif defined(__i386__) && (defined(__GNUC__) || defined(__clang__))

#include

#define setjmp(ctx) ({\

int ret;\

asm("movl $LJMPRET%=, %%ecx\n\t"\

"movl %%ebx, (%%edx)\n\t"\

"movl %%esi, 4(%%edx)\n\t"\

"movl %%edi, 8(%%edx)\n\t"\

"movl %%ebp, 12(%%edx)\n\t"\

"movl %%esp, 16(%%edx)\n\t"\

"movl %%ecx, 20(%%edx)\n\t"\

"xorl %%eax, %%eax\n\t"\

"LJMPRET%=:\n\t"\

: "=a" (ret) : "d" (ctx) : "memory");\

ret;\

})

#define longjmp(ctx) \

asm("movl (%%edx), %%ebx\n\t"\

"movl 4(%%edx), %%esi\n\t"\

"movl 8(%%edx), %%edi\n\t"\

"movl 12(%%edx), %%ebp\n\t"\

"movl 16(%%edx), %%esp\n\t"\

"movl 20(%%edx), %%ecx\n\t"\

".cfi_def_cfa %%edx, 0 \n\t"\

".cfi_offset %%ebx, 0 \n\t"\

".cfi_offset %%esi, 4 \n\t"\

".cfi_offset %%edi, 8 \n\t"\

".cfi_offset %%ebp, 12 \n\t"\

".cfi_offset %%esp, 16 \n\t"\

".cfi_offset %%eip, 20 \n\t"\

"jmp *%%ecx\n\t"\

: : "d" (ctx), "a" (1))

#else

//由于暂时无法优化与保障系统setjmp的稳定,所以暂时不对其进行支持;

#error "此平台或编译器不被支持......."

#endif

/*

//是否使用jemalloc

#ifdef __JEMALLOC__

#include

#include

#else

#include

#endif

*/

// 这个是主协程的宏;

typedef sigjmp_buf Hub;

// 这个子协程专用宏;

typedef sigjmp_buf context;

static Hub Loop,init;

int main_status = 0;

//每次都需要检查这玩意是否为空、是否可用

context next;

//每个协程函数需要前置指定此标志

#define coroutine __attribute__((noinline)) void

int empty = 1;

#define __main__ \

main_status = 1;\

switch(setjmp(Loop)){\

case 0:\

longjmp(init);\

case 1:\

/*这里需要判断是否有需要运行的协程..*/\

if(empty)\

break;\

longjmp(next);\

}

//yield API依赖于begin....end之间。

#define yield() do{\

/*这里要判断是否为初次yield,否则后续会yield会出现死循环.*/\

if(!yielded){\

yielded = 1;\

/*这里将当前上下文保存在main函数要轮询的next指针中,开关为empty。*/\

if(!setjmp(next)){\

empty=0;\

longjmp(Loop);}\

/*什么时候会到这来?第一次yield设置上下文并且打开empty开关后则会到这来*/\

break;\

}\

/*发现不是初次yield,重新设置当前上线文!*/\

if(!setjmp(next)){\

longjmp(Loop);}\

/*每次回来后将开关关上,防止有一次跑到死循环上*/\

else\

{empty=1;break;}\

}while(0)

#define go(fn) {\

/*通过提供的go api来初始化主协程,顺便检查是否已经主协程是否已经初始化*/\

if(!main_status){\

/*这个点是初始化主协程和初始化完毕后跳出来执行任务..*/\

if(!setjmp(init))\

{__main__;}\

else\

{fn;}\

}else{\

/*如果初始化了主协程,则直接运行任务函数!通常会在此调用提供的"阻塞"API*/\

fn;\

/*这个是函数运行完毕后就会在main函数运行下一条语句*/\

}\

}

#define begin {\

/*这里检测是否初次yield*/\

int yielded = 0;\

#define end \

}

Main.c

#include

#include "main.h"

coroutine work()

begin

printf("Hello work!\n");

yield();

printf("bye work!\n");

end

coroutine gwork()

begin

printf("Hello gwork!\n");

yield();

printf("bye gwork!\n");

end

int main(int argc, char const *argv[])

{

go(work());

go(gwork());

printf("End.......\n");

return 0;

}

输出结果:

[root@localhost ~]# ./main

Hello work!

bye work!

Hello gwork!

bye gwork!

End.......

[root@localhost ~]#

说实话,这个实现是个半成品!

毫无疑问,系统提供的setjmp根本就不能拿来用(实现协程)...

至于为什么不继续研究下去?原因真的是精力有限,且代码量也比较大!(可以参考libdill代码量)

优化的地方就别说了,加紧加急赶出来的。(午休什么的都一直在思考代码.......)

但是能在几天内实现,说实话都是耗费了很大精力来弄......(语法限制也是很大一部分)

我的git上也有剥离下来的libdill代码,打包成静态文件后尽有60K大小!

有兴趣的同学可以下载下来,按照指示编译。(libdill仅支持Linux、Unix、BSD等OS)。

上述代码尽量打上了足够多的注释,方便大家参考、学习、指点。

如果有任何疑问、建议,也可以留言分享、探讨。^_^

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值