湖北黄石网站建设,wordpress赚钱方法,网站后台上传用户界面不显示,杭州专业网站排名优化当涉及到网络编程和IO操作时#xff0c;数据拷贝是一个常见的性能瓶颈。传统的数据拷贝过程中#xff0c;数据需要从内核缓冲区复制到用户空间缓冲区#xff0c;然后再从用户空间缓冲区复制到内核缓冲区#xff0c;这个过程会耗费大量的CPU时间和内存带宽#xff0c;降低系…当涉及到网络编程和IO操作时数据拷贝是一个常见的性能瓶颈。传统的数据拷贝过程中数据需要从内核缓冲区复制到用户空间缓冲区然后再从用户空间缓冲区复制到内核缓冲区这个过程会耗费大量的CPU时间和内存带宽降低系统的性能和吞吐量。
为了解决这个问题零拷贝技术应运而生。零拷贝技术是指在数据传输过程中避免将数据从一块内存拷贝到另一块内存从而减少了CPU的开销和内存带宽的消耗提高了系统的性能。
在Java后端开发中使用零拷贝技术可以有效提升系统的性能和吞吐量。本文将介绍零拷贝技术的概念、实现原理以及在Java后端开发中的应用希望能够为读者提供有价值的参考和帮助。
一、传统I/O
在展开说零拷贝之前我们先来回顾一下传统IO的方式是怎么样的。
早期的数据IO由用户进程向CPU发起应用程序与磁盘之间的 I/O 操作都是通过 CPU 的中断完成的。CPU还要负责将磁盘缓冲区拷贝到内核缓冲区(pageCache)再从内核缓冲区拷贝到用户缓冲区。为了减少CPU占用产生了DMA技术大大解放了CPU。
DMA 的全称叫直接内存存取Direct Memory Access是一种允许外围设备硬件子系统直接访问系统主内存的机制。目前大多数的硬件设备包括磁盘控制器、网卡、显卡以及声卡等都支持 DMA 技术。
1.1传统I/O的问题
我们以读取一张图片数据的过程为例来分析传统IO有哪些问题。传统的访问方式是通过 write() 和 read() 两个系统调用实现的通过 read() 函数读取图片到到缓存区中然后通过 write() 方法把缓存中的图片输出到网络端口。
read操作
当应用程序执行 read 系统调用读取一块数据的时候如果这块数据已经存在于用户进程的页内存中就直接从内存中读取数据。
如果数据不存在则先将数据从磁盘加载数据到内核空间的读缓存read buffer中再从读缓存拷贝到用户进程的页内存中。
write操作
当应用程序准备好数据执行 write 系统调用发送网络数据时先将数据从用户空间的页缓存拷贝到内核空间的网络缓冲区socket buffer中然后再将写缓存中的数据拷贝到网卡设备完成数据发送。 从上图中可以看出整个IO的过程需要进行两次DMA拷贝两次CPU拷贝四次上下文切换。总共四次拷贝四次切换。这个代价确实有些大。
说完传统IO接下来我们看下零拷贝都做了哪些优化。
二、零拷贝
2.1什么是零拷贝
零拷贝这个词在很多地方都出现过比如Kafka、Nginx、Tomcat等等这些技术的底层都有用到零拷贝技术那么究竟什么是零拷贝呢
零拷贝是指在数据传输过程中避免了数据的多次拷贝从而提高了数据传输的效率。在传统的IO模型中数据从磁盘中读取到内核缓冲区然后再从内核缓冲区拷贝到用户缓冲区最后再从用户缓冲区拷贝到应用程序中。而在零拷贝模型中数据可以直接从内核缓冲区拷贝到应用程序中避免了数据的多次拷贝提高了数据传输的效率。零拷贝技术可以通过mmap和sendfile等系统调用实现。
所以说零拷贝并不是说不拷贝而是减少拷贝的次数因为从磁盘中拷贝数据到内存或者从内存中的一块儿区域拷贝到另一块儿区域都是一个耗费性能的操作。零拷贝技术的目的就是减少这种行为的发生次数以此来提高性能。
2.2零拷贝实现的几种方式
对比开头说到的传统IO我们可以在以下几个方面进行优化
1. 用户态可以直接操作读写不需要在用户态和内核态之间反复横跳。
2. 尽量减少拷贝次数尽量减少上下文切换次数。
3. 写时复制需要写操作的时候再拷贝只是读操作没必要拷贝
用户态直接IO
用户态直接 I/O 使得应用进程或运行在用户态user space下的库函数直接访问硬件设备。
用户态直接 I/O 只能适用于不需要内核缓冲区处理的应用程序这些应用程序通常在进程地址空间有自己的数据缓存机制称为自缓存应用程序如数据库管理系统 就是一个代表。
其次这种零拷贝机制会直接操作磁盘 I/O由于 CPU 和磁盘 I/O 之间的执行时间差距会造成大量资源的浪费解决方案是配合异步 I/O 使用。
写时复制
写时复制指的是当多个进程共享同一块数据时如果其中一个进程需要对这份数据进行修改那么就需要将其拷贝到自己的进程地址空间中。
这样做并不影响其他进程对这块数据的操作每个进程要修改的时候才会进行拷贝所以叫写时拷贝。
减少拷贝次数
1. mmapwrite零拷贝技术
以mmapwrite的方式替代传统的readwrite的方式减少了一次拷贝。
mmap 是 Linux 提供的一种内存映射文件方法即将一个进程的地址空间中的一段虚拟地址映射到磁盘文件地址使用 mmap 的目的是将内核中读缓冲区read buffer的地址与用户空间的缓冲区user buffer进行映射。从而实现内核缓冲区与应用程序内存的共享省去了将数据从内核读缓冲区read buffer拷贝到用户缓冲区user buffer的过程。 整个拷贝过程会发生 4 次上下文切换1 次 CPU 拷贝和 2 次 DMA 拷贝。mmap 主要的用处是提高 I/O 性能特别是针对大文件。对于小文件内存映射文件反而会导致碎片空间的浪费。
2. Sendfile零拷贝技术
通过 Sendfile 系统调用数据可以直接在内核空间内部进行 I/O 传输从而省去了数据在用户空间和内核空间之间的来回拷贝。 将要读取的文件缓冲区的文件 fd 和要发送的Socket缓冲区的Socket fd 传给sendfile函数Sendfile 调用中 I/O 数据对用户空间是完全不可见的。也就是说这是一次完全意义上的数据传输过程。也就是说用户程序不能对数据进行修改而只是单纯地完成了一次数据传输过程。整个拷贝过程会发生 2 次上下文切换1 次 CPU 拷贝和 2 次 DMA 拷贝。
3. SendfileDMA gather copy
它只适用于将数据从文件拷贝到 socket 套接字上的传输过程。
它将内核空间的读缓冲区read buffer中对应的数据描述信息内存地址、地址偏移量记录到相应的网络缓冲区 socket buffer中由 DMA 根据内存地址、地址偏移量将数据批量地从读缓冲区read buffer拷贝到网卡设备中。
这样 DMA 引擎直接利用 gather 操作将页缓存中数据打包发送到网络中即可本质就是和虚拟内存映射的思路类似。 整个拷贝过程会发生 2 次上下文切换、0 次 CPU 拷贝以及 2 次 DMA 拷贝。
4.Splice零拷贝技术
Splice相当于在SendfileDMA gather copy上的提升Splice 系统调用可以在内核空间的读缓冲区read buffer和网络缓冲区socket buffer之间建立管道pipeline从而避免了两者之间的 CPU 拷贝操作。 基于 Splice 系统调用的零拷贝方式整个拷贝过程会发生 2 次上下文切换0 次 CPU 拷贝以及 2 次 DMA 拷贝。
2.3总结
无论是传统I/O拷贝方式还是引入了零拷贝的方式2次DMA Copy都是必要的步骤因为两次DMA都是依赖硬件完成的。 三、零拷贝的实际应用
3.1JavaNIO基于零拷贝的实现
Java-NIO主要有三个方面用到了零拷贝技术
MappedByteBuffer.map()底层调用了操作系统的mmap()内核函数。
DirectByteBuffer.allocateDirect()可以直接创建基于本地内存的缓冲区。
FileChannel.transferFrom()/transferTo()底层调用了sendfile()内核函数。
3.2主流技术中零拷贝的应用
1. Netty中零拷贝的应用
Netty中的零拷贝是一种用户进程级别的零拷贝体现主要也包含三方面
1 Netty的发送、接收数据的ByteBuf缓冲区默认会使用堆外本地内存创建采用直接内存进行Socket读写数据传输时无需经过二次拷贝。如果使用传统的堆内存进行Socket网络数据读写JVM需要先将堆内存中的数据拷贝一份到直接内存然后才写入Socket缓冲区中相较于堆外直接内存消息在发送过程中多了一次缓冲区的内存拷贝。
2Netty的文件传输采用了transferTo()/transferFrom()方法它可以直接将文件缓冲区的数据发送到目标ChannelSocket底层就是调用了sendfile()内核函数避免了文件数据的CPU拷贝过程。
3Netty提供了组合、拆解ByteBuf对象的API咱们可以基于一个ByteBuf对象对数据进行拆解也可以基于多个ByteBuf对象进行数据合并这个过程中不会出现数据拷贝这个是程序级别的零拷贝实际上就是在原数据的基础上用不同的引用表示而已。
2. 其他技术中的零拷贝技术应用
Kafka底层基于java.nio包下的FileChannel.transferTo()实现零拷贝。Kafka Server基于FileChannel将文件中的消息数据发送到SocketChannel。
RocketMQ基于mmap write的方式实现零拷贝。内部实现基于nio提供的java.nio.MappedByteBuffer基于FileChannel的map方法得到mmap的缓冲区。