当前位置: 首页 > news >正文

多个子流程_协程工作流程的实现

协程的实现之工作流程

问题:协程内部是如何工作呢?

先来看一下协程服务器案例的代码, 代码参考:https://github.com/wangbojing/NtyCo/blob/master/nty_server_test.c

分别讨论三个协程的比较晦涩的工作流程。第一个协程的创建;第二个IO异步操作;第三个协程子过程回调

3.1 创建协程

当我们需要异步调用的时候,我们会创建一个协程。比如accept返回一个新的sockfd,创建一个客户端处理的子过程。再比如需要监听多个端口的时候,创建一个server的子过程,这样多个端口同时工作的,是符合微服务的架构的。

创建协程的时候,进行了如何的工作?创建API如下:

int nty_coroutine_create(nty_coroutine **new_co, proc_coroutine func, void *arg)

参数1:nty_coroutine **new_co,需要传入空的协程的对象,这个对象是由内部创建的,并且在函数返回的时候,会返回一个内部创建的协程对象。

参数2:proc_coroutine func,协程的子过程。当协程被调度的时候,就会执行该函数。

参数3:void *arg,需要传入到新协程中的参数。

协程不存在亲属关系,都是一致的调度关系,接受调度器的调度。调用create API就会创建一个新协程,新协程就会加入到调度器的就绪队列中。

创建的协程具体步骤会在《协程的实现之原语操作》来描述。

3.2 实现IO异步操作

大部分的朋友会关心IO异步操作如何实现,在send与recv调用的时候,如何实现异步操作的。

先来看一下一段代码:

while (1) {
    int nready = epoll_wait(epfd, events, EVENT_SIZE, -1);

    for (i = 0;i < nready;i ++) {

        int sockfd = events[i].data.fd;
        if (sockfd == listenfd) {
            int connfd = accept(listenfd, xxx, xxxx);
            
            setnonblock(connfd);

            ev.events = EPOLLIN | EPOLLET;
            ev.data.fd = connfd;
            epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);

        } else {
            
            epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
            recv(sockfd, buffer, length, 0);

            //parser_proto(buffer, length);

            send(sockfd, buffer, length, 0);
            epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, NULL);
        }
    }
}

在进行IO操作(recv,send)之前,先执行了 epoll_ctl的del操作,将相应的sockfd从epfd中删除掉,在执行完IO操作(recv,send)再进行epoll_ctl的add的动作。这段代码看起来似乎好像没有什么作用。

如果是在多个上下文中,这样的做法就很有意义了。能够保证sockfd只在一个上下文中能够操作IO的。不会出现在多个上下文同时对一个IO进行操作的。协程的IO异步操作正式是采用此模式进行的。

把单一协程的工作与调度器的工作的划分清楚,先引入两个原语操作 resume,yield会在《协程的实现之原语操作》来讲解协程所有原语操作的实现,yield就是让出运行,resume就是恢复运行。调度器与协程的上下文切换如下图所示

15fe34fed94975835532c5cc4c2f7755.png

在协程的上下文IO异步操作(nty_recv,nty_send)函数,步骤如下:

1. 将sockfd 添加到epoll管理中。

2. 进行上下文环境切换,由协程上下文yield到调度器的上下文。

3. 调度器获取下一个协程上下文。Resume新的协程

IO异步操作的上下文切换的时序图如下:

9b2303ed92e48d96d1a84b896d880ac3.png

3.3 回调协程的子过程

在create协程后,何时回调子过程?何种方式回调子过程?

首先来回顾一下x86_64寄存器的相关知识。汇编与寄存器相关知识还会在《协程的实现之切换》继续深入探讨的。x86_64 的寄存器有16个64位寄存器,分别是:%rax, %rbx,

%rcx, %esi, %edi, %rbp, %rsp, %r8, %r9, %r10, %r11, %r12, %r13, %r14, %r15。

%rax 作为函数返回值使用的。

%rsp 栈指针寄存器,指向栈顶

%rdi, %rsi, %rdx, %rcx, %r8, %r9 用作函数参数,依次对应第1参数,第2参数。。。

%rbx, %rbp, %r12, %r13, %r14, %r15 用作数据存储,遵循调用者使用规则,换句话

说,就是随便用。调用子函数之前要备份它,以防它被修改

%r10, %r11 用作数据存储,就是使用前要先保存原值

以NtyCo的实现为例,来分析这个过程。CPU有一个非常重要的寄存器叫做EIP,用来存储CPU运行下一条指令的地址。我们可以把回调函数的地址存储到EIP中,将相应的参数存储到相应的参数寄存器中。实现子过程调用的逻辑代码如下:

void _exec(nty_coroutine *co) {
    co->func(co->arg); //子过程的回调函数
}
 
void nty_coroutine_init(nty_coroutine *co) {
 //ctx 就是协程的上下文
    co->ctx.edi = (void*)co; //设置参数
    co->ctx.eip = (void*)_exec; //设置回调函数入口
 //当实现上下文切换的时候,就会执行入口函数_exec , _exec 调用子过程func
}

相关文章:

  • Permission denied
  • wps交叉表_WPS文字小工具大用途—交叉引用的使用方法
  • 使用Trinity进行转录组组装
  • linux ssh连接交换机_【交换机】交换机如何配置ssh管理
  • python股票分析入门_学习用Python分析股票数据(入门)
  • Aspera 下载_SRA原始数据下载
  • keil5怎么配置程序风格_分享一个在Keil开发环境中配置代码格式化工具Astyle(美化代码风格)...
  • 01-rna-seq从头开始 卖萌哥
  • bc伐木机器人_BC教程之自动合成_我的世界BCmod教程 BCmod怎么玩__ 单机攻略_跑跑车单机游戏网...
  • FastQC或Trimmomatic去接头,低质量碱基
  • python中raise抛出异常_一文教你读懂Python中的异常信息
  • xargs 管道命令
  • android显示布局边界的边距_Android UI之布局
  • 变速恒频风电机组的优缺点_风电轴承的那些事
  • 开发工评价程师自我_房地产项目开发报建-简历范文,【工作经历+项目经验+自我评价】怎么写...
  • [PHP内核探索]PHP中的哈希表
  • 【翻译】Mashape是如何管理15000个API和微服务的(三)
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • Angular 响应式表单 基础例子
  • JAVA SE 6 GC调优笔记
  • jdbc就是这么简单
  • js如何打印object对象
  • react-core-image-upload 一款轻量级图片上传裁剪插件
  • vue-cli在webpack的配置文件探究
  • Webpack 4x 之路 ( 四 )
  • 高度不固定时垂直居中
  • 新书推荐|Windows黑客编程技术详解
  • 用quicker-worker.js轻松跑一个大数据遍历
  • 鱼骨图 - 如何绘制?
  • 正则与JS中的正则
  • 湖北分布式智能数据采集方法有哪些?
  • 我们雇佣了一只大猴子...
  • # Maven错误Error executing Maven
  • #、%和$符号在OGNL表达式中经常出现
  • #pragma once与条件编译
  • #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (zt)最盛行的警世狂言(爆笑)
  • (附源码)springboot 智能停车场系统 毕业设计065415
  • (附源码)ssm教材管理系统 毕业设计 011229
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (亲测有效)解决windows11无法使用1500000波特率的问题
  • (原創) 未来三学期想要修的课 (日記)
  • (转)平衡树
  • (轉貼)《OOD启思录》:61条面向对象设计的经验原则 (OO)
  • ./include/caffe/util/cudnn.hpp: In function ‘const char* cudnnGetErrorString(cudnnStatus_t)’: ./incl
  • .chm格式文件如何阅读
  • .NET Standard / dotnet-core / net472 —— .NET 究竟应该如何大小写?
  • .NET 常见的偏门问题
  • .NetCore 如何动态路由
  • .net用HTML开发怎么调试,如何使用ASP.NET MVC在调试中查看控制器生成的html?
  • .Net转前端开发-启航篇,如何定制博客园主题
  • @transactional 方法执行完再commit_当@Transactional遇到@CacheEvict,你的代码是不是有bug!...
  • [Android]使用Retrofit进行网络请求
  • [Angular 基础] - 数据绑定(databinding)