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

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一,零拷贝是什么?

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值