Linux怎样处理网络请求——彻底理解IO多路复用

常见的网络IO模型

网络 IO 模型分为四种:同步阻塞 IO、同步非阻塞IO、IO 多路复用、异步非阻塞 IO(Async IO, AIO),其中AIO为异步IO,其他都是同步IO

同步阻塞IO

同步阻塞IO:在线程处理过程中,如果涉及到IO操作,那么当前线程会被阻塞,直到IO处理完成,线程才接着处理后续流程。如下图,服务器针对客户端的每个socket都会分配一个新的线程处理,每个线程的业务处理分2步,当步骤1处理完成后遇到IO操作(比如:加载文件),这时候,当前线程会被阻塞,直到IO操作完成,线程才接着处理步骤2。

同步阻塞IO 演示图

实际使用场景

在Java中使用线程池的方式去连接数据库,使用的就是同步阻塞IO模型。

模型的缺点

因为每个客户端存都需要一个新的线程,势必导致线程被频繁阻塞和切换带来开销。

同步非阻塞 IO-NIO(New IO)

同步非阻塞IO:在线程处理过程中,如果涉及到IO操作,那么当前的线程不会被阻塞,而是会去处理其他业务代码,然后等过段时间再来查询 IO 交互是否完成。如下图:Buffer 是一个缓冲区,用来缓存读取和写入的数据;Channel 是一个通道,负责后台对接 IO 数据;而 Selector 实现的主要功能,是主动查询哪些通道是处于就绪状态。Selector复用一个线程,来查询已就绪的通道,这样大大减少 IO 交互引起的频繁切换线程的开销。

实际使用场景

Java NIO 正是基于这个 IO 交互模型,来支撑业务代码实现针对 IO 进行同步非阻塞的设计,从而降低了原来传统的同步阻塞 IO 交互过程中,线程被频繁阻塞和切换带的开销。

NIO使用的经典案例是Netty框架,Elasticsearch底层实际上就是采用的这种机制。

IO多路复用

  • IO多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;没有文件句柄就绪时会阻塞应用程序,交出cpu。多路是指网络连接,复用指的是同一个线程

所以,每个客户端和服务器的socket 连接就可以看做”一路“,多个客户端和该服务器的socket连接就是”多路“,从而,IO多路就是多个socket连接上的输入输出流,复用就是多个socket连接上的输入输出流由一个线程处理。 因此 IO多路复用可以定义如下:

Linux中的 IO多路复用是指:一个线程处理多个IO流

IO多路复用3种实现方式

select/pool/epool

基本socket模型

先看下socket模型,以便与下面几种实现方式对比:

listenSocket = socket() // 系统调用socket(),创建一个主动socket

bind(listenSocket) // 给主动socket绑定地址和端口

listen(listenSocket) // 将默认的主动socket转换为服务器的被动socket(也叫监听socket)

while(true) {

    connSocket = accept(listenSocket) // 接受客户端连接,获取已链接socket

    recv(connSocket) // 从客户端读取数据,只能同时处理一个客户端

    send(connSocket) // 往客户端发送数据,只能同时处理一个客户端
}

实现网络通信流程如下图

基础的socket模型,能够实现服务器端和客户端的通信,但程序每调用一次accept函数,只能处理一个客户端请求,当有大量客户端连接时,这种模型处理性能较差,因此linux提供了高性能的IO多路复用机制来解决这种困境。

select机制

select是最古老的I/O多路复用机制,可以同时监听多个文件描述符的读写事件。它使用的fd_set数据结构来存储待监听的文件描述符集合,并通过select()函数将fd_set集合传递给内核,等待内核返回文件描述符的状态变化。

fd_set数据结构 (bitmap)

typedef struct {
    unsigned long fds_bits[__FDSET_LONGS];
} fd_set;
/**
*  参数说明
*  监听的文件描述符数量__nfds、
*  被监听描述符的三个集合*__readfds,*__writefds和*__exceptfds
*  监听时阻塞等待的超时时长*__timeout
*  返回值:返回一个socket对应的文件描述符
*/
int select(int __nfds, fd_set * __readfds, fd_set * __writefds, fd_set * __exceptfds, struct timeval * __timeout)

select实现网络通信流程如下图:

 缺点

1、select使用的fd_set数据结构对单个进程能监听的文件描述符是有限制的,默认是1024

2、select()函数返回后,需要遍历文件描述符集合,才能找到就绪的描述符,遍历过程会产生一定开销,降低性能。

poll机制

poll与select类似,也可以同时监听多个文件描述符的读写事件。它使用的pollfd数据结构来存储待监听的文件描述符集合,并通过pool()函数将pollfd集合传递给内核,等待内核返回文件描述符的状态变化。相对于select,poll没有fd_set集合大小的限制,但并没有解决轮询获取就绪fd的问题,效率也不高。

pollfd结构体的定义

struct pollfd {
    int fd;         //进行监听的文件描述符
    short int events;       //要监听的事件类型
    short int revents;      //实际发生的事件类型
};

poll实现网络通信流程如下图:

 epoll机制

epoll 是对 select 和 poll 的改进,解决了“性能开销大”和“文件描述符数量少”这两个缺点,是性能最高的多路复用实现方式,能支持的并发量也是最大。

总结下,epoll 相关的函数里内核运行环境分两部分:

  • 用户进程内核态。进行调用 epoll_wait  等函数时会将进程陷入内核态来执行。这部分代码负责查看接收队列,以及负责把当前进程阻塞掉,让出 CPU。

  • 硬软中断上下文。在这些组件中,将包从网卡接收过来进行处理,然后放到 socket 的接收队列。对于 epoll 来说,再找到 socket 关联的 epitem,并把它添加到 epoll 对象的就绪链表中。这个时候再捎带检查一下 epoll 上是否有被阻塞的进程,如果有唤醒之。

epoll 的特点是:

1)使用红黑树存储一份文件

2)通过异步 IO 事件找到就绪的文件描述符,而不是通过轮询的方式;

3)使用队列存储就绪的文件描述符,且会按需返回就绪的文件描述符,无须再次遍历;

epoll接口

图片

1、epoll_create 用于创建一个 eventpoll 对象

epoll 实例内部维护了3个结构,

图片

具体如代码所示:

// 数据结构
// 每一个epoll对象都有一个独立的eventpoll结构体
// 用于存放通过epoll_ctl方法向epoll对象中添加进来的事件
// epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem元素即可
struct eventpoll {
   
    /* 就绪的描述符的双链表. 
      当有的连接数据就绪的时候,内核会把就绪的连接放到 rdllist 链表里。
      这样应用进程只需要判断链表就能找出就绪进程,而不用去遍历整棵树。
    */
    struct list_head rdlist;

    /* 一颗红黑树,通过这棵树来管理用户进程下添加进来的所有socket连接。*/
    struct rb_root  rbr;


   /* wq: 等待队列链表。软中断数据就绪的时候会通过 wq 来找到阻塞在 epoll 对象上的用户进程。存放阻塞的进程*/
    wait_queue_head_t wq;      

    ......
};

2、epoll_ctl 负责把服务端和客户端建立的 socket 连接注册到 eventpoll 对象里,会做三件事:

1)创建一个红黑树节点对象 epitem ,主要包含两个字段,分别存放 socket fd 即连接的文件描述符,和所属的 eventpoll 对象的指针;

struct epitem {

    //红黑树节点
    struct rb_node rbn;

    //socket文件描述符信息
    struct epoll_filefd ffd;

    //所归属的 eventpoll 对象
    struct eventpoll *ep;

    //等待队列
    struct list_head pwqlist;
}

2)添加等待事件到socket的等待队列中,其回调函数是将回调函数ep_poll_callback,ep_poll_callback通过回调default_wake_up_function唤醒用户进程

3)将 epitem 对象插入红黑树

3、epoll_wait 用于等待其管理的连接上的 IO 事件

1) 检查 eventpoll 对象的就绪的连接 rdllist 上是否有数据到达;

2) 如果没有就把当前的进程描述符加入到 eventpoll 的进程等待队列里,然后阻塞当前进程,等待数据到达时通过回调函数被唤醒

图片

 3) 当 eventpoll 监控的连接上有数据到达时,通过下面几个步骤唤醒对应的进程处理数据:

1)socket 的数据接收队列有数据到达,会通过进程等待队列的回调函数 ep_poll_callback 唤醒红黑树中的节点 epitem;

2)ep_poll_callback 函数将有数据到达的 epitem 添加到 eventpoll 对象的就绪队列 rdllist 中;

3)ep_poll_callback 函数检查 eventpoll 对象的进程等待队列上是否有等待项,通过回调函数 default_wake_func 唤醒这个进程,进行数据的处理;

4)当进程醒来后,继续从 epoll_wait 时暂停的代码继续执行,把 rdlist 中就绪的事件返回给用户进程,让用户进程调用 recv 把已经到达内核 socket 等待队列的数据拷贝到用户空间使用。

软中断回调的时候回调函数也整理一下
sock_def_readable:sock 对象初始化时设置的

=>

ep_poll_callback : epoll_ctl 时添加到 socket 上的

=>

default_wake_function: epoll_wait 是设置到 epoll上的

图片

  • 在socket等待队列中,其回调函数是ep_poll_callback,另外其 private 没有用了,指向的是空指针 null。
  • 在 eventpoll 的等待队列项中,回调函数是 default_wake_function。其 private 指向的是等待该事件的用户进程。

ET模式与LT模式的区别

  • epoll有EPOLLLT和EPOLLET两种触发模式,LT是默认的模式,ET是“高速”模式。
  • ET模式(边缘触发): epoll_wait 遍历 ready_list 的时候,会把 socket 从 ready_list 里面移除,然后读取这个 scoket 的事件。
  • LT模式(水平触发)下,会把 socket 从 ready_list 里面移除,然后读取这个 scoket 的事件,如果这个 socket 返回了感兴趣的事件那么当前这个 socket 会再被加入到 ready_list 中,这样下次调用 epoll_wait 的时候,还能拿到这个 socket。

举个栗子:

如果此时一个客户端同时发来了 5 个数据包,按正常的逻辑,只需要唤醒一次 epoll ,把当前 socket 加一次到 ready_list 就行了,不需要加 5 次。然后用户程序可以把 socket 接收队列的所有数据包都读完。

但假设用户程序就读了一个包,然后处理报错了,后面不读了,那后面的 4 个包咋办?

如果是 ET 模式,就读不了了,因为没有把 socket 加入到 ready_list 的触发条件了。除非这个客户端发了新的数据包过来,这样才会再把当前 socket 加入到 ready_list,在新包过来之前,这 4 个数据包都不会被读到。

而 LT 模式不一样,因为每次读完有感兴趣的事件发生之后,会把当前 socket 再加入到 ready_list,所以下次肯定能读到这个 socket,所以后面的 4 个数据包会被访问到,不论客户端是否发送新包。

因此,在 LT模式下开发基于 epoll的应用要简单一些,不太容易出错,而在 ET模式下事件发生时,如果没有彻底地将缓冲区数据处理完,则会导致缓冲区中的用户请求得不到响应。

3种机制底层实现的区别

select和poll都是通过轮询的方式,即内核每次要遍历监听的文件描述符集合,判断每个文件描述符是否有I/O事件发生;

而epoll底层实现是基于事件通知的方式,即当文件描述符状态发生变化时,内核会向应用程序发起事件通知,这种方式避免了无效的遍历,从而提高了效率。

在epoll中,使用epoll_wait函数进行事件监听时,内核将发生的事件文件描述符加入到一个就绪队列中,等待应用程序处理。如果就绪队列中没有任何文件描述符,则epoll_wait函数会阻塞,直到有文件描述符加入就绪队列,这种方式实现了I/O事件的高效处理和调度。

selectpollepoll
数据结构bitmap数组红黑树
最大连接数1024无上限无上限
fd拷贝每次调用select拷贝每次调用poll拷贝fd首次调用epoll_ctl拷贝,每次调用epoll_wait不拷贝
工作效率轮询:O(n)轮询:O(n)回调:O(1)

参考资料:

https://juejin.cn/post/6844904200141438984

IO多路复用机制详解 - 知乎

select poll epoll 区别 和 底层实现-掘金

https://www.cnblogs.com/88223100/p/Deeply-learn-the-implementation-principle-of-IO-multiplexing-select_poll_epoll.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/77076.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

这场大学生竞赛中,上百支队伍与合合信息用AI共克难题

目录 0 校企联合共克难题1 北京林业大学:文档格式转换2 浙江中医药大学:个性化题库3 中南林业科技大学:交互场景挖掘4 重庆邮电大学:大模型赋能智能文档5 总结 0 校企联合共克难题 近日,中国大学生服务外包创新创业大…

web前端开发基础入门html5+css3+js学习笔记(一)

目录 1.第一个前端程序2.前端工具的选择与安装3.VSCode开发者工具快捷键4.HTML5简介与基础骨架4.1 HTML5的DOCTYPE声明4.2 HTML5基本骨架4.2.1 html标签4.2.2 head标签4.2.3 body标签4.2.4 title标签4.2.5 meta标签 5.标签之标题5.1 快捷键5.1 标题标签位置摆放 6.标签之段落、…

GDP药品供应管理规范确保冷链运输合规性

药品运输面临许多挑战,包括产品可能因暴露在不利条件下导致降解。药品供应管理规范 (GDP) 运输指南在确保整个运输链的冷链合规性方面发挥着关键作用。 药品的分销与生产和制造生产线一样精细和敏感。自全球物流公司成立以来,配送过程中对受控环境的需求…

AI Chat 设计模式:14. 适配器模式

本文是该系列的第十四篇,采用问答式的方式展开,问题由我提出,答案由 Chat AI 作出,灰色背景的文字则主要是我的一些思考和补充。 问题列表 Q.1 关于适配器模式,如果由浅入深的来考察,你会依次提出什么问题…

【腾讯云 Cloud Studio 实战训练营】Hexo 框架 Butterfly 主题搭建个人博客

什么是Cloud Studio Cloud Studio 是基于浏览器的集成式开发环境(IDE),为开发者提供了一个永不间断的云端工作站。用户在使用 Cloud Studio 时无需安装,随时随地打开浏览器就能在线编程。 ​ Hexo 博客成品展示 本人博客如下&…

根据一棵树的两种遍历构造二叉树

题目 给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。 示例 1: 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出: [3,9,20,null,null,…

sql server安装报错 合成活动模板库(ATL) 失败

错误 “合成活动模板库(ATL) 规则失败“ 解决办法: 进入SQL Server 2008R2安装包目录找到文件:sqlsupport_msi,安装此文件之后,再安装SQL Server,便可解决该问题。C:\SQL Server 2008R2\new\SQL Server 2008R2\2052_CH…

uniapp开发微信小程序使用painter将页面转换为图片并保存到本地相册

引言 我使用到painter的原因是,在uniapp开发微信小程序时,需要将一个页面的内容转换成图片保存到本地相册。 起初在网上找到很多都是在uniapp中使用 html2canvas 将网页转换成图片再jspdf将图片转换为pdf,但是这种方式在小程序环境不支持&am…

【数据结构】二叉树链式结构的实现及其常见操作

目录 1.手搓二叉树 2.二叉树的遍历 2.1前序、中序以及后序遍历 2.2二叉树的层序遍历 3.二叉树的常见操作 3.1求二叉树节点数量 3.2求二叉树叶子节点数量 3.3求二叉树第k层节点个数 3.3求二叉树的深度 3.4二叉树查找值为x的节点 4.二叉树的销毁 1.手搓二叉树 在学习…

[数据集][目标检测]钢材表面缺陷目标检测数据集VOC格式2279张10类别

数据集格式:Pascal VOC格式(不包含分割路径的txt文件和yolo格式的txt文件,仅仅包含jpg图片和对应的xml) 图片数量(jpg文件个数):2279 标注数量(xml文件个数):2279 标注类别数:10 标注类别名称:["yueyawan",&…

ceph数据分布

ceph的存储是无主结构,数据分布依赖client来计算,有两个条主要路径。 1、数据到PG 2、PG 到OSD 有两个假设: 第一,pg的数量稳定,可以认为保持不变; 第二, OSD的数量可以增减,OSD的…

【TI-CCS笔记】工程编译配置 bin文件的编译和生成 各种架构的Post-build配置汇总

【TI-CCS笔记】工程编译配置 bin文件的编译和生成 各种架构的Post-build配置汇总 TI编译器分类 在CCS按照目录下 有个名为${CG_TOOL_ROOT}的目录 其下就是当前工程的编译器 存放目录为: C:\ti\ccs1240\ccs\tools\compiler按类型分为五种: ti-cgt-arm…

Appium Desktop安装

【提示:官方已不再维护,建议命令行方式安装,但可以学习了解一下】 Appium Desktop是一款适用于Mac、Windows和Linux的应用程序,它以漂亮灵活的UI为您提供Appium自动化服务器的强大功能。它基本上是Appium Server的图形界面。您可…

【嵌入式环境下linux内核及驱动学习笔记-(19)LCD驱动框架2-FrameBuffer】

目录 1、 Frmebuffer(帧缓冲)操作介绍1.1 显示设备的抽象1.2 内存映像1.3 输出画面数据1.4 用户态下操作屏显1.4.1 用文件I / O 操作屏显1.4.2 mmap() 函数1.4.3 ioctl()函数1.4.5 用命令操作屏1.4.6 测试程序 2、Framebuffer总体框架2.1 框架要点2.2 fbmem.c分析2.…

算法通关村第三关【黄金】| 数组元素出现次数问题

1.数字出现的次数超过数组长度的一半 方法一、使用Map键值对来记录每个元素出现的次数&#xff0c;返回次数大于一半的 class Solution {public int majorityElement(int[] nums) {Map<Integer,Integer> map new HashMap<>();for(int i 0;i<nums.length;i){m…

深度学习入门-3-计算机视觉-图像分类

1.概述 图像分类是根据图像的语义信息对不同类别图像进行区分&#xff0c;是计算机视觉的核心&#xff0c;是物体检测、图像分割、物体跟踪、行为分析、人脸识别等其他高层次视觉任务的基础。图像分类在许多领域都有着广泛的应用&#xff0c;如&#xff1a;安防领域的人脸识别…

开源低代码平台Openblocks

网友 HankMeng 想看低代码工具&#xff0c;正好手上有一个&#xff1b; 什么是 Openblocks &#xff1f; Openblocks 是一个开发人员友好的开源低代码平台&#xff0c;可在几分钟内构建内部应用程序。 传统上&#xff0c;构建内部应用程序需要复杂的前端和后端交互&#xff0c;…

百度屏蔽词有哪些?其中就有移民关键词指数被屏蔽?

我是百收网SEO&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 今日tombkeeper消息爆料&#xff1a;百度指数已经屏蔽“移民”等关键词指数。 大家好&#xff0c;我是百收网SEO商学院的狂潮微课老师&#xff0c;今天我们来讲解第 12 节课关键词优化难度分析…

Markdown编辑器 Mac版Typora功能介绍

Typora mac是一款跨平台的Markdown编辑器&#xff0c;支持Windows、MacOS和Linux操作系统。它具有实时预览功能&#xff0c;能够自动将Markdown文本转换为漂亮的排版效果&#xff0c;让用户专注于写作内容而不必关心格式调整。 Typora Mac版除了支持常见的Markdown语法外&#…

国资委79号文解读:国央企OA办公系统信创替代落地实践与标杆案例

国资委79号文解读&#xff1a;国央企OA办公系统信创替代落地实践与标杆案例 2022年9月底&#xff0c;国资委下发了重要的国资发79号文件&#xff0c;全面指导并要求国央企落实信息化系统的信创国产化改造。其中&#xff0c;明确要求所有中央企业在2022年11月底前将安可替代总体…