代码编织梦想

format,png

我的新课《C2C 电商系统微服务架构120天实战训练营》在公众号儒猿技术窝上线了,感兴趣的同学,可以长按扫描下方二维码了解课程详情:

课程大纲请参见文末

format,png

format,png

文章转载:https://juejin.cn/post/6892687008552976398

前言

网络I/O,可以理解为网络上的数据流。通常我们会基于socket与远端建立一条TCP或者UDP通道,然后进行读写。单个socket时,使用一个线程即可高效处理;然而如果是10K个socket连接,或者更多,我们如何做到高性能处理?

  • 基本概念介绍

  • 网络I/O的读写过程

  • linux下的五种网络I/O模型

  • 多路复用I/O深入理解一波

  • Reactor模型

  • Proacotr模型

基本概念介绍

  • 进程(线程)切换

    • 所有系统都有调度进程的能力,它可以挂起一个当前正在运行的进程,并恢复之前挂起的进程

  • 进程(线程)的阻塞

    • 运行中的进程,有时会等待其他事件的执行完成,比如等待锁,请求I/O的读写;进程在等待过程会被系统自动执行阻塞,此时进程不占用CPU

  • 文件描述符

    • 在Linux,文件描述符是一个用于表述指向文件引用的抽象化概念,它是一个非负整数。当程序打开一个现有文件或者创建一个进程,socket套接字时,内核都会向进程返回一个文件描述符

  • linux信号处理

    • Linux进程运行中可以接受来自系统或者进程的信号值,然后根据信号值去运行相应捕捉函数;信号相当于是硬件中断的软件模拟

在零拷贝机制篇章已介绍过 用户空间和内核空间缓冲区,这里就省略了

网络IO的读写过程

  • 当在用户空间发起对socket套接字的读操作时,会导致进程上下文切换,用户进程阻塞(R1)等待网络数据流到来,从网卡复制到内核;(R2)然后从内核缓冲区向用户进程缓冲区复制。此时进程切换恢复,处理拿到的数据

  • 这里我们给socket读操作的第一阶段起个别名R1,第二阶段称为R2

  • 当在用户空间发起对socket的写操作时(send),导致上下文切换,用户进程阻塞等待(1)数据从用户进程缓冲区复制到内核缓冲区。数据copy完成,此时进程切换恢复

linux五种网络IO模型

阻塞式I/O (blocking IO)

ssize_t recvfrom(int sockfd,void *buf,size_t len,unsigned int flags, struct sockaddr *from,socket_t *fromlen);复制代码

format,png
  • 最基础的I/O模型就是阻塞I/O模型,也是最简单的模型。所有的操作都是顺序执行的

  • 阻塞IO模型中,用户空间的应用程序执行一个系统调用(recvform),会导致应用程序被阻塞,直到内核缓冲区的数据准备好,并且将数据从内核复制到用户进程。最后进程才被系统唤醒处理数据

  • 在R1、R2连续两个阶段,整个进程都被阻塞

非阻塞式I/O (nonblocking IO)

  • 非阻塞IO也是一种同步IO。它是基于轮询(polling)机制实现,在这种模型中,套接字是以非阻塞的形式打开的。就是说I/O操作不会立即完成,但是I/O操作会返回一个错误代码(EWOULDBLOCK),提示操作未完成

  • 轮询检查内核数据,如果数据未准备好,则返回EWOULDBLOCK。进程再继续发起recvfrom调用,当然你可以暂停去做其他事

  • 直到内核数据准备好,再拷贝数据到用户空间,然后进程拿到非错误码数据,接着进行数据处理。需要注意,拷贝数据整个过程,进程仍然是属于阻塞的状态

  • 进程在R2阶段阻塞,虽然在R1阶段没有被阻塞,但是需要不断轮询

多路复用I/O (IO multiplexing)

format,png
  • 一般后端服务都会存在大量的socket连接,如果一次能查询多个套接字的读写状态,若有任意一个准备好,那就去处理它,效率会高很多。这就是“I/O多路复用”,多路是指多个socket套接字,复用是指复用同一个进程

  • linux提供了select、poll、epoll等多路复用I/O的实现方式,是现阶段主流框架常用的高性能I/O模型

  • select或poll、epoll是阻塞调用

  • 与阻塞IO不同,select不会等到socket数据全部到达再处理,而是有了一部分socket数据准备好就会恢复用户进程来处理。怎么知道有一部分数据在内核准备好了呢?答案:交给了系统系统处理吧

  • 进程在R1、R2阶段也是阻塞;不过在R1阶段有个技巧,在多进程、多线程编程的环境下,我们可以只分配一个进程(线程)去阻塞调用select,其他线程不就可以解放了吗

信号驱动式I/O (SIGIO)

format,png
  • 需要提供一个信号捕捉函数,并和socket套接字关联;发起sigaction调用之后进程就能解放去处理其他事

  • 当数据在内核准备好后,进程会收到一个SIGIO信号,继而中断去运行信号捕捉函数,调用recvfrom把数据从内核读取到用户空间,再处理数据

  • 可以看出用户进程是不会阻塞在R1阶段,但R2还是会阻塞等待

  • 性能优于多路复用I/O

异步IO (POSIX的aio_系列函数)

format,png
  • 相对同步IO,异步IO在用户进程发起异步读(aio_read)系统调用之后,无论内核缓冲区数据是否准备好,都不会阻塞当前进程;在aio_read系统调用返回后进程就可以处理其他逻辑

  • socket数据在内核就绪时,系统直接把数据从内核复制到用户空间,然后再使用信号通知用户进程

  • R1、R2两阶段时进程都是非阻塞的

  • 性能高于信号驱动式I/O

多路复用IO深入理解一波

select

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);复制代码

  • 1)使用copy_from_user从用户空间拷贝fd_set到内核空间

  • 2)注册回调函数__pollwait

  • 3)遍历所有fd,调用其对应的poll方法(对于socket,这个poll方法是sock_poll,sock_poll根据情况会调用到tcp_poll,udp_poll或者datagram_poll)

  • 4)以tcp_poll为例,其核心实现就是__pollwait,也就是上面注册的回调函数

  • 5)__pollwait的主要工作就是把current(当前进程)挂到设备的等待队列中,不同的设备有不同的等待队列,对于tcp_poll来说,其等待队列是sk->sk_sleep(注意把进程挂到等待队列中并不代表进程已经睡眠了)。在设备收到一条消息(网络设备)或填写完文件数据(磁盘设备)后,会唤醒设备等待队列上睡眠的进程,这时current便被唤醒了

  • 6)poll方法返回时会返回一个描述读写操作是否就绪的mask掩码,根据这个mask掩码给fd_set赋值

  • 7)如果遍历完所有的fd,还没有返回一个可读写的mask掩码,则会调用schedule_timeout(调用select的进程,也就是current)进入睡眠

  • 8) 当设备驱动发生自身资源可读写后,会唤醒其等待队列上睡眠的进程。如果超过一定的超时时间(timeout指定),还是没人唤醒,则调用select的进程会重新被唤醒获得CPU,进而重新遍历fd,判断有没有就绪的fd

  • 9)把fd_set从内核空间拷贝到用户空间

select的缺点

  • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大

  • 同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大

  • select支持的文件描述符数量太小了,默认是1024

epoll

int epoll_create(int size);   int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);   int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);复制代码

  • 调用epoll_create,会在内核cache里建个红黑树用于存储以后epoll_ctl传来的socket,同时也会再建立一个rdllist双向链表用于存储准备就绪的事件。当epoll_wait调用时,仅查看这个rdllist双向链表数据即可

  • epoll_ctl在向epoll对象中添加、修改、删除事件时,是在rbr红黑树中操作的,非常快

  • 添加到epoll中的事件会与设备(如网卡)建立回调关系,设备上相应事件的发生时会调用回调方法,把事件加进rdllist双向链表中;这个回调方法在内核中叫做ep_poll_callback

epoll的两种触发模式

  • epoll有EPOLLLT和EPOLLET两种触发模式,LT是默认的模式,ET是“高速”模式(只支持no-block socket)

    • LT(水平触发)模式下,只要这个文件描述符还有数据可读,每次epoll_wait都会触发它的读事件

    • ET(边缘触发)模式下,检测到有I/O事件时,通过 epoll_wait 调用会得到有事件通知的文件描述符,对于文件描述符,如可读,则必须将该文件描述符一直读到空(或者返回EWOULDBLOCK),否则下次的epoll_wait不会触发该事件

epoll相比select的优点

  • 解决select三个缺点

    • 对于第一个缺点:epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次(epoll_wait不需要复制)

    • 对于第二个缺点:epoll为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd(不需要遍历)

    • 对于第三个缺点:epoll没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,一般来说这个数目和系统内存关系很大

  • epoll的高性能

    • epoll使用了红黑树来保存需要监听的文件描述符事件,epoll_ctl增删改操作快速

    • epoll不需要遍历就能获取就绪fd,直接返回就绪链表即可

    • linux2.6 之后使用了mmap技术,数据不在需要从内核复制到用户空间,零拷贝

关于epoll的IO模型是同步异步的疑问

  • 概念定义

    • 同步I/O操作:导致请求进程阻塞,直到I/O操作完成

    • 异步I/O操作:不导致请求进程阻塞,异步只用处理I/O操作完成后的通知,并不主动读写数据,由系统内核完成数据的读写

    • 阻塞,非阻塞:进程/线程要访问的数据是否就绪,进程/线程是否需要等待

  • 异步IO的概念是要求无阻塞I/O调用。前面有介绍到I/O操作分两阶段:R1等待数据准备好。R2从内核到进程拷贝数据。虽然epoll在2.6内核之后采用mmap机制,使得其在R2阶段不需要复制,但是它在R1还是阻塞的。因此归类到同步IO

Reactor模型

Reactor的中心思想是将所有要处理的I/O事件注册到一个中心I/O多路复用器上,同时主线程/进程阻塞在多路复用器上;一旦有I/O事件到来或是准备就绪,多路复用器返回,并将事先注册的相应I/O事件分发到对应的处理器中

相关概念介绍:

  • 事件:就是状态;比如:读就绪事件指的是我们可以从内核读取数据的状态

  • 事件分离器:一般会把事件的等待发生交给epoll、select;而事件的到来是随机,异步的,所以需要循环调用epoll,在框架里对应封装起来的模块就是事件分离器(简单理解为对epoll封装)

  • 事件处理器:事件发生后需要进程或线程去处理,这个处理者就是事件处理器,一般和事件分离器是不同的线程

Reactor的一般流程

  • 1)应用程序在事件分离器注册读写就绪事件读写就绪事件处理器

  • 2)事件分离器等待读写就绪事件发生

  • 3)读写就绪事件发生,激活事件分离器,分离器调用读写就绪事件处理器

  • 4)事件处理器先从内核把数据读取到用户空间,然后再处理数据format,png

单线程 + Reactor

format,png

多线程 + Reactor

format,png

多线程 + 主从Reactor

format,png

Proactor模型的一般流程

  • 1)应用程序在事件分离器注册读完成事件读完成事件处理器,并向系统发出异步读请求

  • 2)事件分离器等待读事件的完成

  • 3)在分离器等待过程中,系统利用并行的内核线程执行实际的读操作,并将数据复制进程缓冲区,最后通知事件分离器读完成到来

  • 4)事件分离器监听到读完成事件,激活读完成事件的处理器

  • 5)读完成事件处理器直接处理用户进程缓冲区中的数据format,png

Proactor和Reactor的区别

  • Proactor是基于异步I/O的概念,而Reactor一般则是基于多路复用I/O的概念

  • Proactor不需要把数据从内核复制到用户空间,这步由系统完成

END

征稿:愿意技术分享的朋友,欢迎投稿,每篇文章提供 800 ~ 1000 元的稿酬

投稿请扫描下方二维码,添加微信:jeversoncui

format,png

format,png

format,png

format,png

format,png

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接: https://blog.csdn.net/qq_42046105/article/details/111055601

还在网上找Demo?这个Java项目真香!-爱代码爱编程

我前几天专门去网上搜集项目,打算找一些分享给大家用来实践。 但找了一圈发现,网上的项目都比较旧,业务也有些过时,再有就是一些特别大型的项目,不适合初学者做。 不过,我最终还是发现了1个免费的Java训练营。 这个训练营,会有老师带着做实战项目并部署上线,而且训练营还会从基础讲起,带你更系统的学习Java。 我专门体验过上一期,感受非常好!

Google 鼓励的 13 条代码审查标准,建议收藏!-爱代码爱编程

点击上方蓝色“程序猿DD”,选择“设为星标” 回复“资源”获取独家整理的学习资料! 作者 | Rafiullah Hamedy 来源 | https://medium.com/better-programming/13-code-review-standards-inspired-by-google-6b8f99f7fd67 以下为译文:

全面解读操作系统中的内存管理,你懂几点?-爱代码爱编程

关注+星标公众号,不错过精彩内容 转自 | 程序喵大人 今天分享关于内存的几点内容: 什么是物理内存使用物理内存有什么缺点?什么是虚拟内存?虚拟内存如何映射到物理内存什么是分页内存管理?什么是缺页中断?页面置换算法都有哪些?什么是分段内存管理?01 什么是物理内存? 我们常说的物理内存大小就是指内存条的大小,一般买电脑时都会看下内存条是

提高生产力,最全 MyBatis Plus 讲解!-爱代码爱编程

如果你每天还在重复写 CRUD 的 SQL,如果你对这些 SQL 已经不耐烦了,那么你何不花费一些时间来阅读这篇文章,然后对已有的老项目进行改造,必有收获! 一、MP 是什么 MP 全称 Mybatis-Plus ,套用官方的解释便是成为 MyBatis 最好的搭档,简称基友。它是在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率

Spring 事务的那些坑,都在这里了!-爱代码爱编程

Java技术栈 www.javastack.cn 关注阅读更多优质文章 作者:蚊子squirrel来源:www.jianshu.com/p/a4229aa79ace Spring框架已是JAVA项目的标配,其中Spring事务管理也是最常用的一个功能,但如果不了解其实现原理,使用姿势不对,一不小心就可能掉坑里。 为了更透彻的说明这些坑,

新来的小哥这样写代码!同事直呼“中毒了”~-爱代码爱编程

程序员的成长之路 互联网/程序员/技术/资料共享  关注 阅读本文大概需要 5.5 分钟。 tips:文末有好书推荐~ 沉淀、分享、成长,让自己和他人都能有所收获!???? 目录 一、前言二、代码有毒!1. 方法命名2. 最佳排序3. 有点烧脑4. 迷之求和5. 花里胡哨6. 数字判断7. 代码健壮8. 性能优

爱立信、意大利电信及高通公司基于5G毫米波创下远程传输速度纪录-爱代码爱编程

意大利电信(TIM)、爱立信和高通技术公司(Qualcomm Technologies,Inc.)将5G技术应用于固定无线接入(FWA)后,超宽带远距离传输速度已达到创纪录水平。在距站点6.5公里的场景下,意大利电信的实时网络基于26 GHz频段的毫米波(mmWave)频谱创下了1 Gbps(UDP数据流速度达到1 Gbps,TCP数据流速度达到

拔剑四顾心茫然,谁来为工业互联网保驾护航?-爱代码爱编程

大数据产业创新服务媒体 ——聚焦数据 · 改变商业 12月4日下午,在莫斯科和圣彼得堡维护着覆盖8000多只包裹储物柜的网络服务商PickPoint遭到黑客攻击,使这些个人包裹面临被偷盗风险。 原来,莫斯科人在网上订购产品之后,可以让商家将包裹配送到PickPoint储物柜,而不是自己的家庭住址。等包裹到达后,PickPoint会发送取

【观察】新华三以立体化智能联接,释放全行业数字化新动能-爱代码爱编程

申耀的科技观察 读懂科技,赢取未来! 1993年,乔治·吉尔德以计算机网络先驱罗伯特·梅特卡夫的姓氏命名了著名的“梅特卡夫定律”,即一个网络的价值等于该网络内的节点数的平方,且该网络的价值与联网的用户数的平方成正比。 简单地理解,网络接入的终端设备越多,其价值就越大,如果用公式来表示就是V=K×N ²,但在万物互联的新时代,因联接催生出

基于TCP的网络实时聊天室(socket通信案例)-爱代码爱编程

作者:Charzous blog.csdn.net/Charzous/article/details/109540279 开门见山 最近一个月记录了学习Socket网络编程的知识和实战案例,相对来说,比较系统地学习了基于TCP协议实现网络通信,也是计算机网络中重中之重,TCP/IP属于网络层,在java中,对该层的工作实现了封装,在编程中,

记住看小电影前一定要检查一下域名是不是 HTTPS 的,不然...-爱代码爱编程

点击上方[全栈开发者社区]→右上角[...]→[设为星标⭐] 看小电影还是浏览正常网站,一定要检查是不是 HTTPS 的,HTTP有可能被中间人攻击和拦截,下面就是详细的 HTTPS 原理,细思极恐。 1. HTTP 协议 在谈论 HTTPS 协议之前,先来回顾一下 HTTP 协议的概念。 1.1 HTTP 协议介绍HTTP 协议是一种基于文

业务视角下的“个性化”网络空间资产安全管理-爱代码爱编程

业务视角下,网络空间资产安全管理需要“个性化” 网络空间资产安全管理的目标是安全。然而,“纯粹”的安全却并不存在,若能保障业务系统顺利平稳运行,并具备应急处置、快速恢复能力,便是现实意义上的安全了。 所以,基于积极主动防御和快速响应处置的安全管理才能最终实现安全目标,这也决定了网络空间资产安全管理具备行业化和习成化等不同维度的个性化需求。