itunes store服务中断_从服务器开发底层聊一聊协程的实现原理

一、先介绍一组概念

进程

  • 进程是系统进行资源分配和调度的基本单位
  • 进程是一个实体,每一个进程都有自己地址空间

线程

  • 线程是程序执行流的最小单元
  • 一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成
  • 线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源

子例程

  • 子例程是某个主程序的一部分代码
  • 子例程又被称为子程序、过程、方法、函数等。在主程序中可以调用子例程来执行

协程

  • 协程与子例程一样,协程(coroutine)也是一种程序组件
  • 协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行
  • 注意,在一个子程序中中断,去执行其他子程序,不是函数调用,有点类似CPU的中断

二、同步与异步

  • 在介绍协程之前,必须先要详细的介绍一下同步和异步,因为这是引出协程的重要原因

同步

  • 以客户端为例,客户端向服务端发出请求之后,必须阻塞等待服务端给自己返回数据,属于一问一答的模式
3402edfbe1ac3c421842ea05099388ab.png
  • 下面是服务端同步的伪代码,核心是handle()函数:
  • 服务端在接收到消息之后,必须阻塞等待处理完消息再去发送数据,这个过程必须是一次性完成的,并且是在一个线程中完成的
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 {            handle(sockfd);        }    }}  // 可以看到是数据的收发在一个过程中int handle(int sockfd) { // 先接收    recv(sockfd, rbuffer, length, 0);  // 消息处理    parser_proto(rbuffer, length);  // 再发送    send(sockfd, sbuffer, length, 0);}

异步

  • 异步与同步不同,在异步的情况下,客户端向服务端发出请求之后,客户端可以不等待服务端给自己返回数据,接着继续执行。当然接收回复这一过程在其他线程中执行,当回复达到之后再通知客户端去接收(一般由回调函数实现)
ec6a31cef6b3ea4093dddc98c7e6809b.png
  • 下面是服务端同步的伪代码,核心是handle()函数:
  • 可以看到服务端在处理数据时,把任务交给了其他的线程去执行,而不必阻塞等待
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 {            handle(sockfd);        }    }} // 此函数是在线程池创建的线程中运行int thread_cb(int sockfd) {    recv(sockfd, rbuffer, length, 0);    parser_proto(rbuffer, length);    send(sockfd, sbuffer, length, 0);} // 此函数是在主线程中运行的int handle(int sockfd) { // 把读写任务放到另外的线程中去执行    push_thread(sockfd, thread_cb); //将 sockfd 放到其他线程中运行。}

Linuxc/c++后台服务器开发高阶视频学习资料后台私信【架构】获取

092034e609002a6d92e48e46c719ac22.png

备注

  • "异步IO"和"IO异步"的区别:
  • 异步IO:核心点是"IO",例如POSXI提供的AIO接口
  • IO异步:核心点是"操作",例如上面服务端代码的异步处理,就是IO异步操作。我们平常所说的"多线程异步"指的就是这个
  • "同步IO"和"IO同步"与上面也是相同的道理

同步与异步的区别

0e15c1ff7e8612baadc50afc535f9c17.png
  • 总结起来就是:
  • 同步:编程简单,性能差
  • 异步:编程复杂,性能高
  • 同步编程简单是因为其数据的处理是在同一个过程中进行处理的。但是异步的处理可能在不同的线程中进行处理,例如在异步的情况下客户端发送2次请求,这2个请求可能在服务端的2个线程中进行处理,2个线程共用一个客户端的fd,可能造成数据混乱,最常见的解决方法就是进行加锁

四、设计协程的核心

  • 通过上面的介绍,我们知道了同步与异步之间的差异,有没有一种方式,有异步性能,同步的代码逻辑。来方便编程人员对 IO 操作的 组件呢? 有,采用一种轻量级的协程来实现。在每次 send 或者 recv 之前进行 切换,再由调度器来处理 epoll_wait 的流程
  • 而协程的设计核心目标就是:为了拥有同步的简单编程方式,同时又想要拥有异步的性能

五、实现协程的核心:跳转(协程切换)

  • 协程想要拥有同步的编程方式和异步的性能,因此我们不能对同步的代码进行修改,而要想办法对异步的代码进行修改,使得其
  • 下面我们以https://blog.csdn.net/qq_41453285/article/details/106357786中的HTTP客户端异步实现代码为例
  • 下面且听我细细道来

如何跳转?往哪里跳转?

  • 在代码中,客户端调用async_http_commit()函数向服务端发送一个HTTP请求,为了实现异步的方式,我们在调用send()发送数据之后,把这个fd添加到epoll中进行管理(这是异步的方式),而不是在send()后面调用recv()(这是同步的方式)
8476d0c90a0a522acf1b706d20a4a962.png
  • 下面是async_http_thread_func()函数的代码,这个函数在while(1)循环中一直调用epoll_wait()监听描述符是否有事件发生,例如上面的async_http_commit()函数调用send()给服务端发送数据之后,服务端给客户端回送响应,那么epoll_wait()就会被触发,从而调用recv()接收数据
6aed4ac8ce1c12dde45ae3b091e830b6.png
  • 那么如何用异步代码实现同步的效果呢?那就需要程序进行跳转在
  • 关于跳转可以详细见下面的解释(图片点开来看):
  • (图左侧)async_http_commit()函数接收完数据之后为了实现与同步一样的效果,我们跳转到右侧的async_http_thread_func()函数
  • (图右侧)跳转到async_http_thread_func()函数之后就可以调用epoll_wait()来检测数据了(因为上面的async_http_commit()函数调用了send()发送数据了),如果epoll_wait()检测到有数据来之后就可以在下面接收数据,当接收完数据之后再跳转回到左侧的async_http_commit()函数中
8a7ad918969ff7e21135be33ff568a09.png

跳转的方法有哪些呢?

  • 程序跳转的方法有3种:
  • ①setjmp()、longjmp()函数:不推荐使用,编码复杂
  • ②ucontext
  • ③汇编实现
  • 这3种方法,其中最常用的就是汇编,并且Go等语言的协程也是用汇编实现的

yield、resume

  • yield:让出当前的CPU,跳转到指定的位置进行执行,这个过程叫做yield
  • resume:上面yield让出CPU之后跳转到指定的位置执行,当指定的位置执行完成之后,回到当初的位置这个过程叫做resume
  • 例如,对于上面来说,就是
44a7e7fdf21851a3e0d26a8a21f5ccc5.png

六、如何通过汇编实现协程的切换呢?

  • 下载代码https://github.com/wangbojing/NtyCo开始解析,后面要用到
  • 对于上面的跳转来说,其实就是协程之间的切换,如何实现这种切换呢?
  • 在介绍协程切换之前,来说一下线程的切换,如下图所示:
  • CPU有很多的寄存器,这些寄存器保存了当先在处理器上运行线程的信息
  • 例如当前CPU运行的是A线程,那么寄存器保存的都是线程A的信息
  • 当此时需要把线程A切出CPU,来让B线程在CPU上运行,那么就需要把当前寄存器的内容都保存在线程A的栈中,然后把B运行所需要的内容加载到寄存器中,从而使得B线程在CPU上运行起来
6aeca3906be178279d39fb5bbd4722ad.png
  • 协程如何切换?与线程是相同的道理,还是以上面的图片为例:
  • sync_http_commit()函数调用send()之后,在跳转到pos位置之前把当前寄存器的内容保存到一个结构体中(例如命名为store结构体)
  • 跳转到async_http_thread_func()函数指定的pos位置之后,把pos位置的内容信息加载到寄存器中开始执行

如何实现这些内容的保存与切换

  • 首先到代码的Nty_coroutine.c文件中找到_switch()函数,这个函数是实现切换的核心:
  • 参数1:新的上下文
  • 参数2:当前的上下文
  • _switch()函数就是把当前寄存器的内容保存在参数2中,然后加载参数1所指定的内容加载到寄存器中
  • 例如,如果是上面的sync_http_commit()函数,其在"jump-pos"的时候就调用_switch()进行切换,然后async_http_thread_func()函数执行完需要切换回sync_http_commit()函数的时候,会在"jump->back"的地方调用这个函数,只是参数不同而已
9485d6b19f45f792accdf9f3b6331126.png
  • nty_cpu_ctx结构体就是一些寄存器的指针
d802c4308214f55e8cb3019805be8ee0.png
  • _switch()函数可以用下面的图来表示
c72ba9bc2b1ce9f35d47ea910b237031.png
  • 那么如何实现这些寄存器值的保存与交换呢?
  • 以下面为例,自己看图片吧,稍微有点复杂
  • _switch()函数的参数1名为rdi、参数2名为rsi
  • 在左侧,前一半部分:汇编指令把寄存器的内容保存到rsi中,留下次跳转回来使用;后一半部分:把rdi的内容加载到寄存器中开始使用
  • 0、8、16那些是偏移,因为一个指针就是8字节,所以rsp对应的是esp、rbp对应的是ebp......依次类推
e08aa860197f88aa3df587d643fb8cf6.png
  • X86-64有16个64位寄存器,分别是:
  • %rax:作为函数返回值使用
  • %rsp:栈指针寄存器,指向栈顶
  • %rdi,%rsi,%rdx,%rcx,%r8,%r9:用作函数参数,依次对应第1参数,第2参数......依次类推(例如在_switch()函数中,参数1叫做rdi、参数2叫做rsi......)
  • %rbx,%rbp,%r12,%r13,%14,%15:用作数据存储,遵循被调用者使用规则,简单说就是随便 用,调用子函数之前要备份它,以防他被修改 %r10,%r11 用作数据存储,遵循调用者使用规则,简单说就是使用之前要先保存原值
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估中心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值