首页 > 其他分享 >【图解IO与Netty系列】IO多路复用

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

时间:2024-05-26 15:05:51浏览次数:22  
标签:Netty socket 多路复用 epoll 阻塞 线程 IO 就绪

IO多路复用

为什么要使用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多路复用的性能。

标签:Netty,socket,多路复用,epoll,阻塞,线程,IO,就绪
From: https://blog.csdn.net/weixin_43889578/article/details/136128856

相关文章

  • PostgreSQL的扩展(extensions)-常用的扩展之pg_plan_advsr
    PostgreSQL的扩展(extensions)-常用的扩展之pg_plan_advsrpg_plan_advsr是PostgreSQL社区中的一个扩展,用于分析和改进查询执行计划。它能够自动识别哪些查询执行缓慢,并提供优化建议,以提高查询性能。pg_plan_advsr能够为指定的查询生成性能建议,包括索引创建、SQL语句重写......
  • Java NIO通信基础
    第3章  JavaNIO通信基础 NIO弥补了原来面向流的OIO同步阻塞的不足,它为标准java代码提供了高速的、面向缓冲区的IO。JavaNIO由以下三个核心组件组成:●Channel(通道)●Buffer(缓冲区)●Selector(选择器) 1.Channel(通道)在OIO中,同一个网络连接会关联到两个流:一个输入......
  • PTP(Precision Time Protocol)是一种用于精确时间同步的网络协议。它旨在使网络设备能够
    PTP(PrecisionTimeProtocol)是一种用于精确时间同步的网络协议。它旨在使网络设备能够以极高的精度和准确性进行时间同步,通常用于需要时间同步的应用领域,如金融交易、工业自动化和无线电通信等。PTP的工作原理是通过在网络中的主时钟和从时钟之间进行时间戳的传递和比较来实现精......
  • Visio 2021下载教程|visio流程图软件的完整安装步骤
    Visio是微软公司(Microsoft)推出的一款流程图和图表制作软件。它提供了丰富的图形库和工具,可以帮助用户创建各种类型的图表、流程图、组织结构图、平面布局图等。目前最新版也是用的最广泛的版本为Visio2021。MicrosoftVisio2003-2021全版本软件安装包下载:https://pan.baid......
  • CF1089I Interval-Free Permutations
    标签:析合树析合树就是用来处理这一种值域连续段的问题的。OI-wiki上对于析合树的讲解。我们回顾一下题目,要求不存在长度为\([2,n-1]\)之间的连续段,换句话说,就是根节点下恰有\(n-1\)个节点,且没有任何一个字段是题目中要求的连续段。我们记这样的答案为\(A_n\)也就......
  • Nginx R31 doc-11-Compression and Decompression 压缩与解压缩
    前言大家好,我是老马。很高兴遇到你。我们为java开发者实现了java版本的nginxhttps://github.com/houbb/nginx4j如果你想知道servlet如何处理的,可以参考我的另一个项目:手写从零实现简易版tomcatminicat压缩与解压缩压缩服务器响应,或者对不支持压缩的客户端进行......
  • Distributed Transactions Mit 6.824
    Topic1:distributedtransactions=concurrencycontrol+atomiccommit传统计划:事务程序员标记代码序列的开始/结束作为事务。事务示例x和y是银行余额——数据库表中的记录。x和y位于不同的服务器上(可能在不同的银行)。x和y开始时都是$10。T1和T2是事务。......
  • 解决CLion调试时无法显示变量值的问题
    1问题描述使用CLion的时候,调试时无法显示变量的值,例如:图来自StackOverflow。2解决办法可以尝试切换调试器解决,在Linux下,CLion支持GDB和LLDB,如果GDB不行,可以切换到LLDB。切换方式:File|Settings|Build,Execution,Deployment|Toolchains,将其中的Debugger切换:如果De......
  • UVA11922 Permutation Transformer 题解
    题目传送门前置知识无旋treap解法与luoguP3391【模板】文艺平衡树不同的是本题翻转后需要放到整个序列的末尾。由于需要翻转后放到末尾,故无旋Treap在维护文艺平衡树的过程中合并时跳着合并即可。代码#include<bits/stdc++.h>usingnamespacestd;#definelllong......
  • Netty_Redis_Zookeeper高并发实战-读书笔记
    转载自:https://www.cnblogs.com/leihongzhi/p/17381156.html 第1章    高并发时代的必备技能1.nettyNetty是JBOSS提供的一个Java开源框架,基于NIO的客户端/服务器编程框架,能够快速开发高并发、高可用、高可靠的网络服务器程序,也能开发高可用、高可靠的客户端程序。NIO是......