【图解IO与Netty系列】IO多路复用

IO多路复用

  • 为什么要使用IO多路复用
  • Linux的IO多路复用接口
    • select
    • poll
    • epoll

为什么要使用IO多路复用

我们常用的IO模型是BIO,我们Java里的IO流大多数都是BIO,也就是同步阻塞式IO,这种IO操作的好处是简单方便,但是缺点也很明显——性能不高。

阻塞式IO的特点就是在数据未就绪前,要等待数据就绪。比如以网络IO为例,在客户端没有发送数据前,服务端调用read函数会阻塞等待客户端发送数据,当客户单发送的数据到达服务端网卡缓存区,并被DMA拷贝到操作系统内核时,服务端的用户线程才开始拷贝数据到用户空间。由于BIO是同步阻塞IO,这个“同步”就是把内核空间的数据拷贝到用户空间的这个工作由用户线程自己完成,在数据拷贝期间用户线程依然是阻塞的。

在这里插入图片描述

于是就有了NIO,NIO是同步非阻塞式IO,在客户端未发送数据前,服务端用户线程线程调用read()函数不会阻塞,而是马上返回一个error,用户线程可以先干点别的事情,然后再次尝试read(),如果此时数据就绪,那么read()函数会阻塞,用户线程把内核空间的数据拷贝到用户空间。

在这里插入图片描述

NIO比起BIO来说性能有所提供,在数据未就绪时不需要阻塞等待。由于数据未就绪时不会阻塞当前线程,因此把所有需要监听的Socket套接字收集到一个集合当中,当前线程可以轮询这个套接字集合,哪个套接字就绪就阻塞读取哪个套接字的数据。这样就由一个线程监听一个Socket变成一个线程监听多个Socket。

在这里插入图片描述

但是NIO需要用户线程不断轮询,这是非常消耗CPU资源的一种操作,效率也不高。我们应该让当前线程阻塞监听多个socket,如果有一个或多个socket就绪,当前线程才解阻塞,然后我们可以把就绪socket的读取操作提交给线程池执行,这样效率就得到提升。于是就是有了IO多路复用。

我们使用IO多路复用时,通常要先注册需要监听的socket文件描述符以及在该socket上关注的事件(读就绪事件、写就绪事件、连接就绪事件)。

在这里插入图片描述

操作系统内核会使用对应的数据结构保存注册进来的socket文件描述符,当对应的socket有事件就绪时,操作系统会通知阻塞的用户线程;用户线程接收到通知后会解阻塞,用户线程解阻塞后可以自己操作有事件就绪的Socket,也可以提交到线程池处理。

在这里插入图片描述

为什么IO多路复用会比BIO和NIO的性能高呢?原因有两点:

  • IO多路复用可以一个线程监听多个Socket。
  • IO多路复用的read操作都是有效的read,是收到操作系统内核的Socket已就绪通知时才发起的read操作,没有了等待数据就绪期间的阻塞。

Linux的IO多路复用接口

Linux提供的IO多路复用接口一共有select、poll、epoll三种。

在这里插入图片描述

select

int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval 
*timeout);

select函数向操作系统注册需要监听的文件描述符数组,数组最大上限被限制为1024(默认1024,可以修改),Linux操作系统内核会保存这个文件描述符数组。注册的数组分三个,分别是关注读就绪事件的readfds,关注写就绪事件的writefds,关注异常事件的exceptfds。

在这里插入图片描述

当调用select函数后,当前线程就处于阻塞状态。当数组中的某个或某几个socket有事件发生时,当前线程会解阻塞,然后当前线程需要遍历数组中的每一个socket文件描述符,判断它是否有事件发生。

在这里插入图片描述

使用select函数的IO多路复用是比BIO和NIO的性能高不少,但是存在两个缺点:

  • socket文件描述符数组长度有限制,意味着监听的socket有上限。
  • 当有socket就绪时,用户线程并不知道数组中哪些socket文件描述符就绪,需要遍历。

poll

int poll (struct pollfd *fds, unsigned int nfds, int timeout);

poll使用pollfd结构体存在需要监听的socket文件描述符以及关注的事件。

在这里插入图片描述

当前线程调用poll函数,会以pollfd数组的形式向操作系统内核注册需要监听的socket文件描述符。

在这里插入图片描述

调用了poll函数之后,当前线程阻塞。当某个或某几个pollfd中的socket文件描述符有事件就绪时,操作系统内核会通知当前线程,当前线程解阻塞。当前线程解阻塞之后,还是需要遍历pollfd数组,判断每一个pollfd是否关注的事件已就绪。

在这里插入图片描述

poll解决了select有监听socket文件描述符上限的问题,理论上poll可以监听无数个socket。但是与select一样,用户线程并不知道哪些socket就绪,还是需要遍历数组,因此效率还是有待提高。因此就有了epoll这种不需要遍历的IO多路复用接口。

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);

相比与select与poll接口的只有一个函数,epoll有三个函数——epoll_create、epoll_ctl、epoll_wait。

epoll_create创建一个epoll实例,epoll实例使用一个红黑树结构保存注册进来的socket文件描述符,然后使用一个链表保存就绪的socket文件描述符,此时epoll实例还是空的。

在这里插入图片描述

epoll_ctl函数则是向epoll实例添加需要监听的socket文件描述符以及关注的事件类型,socket描述符以及关注的事件类型被包装在epoll_event结构体中。

在这里插入图片描述

epoll_wait函数是阻塞等待注册到epoll实例的一个或多个文件描述符就绪。当epoll实例中的某个socket有事件就绪时,会把对应的epoll_event拷贝到epoll实例中的链表结构中。当用户线程调用epoll_wait函数时,如果epoll实例中的链表结构没有就绪的epoll_event时,会阻塞等待,如果epoll_event非空,则把这个链表拷贝到用户空间,此时用户线程就可以逐一处理链表中就绪的socket,无需遍历判断是否就绪。

在这里插入图片描述

epoll解决了监听的socket有上限和需要遍历判断socket是否就绪的两个问题,大大提供了Linux里IO多路复用的性能。

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

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

相关文章

【雷丰阳-谷粒商城 】【分布式基础篇-全栈开发篇】【00】补充

持续学习&持续更新中… 守破离 【雷丰阳-谷粒商城 】【分布式基础篇-全栈开发篇】【00】补充 WindowsCMD插件IDEAVsCode MavenvagrantDocker解决MySQL连接慢问题启动(自动)Docker注意切换到root用户远程访问MySQL MyBatisPlus代码地址参考 WindowsC…

vs2013使用qt Linguist以及tr不生效问题

一、qt Linguist(语言家)步骤流程 1、创建翻译文件,在qt选项中 2.选择对应所需的语言,得到.ts后缀的翻译文件 3.创建.pro文件,并将.ts配置在.pro文件中 3.使用qt Linguist 打开创建好的以.ts为后缀的翻译文件,按图所示…

YOLOv10 | 手把手教你利用yolov10训练自己数据集(含环境搭建 + 参数解析 + 数据集查找 + 模型训练、推理、导出)

一、前言 本文内含YOLOv10网络结构图 各个创新模块手撕结构图 训练教程 推理教程 参数解析 环境搭建 数据集获取等一些有关YOLOv10的内容! 目录 一、 前言 二、整体网络结构图 三、空间-通道分离下采样 3.1 SCDown介绍 3.2 C2fUIB介绍 3.3 PSA介绍 …

Java核心: 脚本引擎和动态编译

静态语言和动态语言的在相互吸收对方的优秀特性,取人之长补己之短。脚本引擎和动态编译就是其中一个关键特性,扩展了Java的能力边界。这一篇我们主要讲两个东西: ScriptEngine,执行脚本语言代码,如JavaScript、Groovy JavaCompile…

P459 包装类Wrapper

包装类的分类 1)针对八种基本数据类型相应的引用类型——包装类。 2)有了类的特点,就可以调用类中的方法。 Boolean包装类 Character包装类 其余六种Number类型的包装类 包装类和基本数据类型的相互转换 public class Integer01 {publi…

关于数据库和数据表的基础SQL

目录 一. 数据库的基础SQL 1. 创建数据库 2. 查看当前有哪些数据库 3. 选中数据库 4. 删除数据库 5. 小结 二. 数据表的基础SQL 1. 创建数据表 2. 查看当前数据库中有哪些表 3. 查看指定表的详细情况(查看表的结构) 4. 删除表 5. 小结 一. 数据库的基础SQL 1. 创建…

Redis篇 redis基本命令和定时器原理

基本命令和定时器原理 一. exists命令二. del命令三. Expire命令四. ttl命令五. redis的过期策略六. 定时器的两种设计方式七. type命令 一. exists命令 用来判断key的值是否存在 返回值是key的个数 这样写的话,有没有什么区别呢? 效率变低,消…

用例篇

弱网测试 弱网测试的目的是尽可能保证用户体验,关注的关键点包括: 页面响应时间是否可以接受,关注包括哦热启动、冷启动时间、页面切换、前后台切换、首字时间,首屏时间等。页面呈现是否完成一致。超时文案是否符合定义&#xf…

YoloV1模型

You Only Look Once 文章目录 You Only Look Once置信度定义类别条件概率NMSnp.maxmiumnp.argsort() Yolov1直接采用网络特征输出,实现置信度预测、分类、边界框回归; 核心内容总结: 输入图像划分为 S S 网格。如果对象的中心落入网格单元中…

Java——认识Java

一、介绍 1、起源 Java 是由 Sun Microsystems 于 1995 年推出的一种面向对象的编程语言和计算平台。由詹姆斯高斯林(James Gosling,后来被称为Java之父)和他的同事们共同研发。后来,Sun 公司被 Oracle(甲骨文&#…

vue3 vite动态根据字符串加载组件

1 原理 import.meta.glob() 其实不仅能接收一个字符串,还可以接收一个字符串数组,就是匹配多个位置 let RouterModules import.meta.glob(["/src/view/*/*.vue", "/src/view/*.vue"]);这样我们就拿到了相对路劲的组件对象&#xf…

leedcode【142】. 环形链表 II——Java解法

Problem: 142. 环形链表 II 思路解题方法复杂度Code性能 思路 1.用快慢指针找到相遇的点(快指针一次走一步,慢指针一次走两步) 2.一个指针从head开始,一个指针从相遇点开始,一次一步,相遇处即为环入口 解题…

【模版方法设计模式】

文章目录 模板方法设计模式模板方法的设计原则模板方法设计模式组成部分代码实现抽象类实现具体实现类执行 模板方法设计模式 模版方法设计模式(Template Method Pattern)是一种行为设计模式,它定义了一个操作中的算法骨架,而将一…

网络应用层之(1)DHCPv6协议

网络应用层之(1)DHCPv6协议 Author: Once Day Date: 2024年5月26日 一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦… 漫漫长路,有人对你微笑过嘛… 全系列文章可参考专栏: 通信网络技术_Once-Day的博客-C…

设计模式八股文

什么是设计模式? 设计模式是软件开发过程中经常遇到的问题的通用解决方案。类似于前人总结的经验,遇到相似问题的时候有个参考。 设计模式七大基本原则? 单一职责:一个类应该只作一件事情。将功能分为小的独立的单元。开放封闭…

文心一言 VS 讯飞星火 VS chatgpt (267)-- 算法导论20.2 2题

二、写出 PROTO-vEB-DELETE 的伪代码。通过扫描簇内的相关位,来更新相应的 summary 位。并且你实现的伪代码的最坏情况运行时间是多少?如果要写代码,请用go语言。 文心一言: 首先,让我们回顾一下vEB(Van …

matplotlib latex表格

使用python3环境 import matplotlib.gridspec as gridspec import matplotlib.pyplot as pltimport numpy as np import matplotlib as mpl #mpl.use(pgf)def figsize(scale, nplots 1):fig_width_pt 390.0 # Get this from LaTeX using \the\text…

学 Python 具体能干什么?

Python 是一种功能强大、用途广泛的编程语言,因其简洁易读的语法和丰富的库生态系统而备受欢迎。学习 Python后,你可以从事以下几方面的工作: 1. Web 开发 Python 有很多流行的 Web 框架,如: Django:一个…

无畏并发: Rust Mutex的基本使用

并发是很多编程语言避不开的一块主要内容,主打一个无畏并发的Rust自然也面临这样的挑战。Rust中的Mutex提供了强大的同步原语,确保共享数据的线程安全,这篇文章中,我们会探讨Mutex的使用,从基础的用法到一些高阶内容。…

数据结构(六)图

2024年5月26日一稿(王道P220) 6.1 图的基本概念 6.1.1 图的定义 6.2 图的存储及基本操作 6.2.1邻接矩阵法 6.2.2 邻接表