论基层门户网站的建设,七牛上传wordpress,网站名拍卖价格,前端小程序开发流程主要通过以下9个方面来了解协程的原理#xff1a;
目录
1、为什么使用协程
1.3、协程的适用场景
2、协程的原语操作
3、协程的切换
3.1、汇编实现 4.协程的运行流程
5.协程的结构体定义(我们其实可以参照线程或者进程的状态来设计)
5.1、多状态集合设计
6.协程的调度…主要通过以下9个方面来了解协程的原理
目录
1、为什么使用协程
1.3、协程的适用场景
2、协程的原语操作
3、协程的切换
3.1、汇编实现 4.协程的运行流程
5.协程的结构体定义(我们其实可以参照线程或者进程的状态来设计)
5.1、多状态集合设计
6.协程的调度策略
7.协程调度器如何定义
8、多核模式
9、性能测试 为什么要有协程协程解决了什么问题 关于协程我们经常看到这样的话同步的编程方式异步的性能。那么什么是同步什么是异步呢
同步与异步 同步和异步是形容两者之间的关系。两者在一个流程内就是同步两者不在一个流程内就是异步。
我们这里说的同步和异步是指io同步操作和io异步操作。
还有一个容易与io异步操作混淆的概念异步io就是指有io数据的时候直接callbackAIO, 比如boost的asio 主要差别是在IO事件是否就绪的两种处理方式的区别
方法一io 同步操作
发起请求等待响应接受响应
对 io 的操作和 epoll_wait 放在同一流程里需要等待 io 的响应。
优点sockfd 管理方便操作逻辑清晰缺点依赖 io 响应速度性能差
int handle(int sockfd) {recv(sockfd, rbuffer, length, 0);parser_proto(rbuffer, length);send(sockfd, sbuffer, length, 0);
}方法二io 异步操作
handle 函数内部将 sockfd 的操作push 到线程池中在 io 数据拷贝阶段可以做其他事。
int thread_cb(int sockfd) {// 此函数是在线程池创建的线程中运行与 handle 不在一个线程上下文中运行recv(sockfd, rbuffer, length, 0);parser_proto(rbuffer, length);send(sockfd, sbuffer, length, 0);
}
int handle(int sockfd) {//此函数在主线程 main_thread 中运行在此处之前确保线程池已经启动。push_thread(sockfd, thread_cb); //将 sockfd 放到其他线程中运行。
}1、为什么使用协程 从性能方面来看对于使用异步 io 的线程存在三个问题
系统线程占用大量的内存空间 线程切换占用大量的系统时间 为了线程安全线程间需要加锁保护资源降低执行的效率 从编程角度来看无论同步还是异步编程方式都是基于事件驱动的。事件驱动流程包括注册事件绑定回调触发回调提高了系统的并发。但是由于回调的多层嵌套使得编程复杂降低了代码的可维护性。
在资源有限的前提下高性能服务需要解决的问题有
减少线程的重复高频创建线程池 尽量避免线程的阻塞 Reactor 非阻塞回调解决问题的能力有限 响应式编程容易陷入回调地狱割裂业务逻辑 协程将同 io 转成异步 io 提升代码的可维护与可理解性减少回调函数减少回调链深度 而协程的出现可以很好地解决上述问题。
协程运行在线程之上。当一个协程调用阻塞 io主动让出 cpu ( yield 原语) 让另一个协程运行在当前线程之上 resume 原语。协程没有增加线程数量只是在线程的基础上通过分时复用的方式运行多个协程降低了系统内存。而且协程的切换在用户态完成减少了系统切换开销。
综上所述协程的优势体现在
消耗系统资源和切换代价更小 协程可以实现无锁编程 简化了异步编程可以达到以同步的编程方式实现异步的性能。
1.3、协程的适用场景
协程适用于 I/O 密集型业务线程切换频繁。其他情况性能不会有太大的提升。
2、协程的原语操作
yield 协程主动让出CPU给调度器。时机业务提交 - epoll_wait resume 调度器恢复协程的运行权。时机epoll_wait - 业务处理 resume 和 yield 是两个可逆的原子操作。
io 异步操作函数执行流程如下
将 sockfd 添加到 epoll 管理 由协程上下文 yield 到调度器的上下文 调度器获取下一个协程上下文resume 新的协程 1.commit完后switch -- epoll_wait 是 yield 让出(将当前协程从寄存器里让出)操作到wait下等待io就绪如果IO就绪那么使用resume操作在将当前协程让出后把新的协程交换到cpu上运行(寄存器上) 2.epoll_wait -- io处理流程 是 resume 恢复操作 yield 与 resume 是一个switch操作三种实现方式 1.longjump/setjump 2.ucontext 3.汇编实现
3、协程的切换 协程的上下文如何进行切换现有的 C 协程库均基于两种方案
汇编实现libcoBoost.context OS 提供的 API phxrpc基于 ucontext / Boost.context 的上下文切换 libmill基于 setjump/longjump 的协程切换 一般来说基于汇编的上下文切换要比采用系统调用的切换更加高效
3.1、汇编实现 x86-64有16个64位寄存器分别是%rax%rbx%rcx%rdx%rdi%rsi%rbp%rsp%r8%r9%r10%r11%r12%r13%r14%r15。
%rax: Return value, 作为函数返回值使用。 %rsp: Stack pointer栈指针栈顶指针指向栈顶 %rbp: Base pointer基址指针栈桢栈底指针 Frame pointer指向栈的底部 %rdi%rsi%rdx%rcx%r8%r9: 用作函数参数依次对应第1, …, 6参数 %rbx%r12%r13%r14%r15: Caller saved被调用者保护寄存器易失性寄存器程序调用过程中寄存器的值不需要保存。如果要保存则调用者负责压栈。 %r10%r11: Callee-owned调用者保护寄存器非易失性寄存器。程序调用过程中需要保存不能覆盖。被调用寄存器先保存值然后再调用调用结束后恢复调用前的值。 %rip: Instruction pointer, 相当于PC指针指向当前的指令地址指向下一条要执行的指令 协程上下文切换就是先将 cpu 寄存器的值暂时保存到 cur_ctx再将即将运行的协程的上下文 new_ctx 的值 mov 到相对应的 cpu 寄存器上完成切换。 切换函数 _switch 的定义
/*** brief switch实现协程切换保存cpu寄存器的值到cur_ctx加载new_ctx上下文到cpu寄存器* param new_ctx: 对应寄存器rdi即将运行协程的上下文加载它的上下文到cpu寄存器* param cur_ctx: 对应寄存器rsi正在运行协程的上下文保存cpu寄存器到它的上下文 * return int */
int _switch(nty_cpu_ctx *new_ctx, nty_cpu_ctx *cur_ctx);
//
__asm__ (.text \n.p2align 4,,15 \n
.globl _switch \n
.globl __switch \n
_switch: \n
__switch: \nmovq %rsp, 0(%rsi) # save stack_pointer \nmovq %rbp, 8(%rsi) # save frame_pointer \nmovq (%rsp), %rax # save insn_pointer \nmovq %rax, 16(%rsi) \nmovq %rbx, 24(%rsi) # save rbx,r12-r15 \nmovq %r12, 32(%rsi) \nmovq %r13, 40(%rsi) \nmovq %r14, 48(%rsi) \nmovq %r15, 56(%rsi) \nmovq 56(%rdi), %r15 \nmovq 48(%rdi), %r14 \nmovq 40(%rdi), %r13 # restore rbx,r12-r15 \nmovq 32(%rdi), %r12 \nmovq 24(%rdi), %rbx \nmovq 8(%rdi), %rbp # restore frame_pointer \nmovq 0(%rdi), %rsp # restore stack_pointer \nmovq 16(%rdi), %rax # restore insn_pointer \nmovq %rax, (%rsp) \nret \n
);nty_cpu_ctx 结构体存储寄存器的值
// x86 寄存器列表每个寄存器8字节
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;4.协程的运行流程 协程中遇到io操作就加入到epoll里面yield将CPU让出回到调度器调度器进行调度决定哪个协程运行。
一个fd对应一个协程的设计方法是不是最优的能不能设计成多分fd对应一个协程
对于网络框架一个fd对应一个协程是一个很好的方案
如果是对界面刷新或者磁盘文件操作就不是很合适。
比如A协程 recv如果该fd io已经准备就绪了这时候yield调度器会调度其他协程运行可能调度几百几千个其他协程最后再回到A协程进行recv它的实时性有没有意义
对于大量io所有io一起看的话单个io的实时性是没有意义的。 5.协程的结构体定义(我们其实可以参照线程或者进程的状态来设计)
//这里我们就采用比较简单的方式来实现
协程数据结构设计分为两部分
运行体 R包含运行状态就绪睡眠等待运行体回调函数回调参数栈指针栈大小当前运行体调度器 S包含执行集合就绪睡眠等待
5.1、多状态集合设计 新创建的协程创建完成后加入到就绪集合等待调度器的调度协程在运行完成后进行 IO 操作此时 IO 并未准备好进入等待状态集合IO 准备就绪协程开始运行后续进行 sleep 操作此时进入到睡眠状态集合。
那么运行体如何在多状态集合高效切换三种集合如何设置合理的数据结构
就绪 (ready) 集合不设置优先级所有协程优先级一致使用队列存储就绪的协程简称就绪队列 (ready_queue) 睡眠 (sleep) 集合需要对睡眠时长排序采用红黑树来存储简称睡眠树 (sleep_tree)。key 为睡眠时长value 为对应的协程结点。 等待 (wait) 集合需要对 IO 等待时间排序采用红黑树来存储简称等待树 (wait_tree)。 struct coroutine {nty_cpu_ctx ctx; //上下文环境保存CPU寄存器proc_coroutine func; // 子过程的回调函数void *arg; // 子过程回调函数的参数void *ret; // 子过程回调函数的返回值nty_coroutine_status status; // 运行状态ready, wait, sleepnty_schedule *sched; // 调度器uint64_t birth; // 创建时间uint64_t id; // 协程 idvoid *stack; // 栈空间size_t stack_size; // 栈空间大小RB_ENTRY(_nty_coroutine) sleep_node; // 睡眠 sleep 树RB_ENTRY(_nty_coroutine) wait_node; // 等待 wait 树TAILQ_ENTRY(_nty_coroutine) ready_next; // 就绪 ready 队列
};我们每个协程有自己独立的栈空间比较好共享栈的话在编程和处理方面比较麻烦
6.协程的调度策略
协程如何被调度有两种方案生产者消费者模式和多状态运行。 while (1) {//遍历睡眠集合将满足条件的加入到 readynty_coroutine *expired NULL;while ((expired sleep_tree_expired(sched)) ! NULL) {TAILQ_ADD(sched-ready, expired);}//遍历等待集合将满足添加的加入到 readynty_coroutine *wait NULL;int nready epoll_wait(sched-epfd, events, EVENT_MAX, 1);for (i 0;i nready;i ) {wait wait_tree_search(events[i].data.fd);TAILQ_ADD(sched-ready, wait);}// 使用 resume 恢复 ready 的协程运行权while (!TAILQ_EMPTY(sched-ready)) {nty_coroutine *ready TAILQ_POP(sched-ready);resume(ready);}
}多状态运行 while (1) {//遍历睡眠集合使用 resume 恢复 expired 的协程运行权nty_coroutine *expired NULL;while ((expired sleep_tree_expired(sched)) ! NULL) {resume(expired);}//遍历等待集合使用 resume 恢复 wait 的协程运行权nty_coroutine *wait NULL;int nready epoll_wait(sched-epfd, events, EVENT_MAX, 1);for (i 0;i nready;i ) {wait wait_tree_search(events[i].data.fd);resume(wait);}// 使用 resume 恢复 ready 的协程运行权while (!TAILQ_EMPTY(sched-ready)) {nty_coroutine *ready TAILQ_POP(sched-ready);resume(ready);}
}其实我认为是第一种比较好因为参照线程和进程的状态来实现会更加贴合操作系统运行 7.协程调度器如何定义
每一协程都需要使用的而且可能会不同属性的就是协程属性私有。每一协程都需要的而且数据一致的就是调度器的属性公共。调度器是管理所有协程运行的组件。
// 调度策略
struct scheduler_op {remove_wait();remove_sleep();
};// 调度器用来管理所有的协程
struct scheduler {int epfd; struct epoll_event events[]; struct coroutine *cur; // 当前运行的协程queue_tail(, struct coroutine) ready; // 指向就绪队列rbtree_root(, struct coroutine) wait; // 指向等待树rbtree_root(, struct coroutine) sleep; // 指向睡眠树struct scheduler_op *sch_op; //调度策略
};这么定义好了之后调度器就可以遍历各个状态的数据结构然后加一个定时器将要超时的
协程调度上来设置当前运行协程是因为调度器需要运行一个协程如果来新的协程再把老的协程执行让出操作新的协程resume操作交给调度器进行调度
调度器与协程每个协程对应一个客户端的运行关系 if (io 是否可写) {connect();
}
else {epoll_ctl(epfd, fd);yield();
}8、多核模式 一个线程一个调度器简单不需要加锁 一个进程一个调度器简单不需要加锁 多个线程共用一个调度器复杂入队需要加锁 9、性能测试 测试标准
并发量fd数量、协程的数量fd数量 协程数量
为什么这么说呢是因为我们当一个客户端连接的是否事件就绪如果是读事件中的监听套接字那么还需要创建一个新的协程将新的fd加入到epoll中所以每个客户端连接过来我们都需要创建一个协程来管理当前客户端的fd
所以说fd数量 协程数量每秒接入量fd - coroutine_create断开连接coroutine_destory 文章转载自: http://www.morning.yzxlkj.com.gov.cn.yzxlkj.com http://www.morning.zlxkp.cn.gov.cn.zlxkp.cn http://www.morning.fsrtm.cn.gov.cn.fsrtm.cn http://www.morning.rfdqr.cn.gov.cn.rfdqr.cn http://www.morning.fbrshjf.com.gov.cn.fbrshjf.com http://www.morning.dydqh.cn.gov.cn.dydqh.cn http://www.morning.wrwcf.cn.gov.cn.wrwcf.cn http://www.morning.rbjth.cn.gov.cn.rbjth.cn http://www.morning.xknsn.cn.gov.cn.xknsn.cn http://www.morning.mmclj.cn.gov.cn.mmclj.cn http://www.morning.dbnpz.cn.gov.cn.dbnpz.cn http://www.morning.krfpj.cn.gov.cn.krfpj.cn http://www.morning.bpmnz.cn.gov.cn.bpmnz.cn http://www.morning.rnrwq.cn.gov.cn.rnrwq.cn http://www.morning.mnwmj.cn.gov.cn.mnwmj.cn http://www.morning.rzmsl.cn.gov.cn.rzmsl.cn http://www.morning.bkslb.cn.gov.cn.bkslb.cn http://www.morning.xnrgb.cn.gov.cn.xnrgb.cn http://www.morning.qxnlc.cn.gov.cn.qxnlc.cn http://www.morning.htmhl.cn.gov.cn.htmhl.cn http://www.morning.qwbht.cn.gov.cn.qwbht.cn http://www.morning.yrnll.cn.gov.cn.yrnll.cn http://www.morning.bmbnc.cn.gov.cn.bmbnc.cn http://www.morning.hybmz.cn.gov.cn.hybmz.cn http://www.morning.djxnn.cn.gov.cn.djxnn.cn http://www.morning.ktrh.cn.gov.cn.ktrh.cn http://www.morning.lhxkl.cn.gov.cn.lhxkl.cn http://www.morning.trsfm.cn.gov.cn.trsfm.cn http://www.morning.wrtpk.cn.gov.cn.wrtpk.cn http://www.morning.wtcbl.cn.gov.cn.wtcbl.cn http://www.morning.kpbgvaf.cn.gov.cn.kpbgvaf.cn http://www.morning.nkcfh.cn.gov.cn.nkcfh.cn http://www.morning.wklrz.cn.gov.cn.wklrz.cn http://www.morning.rmqlf.cn.gov.cn.rmqlf.cn http://www.morning.jkftn.cn.gov.cn.jkftn.cn http://www.morning.rfhmb.cn.gov.cn.rfhmb.cn http://www.morning.yyngs.cn.gov.cn.yyngs.cn http://www.morning.clybn.cn.gov.cn.clybn.cn http://www.morning.rzsxb.cn.gov.cn.rzsxb.cn http://www.morning.jhwwr.cn.gov.cn.jhwwr.cn http://www.morning.wqcz.cn.gov.cn.wqcz.cn http://www.morning.qmnjn.cn.gov.cn.qmnjn.cn http://www.morning.geledi.com.gov.cn.geledi.com http://www.morning.tfsyk.cn.gov.cn.tfsyk.cn http://www.morning.wqfzx.cn.gov.cn.wqfzx.cn http://www.morning.rqbkc.cn.gov.cn.rqbkc.cn http://www.morning.txmlg.cn.gov.cn.txmlg.cn http://www.morning.yrhsg.cn.gov.cn.yrhsg.cn http://www.morning.xmhpq.cn.gov.cn.xmhpq.cn http://www.morning.sqmlw.cn.gov.cn.sqmlw.cn http://www.morning.llxyf.cn.gov.cn.llxyf.cn http://www.morning.lbfgq.cn.gov.cn.lbfgq.cn http://www.morning.wjtxt.cn.gov.cn.wjtxt.cn http://www.morning.rmdwp.cn.gov.cn.rmdwp.cn http://www.morning.jhwqp.cn.gov.cn.jhwqp.cn http://www.morning.qbjrl.cn.gov.cn.qbjrl.cn http://www.morning.nfccq.cn.gov.cn.nfccq.cn http://www.morning.nwfxp.cn.gov.cn.nwfxp.cn http://www.morning.bwdnx.cn.gov.cn.bwdnx.cn http://www.morning.fksyq.cn.gov.cn.fksyq.cn http://www.morning.mnsmb.cn.gov.cn.mnsmb.cn http://www.morning.yuminfo.com.gov.cn.yuminfo.com http://www.morning.jtcq.cn.gov.cn.jtcq.cn http://www.morning.tqlhn.cn.gov.cn.tqlhn.cn http://www.morning.mtbth.cn.gov.cn.mtbth.cn http://www.morning.brnwc.cn.gov.cn.brnwc.cn http://www.morning.lmjkn.cn.gov.cn.lmjkn.cn http://www.morning.qnzk.cn.gov.cn.qnzk.cn http://www.morning.vuref.cn.gov.cn.vuref.cn http://www.morning.xgzwj.cn.gov.cn.xgzwj.cn http://www.morning.trrpb.cn.gov.cn.trrpb.cn http://www.morning.bhwz.cn.gov.cn.bhwz.cn http://www.morning.cyfsl.cn.gov.cn.cyfsl.cn http://www.morning.hsxkq.cn.gov.cn.hsxkq.cn http://www.morning.hqwtm.cn.gov.cn.hqwtm.cn http://www.morning.tllhz.cn.gov.cn.tllhz.cn http://www.morning.jrkzk.cn.gov.cn.jrkzk.cn http://www.morning.zrqs.cn.gov.cn.zrqs.cn http://www.morning.ldzss.cn.gov.cn.ldzss.cn http://www.morning.clpfd.cn.gov.cn.clpfd.cn