协程的实现思路与原理

协程的实现思路与原理

为什么要有协程

  • 以同步的方式实现异步的效率,同步的效率低
  • 综合同步和异步的优势

异步方式

发数据
  • socket
  • connect 服务器
  • 设置协议
  • send
  • fd ADD epoll
接数据
  • epoll_wait()
  • read fd
  • 解析对应数据

上面两个流程是异步的。

那么如何使用同步的方式实现异步的效率? 我们引入yield 和 resume原语。yield 为让出,resume为恢复

那么流程就变成如下:

发数据
  • socket
  • connect 服务器
  • 设置协议
  • send
  • fd ADD epoll
  • yield
接数据
  • epoll_wait()
  • read fd
  • 解析对应数据
  • resume

在一个线程里做。

yield 和 resume实现(协程的切换)

在这里插入图片描述

实现方式
  • longjmp/setjmp

  • ucontext

  • 用汇编实现

很多开源代码都用的汇编实现,我们着重看看汇编实现。可读性看似是汇编,实际上可读性比longjmp/setjmp好多了。

汇编实现
yield = switch(a, b);
resume = switch(b, a);

CPU上下文

typedef struct _nty_cpu_ctx {
	void *esp; //
	void *ebp;
	void *eip;
	void *edi;
	void *esi;
	void *ebx;
	void *r1;
	void *r2;
	void *r3;
	void *r4;
	void *r5;
} nty_cpu_ctx;

切换就是指先保存当前协程的寄存器值然后加载另一个协程的寄存器值

#ifdef __i386__
__asm__ (
"    .text                                  \n"
"    .p2align 2,,3                          \n"
".globl _switch                             \n"
"_switch:                                   \n"
"__switch:                                  \n"
"movl 8(%esp), %edx      # fs->%edx         \n"
"movl %esp, 0(%edx)      # save esp         \n"
"movl %ebp, 4(%edx)      # save ebp         \n"
"movl (%esp), %eax       # save eip         \n"
"movl %eax, 8(%edx)                         \n"
"movl %ebx, 12(%edx)     # save ebx,esi,edi \n"
"movl %esi, 16(%edx)                        \n"
"movl %edi, 20(%edx)                        \n"
"movl 4(%esp), %edx      # ts->%edx         \n"
"movl 20(%edx), %edi     # restore ebx,esi,edi      \n"
"movl 16(%edx), %esi                                \n"
"movl 12(%edx), %ebx                                \n"
"movl 0(%edx), %esp      # restore esp              \n"
"movl 4(%edx), %ebp      # restore ebp              \n"
"movl 8(%edx), %eax      # restore eip              \n"
"movl %eax, (%esp)                                  \n"
"ret                                                \n"
);
#elif defined(__x86_64__)

__asm__ (
"    .text                                  \n"
"       .p2align 4,,15                                   \n"
".globl _switch                                          \n"
".globl __switch                                         \n"
"_switch:                                                \n"
"__switch:                                               \n"
"       movq %rsp, 0(%rsi)      # save stack_pointer     \n"
"       movq %rbp, 8(%rsi)      # save frame_pointer     \n"
"       movq (%rsp), %rax       # save insn_pointer      \n"
"       movq %rax, 16(%rsi)                              \n"
"       movq %rbx, 24(%rsi)     # save rbx,r12-r15       \n"
"       movq %r12, 32(%rsi)                              \n"
"       movq %r13, 40(%rsi)                              \n"
"       movq %r14, 48(%rsi)                              \n"
"       movq %r15, 56(%rsi)                              \n"
"       movq 56(%rdi), %r15                              \n"
"       movq 48(%rdi), %r14                              \n"
"       movq 40(%rdi), %r13     # restore rbx,r12-r15    \n"
"       movq 32(%rdi), %r12                              \n"
"       movq 24(%rdi), %rbx                              \n"
"       movq 8(%rdi), %rbp      # restore frame_pointer  \n"
"       movq 0(%rdi), %rsp      # restore stack_pointer  \n"
"       movq 16(%rdi), %rax     # restore insn_pointer   \n"
"       movq %rax, (%rsp)                                \n"
"       ret                                              \n"
);
#endif
协程启动
  • 协程创建
  • 协程加入就绪队列
  • 入口函数
    • eip–>func
    • esp–>void *stack;
协程定义
  • context 上下文

  • stack 栈

  • size 栈大小

  • func 入口函数

  • arg 参数

  • wait (集合元素)

  • sleep (集合元素)

  • ready (集合元素)

  • exit 退出

  • status 协程状态

调度器

过程:

在这里插入图片描述

  • sockfd添加到epoll管理
  • 进行上下文切换,由协程上下文切换到调度器上下文
  • 调度器获取下一个协程的上下文。resume新的协程
接口定义
  • coroutine_create 协程创建
  • scheduler_loop 调度器运行
  • IO操作都需要封装

以后有机会会自己实现一个自己的协程框架

参考:libco libgo

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值