一文搞懂网络通信的基石✅IO模型与零拷贝

简介: 【10月更文挑战第1天】本文深入探讨了网络通信中的IO模型及其优化方法——零拷贝技术。首先介绍了IO模型的概念及五种常见类型:同步阻塞、同步非阻塞、多路复用、信号驱动和异步IO模型。文章详细分析了每种模型的特点和适用场景,特别是多路复用和异步IO在高并发场景中的优势。接着介绍了零拷贝技术,通过DMA直接进行数据传输,避免了多次CPU拷贝,进一步提升了效率。最后总结了各种模型的优缺点,并提供了相关的代码示例和资源链接。

网络通信的基石:IO模型与零拷贝

中间件作为现代软件架构的基石,扮演着承上启下的关键角色,它不仅衔接了多样化的服务与系统,还极大地促进数据的流动与处理

而这一切高效运作的背后,网络通信是各大中间件中不可或缺的一环

如常见的WEB服务器(tomcat、jetty、undertow),数据库(MySQL、Redis),MQ...

它们都需要进行网络通信,那么如何才能高效的进行网络通信呢?

在聊这个话题前,我们需要先聊聊IO模型

什么是IO模型呢?

IO即输入/输出,IO模型的提出主要是解决计算机CPU在内存与磁盘/网卡等外部设备速度不匹配的问题

当CPU想要读取磁盘/网卡上的数据时,数据拷贝到内存是需要时间的,那这时CPU是去等待数据拷贝完成,还是先去执行其他任务呢?

举个简单的例子:

精通CRUD的小菜快速完成简单的CRUD,但还需要等待其他部门提供的接口,由于其他部门的业务比较复杂,接口文档可能要几天后才能给出,小菜想趁着这段时间休息一会

可是作为小菜上司的我可不乐意了,还有这么多开发任务呢,我再分一个任务给小菜做,让小菜不能空闲下来,等到后续其他部门的接口写好了,再通知小菜完成这个开发任务(提升小菜的“吞吐量”)

这个案例中小菜就是CPU,完成简单的CRUD可以看成在内存上操作(速度快),等待其他部门的接口可以看成等待外部设备把数据拷贝到内存(速度慢)

为了解决这个问题,我提出一种“IO模型”:让小菜先去干别的活,等其他部门的接口好了再通知小菜回来完成这个开发任务

步入正题,常见的IO模型分为五种:同步阻塞IO模型、同步非阻塞IO模型、多路复用IO模型、信号驱动IO模型、异步IO模型

处理流程

为了更好的理解IO模型,先举个体现总体流程的下载文件案例:

下载文件案例中,客户端先向服务端发送请求,而服务器收到请求后,读取磁盘中的文件数据,发送到网卡上再响应给客户端

在这个过程中,以服务端的视角可以看成先从磁盘中读取数据,再往网卡上写数据

由于磁盘、网卡属于外部设备,由于外部设备速度慢,不会使用CPU进行拷贝数据,而是通过DMA进行数据拷贝(这样就不需要占用CPU的资源)

操作系统分为用户态和内核态,其中应用程序处于用户态

由于操作系统中的重要资源不能被用户态的应用程序直接访问,需要先切换到内核态再进行访问

image.png

用传统的阻塞IO(BIO)举例,当服务端接收到请求后:

  1. 读取磁盘要先切换为内核态,然后由DMA进行拷贝(将磁盘上的数据拷贝到内核缓冲区),此时用户线程一直阻塞
  2. DMA拷贝完成后,由CPU将数据从内核缓冲区拷贝到用户态内存上的用户缓冲区,此时用户线程才被唤醒
  3. 状态切换为用户态后,用户线程从用户态中内存的缓冲区将数据拷贝到JVM的堆内存缓冲区中
  4. Java程序处理数据(已经读完),开始写数据,写数据的流程与读数据流程类似,只不过是相反的

图中的直接内存为用户态的内存,而堆内存为JVM的(属于JVM管理)

在这个过程中:用户线程一直阻塞,读数据与写数据存在大量重复拷贝、状态切换,都会导致性能被大大浪费

通过IO模型、零拷贝等优化方式能够优化这个过程,提升响应速度

IO模型

同步阻塞

在同步阻塞IO模型下,当用户线程请求读取外部设备的数据时,会一直阻塞直到数据拷贝完成,再去使用数据

image.png

具体流程如下:

  1. 用户线程发起系统调用请求读取外部设备数据(进入阻塞)
  2. 用户态切换为内核态准备数据:使用DMA将外部设备数据拷贝到内核缓冲区
  3. 内核进行数据拷贝:将内核缓冲区的数据拷贝到用户缓冲区
  4. 内核态切换为用户态,使用数据

阻塞指的是:在用户线程发起系统调用时,并没有立即返回,而是等到数据拷贝完成被唤醒再使用

同步指的是:在数据拷贝阶段,用户线程是阻塞等待数据完成拷贝的(也可以理解为同步是用户线程主动请求内核进行数据拷贝的)

在同步阻塞IO模型下,由于准备、拷贝数据阶段都会阻塞等待,因此性能并不理想

如果高并发的请求打进来,让等比例的线程数量来等待,资源开销是非常大的,因此现代中间件一般不会采取这种模型

同步非阻塞

同步非阻塞IO模型会频繁发起系统调用来判断数据是否已就绪,如果已就绪则同步阻塞进行拷贝

在这个过程中,准备数据阶段是通过轮询非阻塞的方式实现的,当响应数据就绪时,再发起系统调用同步阻塞进行数据拷贝

image.png

同步非阻塞IO模型虽然在数据准备的阶段不需要阻塞,但会通过轮询的方式一直进行系统调用,产生一定的开销

要求网络通信高效的中间件也不会使用这种模型

多路复用

在多路复用模型中,内核线程能够同时监听多个网络请求的通道

使用前,会将数据通道注册到select上,当使用select时会进行阻塞,直到select监听到数据通道上数据已就绪,此时再请求读取数据,使用read系统调用,同步阻塞直到拷贝完数据

image.png

在多路复用模型中实现还分为三种方式:select、poll、epoll

select就是上述举例流程,缺点是最多监听1024个数据通道,并且阻塞到数据就绪时需要遍历处理O(n)

poll在select基础上,动态调整只要内存够理论上无监听通道数量的上限,但数据就绪时还需要遍历处理

epoll使用事件回调的方式,当数据就绪时不需要再轮询,并且内核维护不再需要将数据拷贝到用户态

在多路复用模型中,由于一个内核线程可以监听多个数据通道,这样即使维护大量的网络数据通道,开销也不会太大

而且有epoll事件回调、不用拷贝的优化性能非常好,大部分的中间件都会选择多路复用模型实现网络通信

信号驱动

在信号驱动模型中,会先发送信号的系统调用(立即返回 非阻塞),当数据准备好后通知,再发送读数据的系统调用(阻塞),让内核完成拷贝数据

image.png

信号驱动避免准备数据时的阻塞,并且不需要轮询发起系统调用,但在数据拷贝时依旧需要同步阻塞

异步

在异步IO模型中,发起请求的系统调用时会携带回调函数,发起系统调用后立即返回(非阻塞)

当数据就绪后,不需要用户线程同步触发,而是由内核主动将数据拷贝到用户缓冲区

image.png

在异步IO(AIO)中完全没有阻塞也不再需要同步

在要求高效的高并发网络通信中,一般使用多路复用模型NIO和异步IO(AIO)

JDK中的NIO指的就是多路复用模型,而NIO2指的就是AIO,后续讲解中间件如何高效处理网络通信时都会出现它们的身影~

零拷贝

聊完IO模型后,我们能够知道使用NIO、AIO能够加快处理流程的速度

处理流程中还存在大量的CPU拷贝,在Linux内核逐步升级后,网卡支持的情况下还可以实现零拷贝

零拷贝指的是不再需要使用CPU进行数据拷贝,而是直接通过DMA进行拷贝

image.png

为什么无法从内核直接拷贝到JVM堆内存?

在传统的流程处理中,需要先从内核拷贝到直接内存,再从直接内存拷贝到JVM堆内存

既然直接内存和JVM堆内存都处于用户态的内存,为什么不从内核直接拷贝到JVM堆内存呢?

这是由于JVM堆内存会发生GC,可能改变位置,内核拷贝到堆内存时无法保证不会GC

而本地内存拷贝到JVM堆内存,HotSpot虚拟机保证不在安全点上,因此不会GC

JVM安全点相关知识感兴趣的同学可以查看这篇文章

总结

IO模型的提出是为了解决CPU在内存的速度与外部设备加载到内存速度的差异

在操作系统中为了安全使用系统资源,IO时会涉及到用户态、内核态的切换

IO阶段通常分为准备数据和拷贝数据,准备数据主要由DMA将外部设备数据拷贝到内核缓冲区,拷贝数据是将内核缓冲区拷贝到用户缓冲区

同步阻塞IO模型(BIO)发起系统调用后会阻塞到数据拷贝完成,不适合处理高并发网络通信的场景

同步非阻塞IO模型使用轮询的方式判断数据是否就绪,就绪再同步阻塞等待数据拷贝

信号驱动模型中数据就绪后通过信号通知应用发起系统调用读取数据,避免同步非阻塞下轮询的开销

多路复用IO模型使用select时,监听多个通道,select阻塞直到监听到通道上数据就绪,再通知应用进行读取发起同步阻塞直到数据拷贝结束

使用select,当多个通道数据同时就绪时,只能轮询处理,并且只能监听1024个通道;使用poll进行优化能够监听无上限通道数量

使用epoll 事件回调的方式避免轮询处理,并且内核维护不需要再进行数据拷贝

异步IO模型使用回调的方式避免数据就绪时同步阻塞进行数据拷贝,Linux下也是使用epoll模拟实现

当网卡支持时使用sendfile零拷贝可以避免大量CPU数据拷贝

最后(不要白嫖,一键三连求求拉~)

本篇文章被收入专栏 后端的网络基石,感兴趣的同学可以持续关注喔

本篇文章笔记以及案例被收入 Gitee-CaiCaiJavaGithub-CaiCaiJava,除此之外还有更多Java进阶相关知识,感兴趣的同学可以starred持续关注喔~

有什么问题可以在评论区交流,如果觉得菜菜写的不错,可以点赞、关注、收藏支持一下~

关注菜菜,分享更多技术干货,公众号:菜菜的后端私房菜

相关文章
|
11天前
|
19天前
|
存储 安全 算法
网络安全与信息安全:构建安全数字生活的基石
【10月更文挑战第5天】 在数字化时代,网络安全与信息安全已成为维护个人隐私、企业机密和国家安全的重要防线。本文旨在探讨网络安全漏洞的形成与防范、加密技术的应用及其重要性,以及提升公众安全意识的必要性。通过深入浅出的方式,帮助读者理解网络安全的核心要素,并强调每个人都是网络安全生态中不可或缺的一环。
45 1
|
18天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于BP神经网络的苦瓜生长含水量预测模型matlab仿真
本项目展示了基于BP神经网络的苦瓜生长含水量预测模型,通过温度(T)、风速(v)、模型厚度(h)等输入特征,预测苦瓜的含水量。采用Matlab2022a开发,核心代码附带中文注释及操作视频。模型利用BP神经网络的非线性映射能力,对试验数据进行训练,实现对未知样本含水量变化规律的预测,为干燥过程的理论研究提供支持。
|
18天前
|
网络协议 前端开发 Java
网络协议与IO模型
网络协议与IO模型
网络协议与IO模型
|
17天前
|
机器学习/深度学习 网络架构 计算机视觉
目标检测笔记(一):不同模型的网络架构介绍和代码
这篇文章介绍了ShuffleNetV2网络架构及其代码实现,包括模型结构、代码细节和不同版本的模型。ShuffleNetV2是一个高效的卷积神经网络,适用于深度学习中的目标检测任务。
55 1
目标检测笔记(一):不同模型的网络架构介绍和代码
|
3天前
|
机器学习/深度学习 人工智能 算法
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型
车辆车型识别,使用Python作为主要编程语言,通过收集多种车辆车型图像数据集,然后基于TensorFlow搭建卷积网络算法模型,并对数据集进行训练,最后得到一个识别精度较高的模型文件。再基于Django搭建web网页端操作界面,实现用户上传一张车辆图片识别其类型。
11 0
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型
|
12天前
|
机器学习/深度学习 算法 数据挖掘
【深度学习】经典的深度学习模型-02 ImageNet夺冠之作: 神经网络AlexNet
【深度学习】经典的深度学习模型-02 ImageNet夺冠之作: 神经网络AlexNet
22 2
|
12天前
|
网络协议 安全 数据安全/隐私保护
网络协议:互联网通信的基石
【10月更文挑战第12天】
54 1
|
19天前
|
开发者
什么是面向网络的IO模型?
【10月更文挑战第6天】什么是面向网络的IO模型?
18 3
|
19天前
|
缓存 Java Linux
硬核图解网络IO模型!
硬核图解网络IO模型!

热门文章

最新文章