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

一,零拷贝是什么?

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

你可能感兴趣的:(NIO,java)