什么是零拷贝?在java中是怎么实现的?

本文深入探讨了零拷贝技术的概念、实现方式及其在不同场景下的应用。详细讲解了DMA、mmap、sendfile等技术如何减少CPU负载,提高IO效率,并分析了Java NIO在零拷贝方面的实现。

一,零拷贝是什么?

1.是什么?
零拷贝是指cpu不参与数据在内存间的复制。
2.有哪些好处?
减少上下文切换,避免cpu数据拷贝带来的负载。
3.怎么实现?
DMA,mmap+write,sendfile,sendfile+DMA gather copy

二,传统的IO的流程是什么?

读文件:用户进程调用read()->发起系统调用->操作系统运行级别由用户态转化为内核态->CPU将磁盘缓冲区里的数据复制到内核缓冲区->CPU将内核缓冲区中的数据复制到用户缓冲区->操作系统由内核态转化为用户态。
写文件:用户进程调用write()->发起系统调用-》用户态转化为内核态-》cpu将用户缓冲区的数据复制到内核缓冲区-》cpu将内核缓冲区中的数据写到本地-》由内核态转化为用户态。

三,DMA 直接内存存取

DMA拷贝:由CPU向DMA磁盘控制器下达指令,由DMA将磁盘缓冲区的数据发送到内核缓冲区,发送完毕将信息反馈给CPU;
使用DMA拷贝的传统IO可以避免两次CPU拷贝。

四,mmap+write

mmap是linux提供的一种内存映射方法,可以将内核缓冲区地址与用户缓冲区地址进行映射,实现内核缓冲区和用户缓冲区的数据共享,从而避免一次CPU拷贝。
不过如果write,即从内核读缓冲区,向内核写缓冲区的copy仍需要CPU参与。
读+写整个流程是:
1.用户通过mmap发起系统调用-》上下文从用户态转化为内核态-》将内核的缓存区与用户缓存区进行映射-》CPU利用DMA将数据从硬盘缓冲区写入到内核缓存区-》上下文从内核态转化为用户态,mmap系统调用执行返回
2.用户使用write来发起系统调用-》上下文从用户态转化为内核态-》CPU将数据从内核读缓冲区拷贝到网络缓冲区-》CPU利用DMA将数据发到网卡-》上下文到换到用户态,write系统调用返回。
优点:mmap将内存copy从两次减少到一次。
不足:不过仍存在小文件浪费空间的问题,因为内存映射要对齐页边界。

五,sendfile

减少两次上下文切换,数据对用户不可见。
用户进程通过调用sendfile()发起系统调用-》上下文切换-》DMA传输数据到内核读缓冲区-》CPUcopy到网络缓冲区->DMA将数据发到网卡-》上下文切换
问题。
优点:减少上下文切换,仍有一次CPU拷贝
不足:用户不能修改数据,单纯完成传输

六,sendfile+DMA gather copy

引入DMA gather操作,将内核空间的读缓冲区的数据描述信息,如内存地址,偏移量,记录到内核网络缓冲区。DMA通过内存地址偏移量,批量地从内核读缓冲区拷贝数据到网卡,省略了仅有的一次CPU拷贝。
最终发生了两次上下文切换+0次CPU拷贝+2次DMA拷贝。
不足:1.需要硬件支持,2.不能操作数据,3.只适用从文件拷贝到socket

JAVA中NIO实现

一, MappedByteBuffer

MappedByteBuffer 是NIO基于内存映射实现零拷贝的方法。
Channel相当于内核缓冲区,Buffer相当于用户缓冲区。
FileChannel 的map方法可以把一个文件映射为内存文件。

二,FileChannel
FileChannel定义了transferFrom(),transferTo().
transferTo()为例,优先使用sendfile+DMA gather 实现零拷贝
其次是使用map映射,如果还不满足则改用传统IO

各种典型应用采用的什么方案?

Netty使用的是FileChannel的transferTo()方法,通过 DefaultFileRegion 类进行包装。
RocketMQ使用的是mmap()
Kafka使用sendfile 可以利用DMA,消耗CPU少,效率高。小文件传输只是用BIO,效率低于mmap();

零拷贝(Zero-Copy)技术是一种优化数据传输效率的机制,其核心目标是减少数据在内存中的复制次数,特别是在数据从磁盘或网络传输到用户空间的过程中,避免不必要的数据拷贝和上下文切换。这种技术通过减少 CPU 的参与次数和内存带宽的占用,显著提高了 I/O 操作的性能[^1]。 在传统的数据传输方式中,例如从磁盘读取文件并通过网络发送,通常涉及多次数据复制和上下文切换。以 Linux 系统为例,传统 I/O 流程包括以下步骤: 1. 用户进程调用 `read()` 从磁盘读取文件,触发从用户态切换到内核态。 2. 数据从磁盘加载到内核缓冲区。 3. 数据从内核缓冲区复制到用户缓冲区。 4. 用户进程调用 `write()` 将数据写入网络套接字,再次切换到内核态。 5. 数据从用户缓冲区复制回内核缓冲区,再通过网络接口发送。 整个过程涉及四次上下文切换和四次数据拷贝,其中两次由 CPU 参与的数据复制(用户空间 ↔ 内核空间),两次由 DMA 控制器完成(磁盘 ↔ 内核缓冲区,内核缓冲区 ↔ 网络设备)[^4]。 零拷贝技术通过多种实现方式优化这一过程,主要思路包括: - **用户态直接 I/O**:允许应用程序直接访问硬件存储,跳过内核缓冲区,减少用户空间与内核空间之间的数据复制。 - **减少数据拷贝次数**:通过系统调用如 `sendfile()`、`splice()` 等,在内核空间内部完成数据传输,避免将数据复制到用户空间。 - **写时复制(Copy-on-Write)**:多个进程共享同一块数据时,仅在需要修改时才进行数据复制,读取时无需复制,从而节省内存资源和 CPU 开销[^3]。 这些机制使得零拷贝技术在高性能网络服务器、大数据传输、视频流服务等场景中得到了广泛应用。例如在 Java 中,可以通过 `FileChannel` 的 `transferTo()` 和 `transferFrom()` 方法实现零拷贝的数据传输[^2]。 ### 示例代码:Java 中使用零拷贝进行文件传输 ```java import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.channels.FileChannel; public class ZeroCopyExample { public static void main(String[] args) throws Exception { try (FileChannel source = new FileInputStream("source.bin").getChannel(); FileChannel destination = new FileOutputStream("destination.bin").getChannel()) { long position = 0; long size = source.size(); while (position < size) { // 使用 transferTo 实现零拷贝 position += source.transferTo(position, size - position, destination); } } } } ``` 该示例展示了如何利用 Java NIO 的 `FileChannel` 实现高效的文件拷贝,避免了用户空间与内核空间之间的数据复制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值