大白话解释,零拷贝就是没有把数据从一个存储区域拷贝到另一个存储区域。但是没有数据的复制,怎么可能实现数据的传输呢?其实我们在java NIO、netty、kafka遇到的零拷贝,并不是不复制数据,而是减少不必要的数据拷贝次数,从而提升代码性能
缓冲区 是所有I/O的基础,I/O 无非就是把数据移进或移出缓冲区
进程发起read请求,内核先检查内核空间缓冲区是否存在进程所需数据,如果已经存在,则直接copy数据到进程的内存区。如果没有,系统则向磁盘请求数据,通过DMA写入内核的read缓冲冲区,接着再将内核缓冲区数据copy到进程的内存区
进程发起write请求,则是把进程的内存区数据copy到内核的write缓冲区,然后再通过DMA把内核缓冲区数据刷回磁盘或者网卡中
虚拟内存:现代操作系统都使用虚拟内存,有如下两个好处
一个以上的虚拟地址可以指向同一个物理内存地址
虚拟内存空间可大于实际可用的物理地址
利用第一点特性可以把内核空间地址和用户空间的虚拟地址映射到同一个物理地址,这样DMA就可以填充(读写)对内核和用户空间进程同时可见的缓冲区了;大致如下
#include <unistd>
ssize_t write(int filedes, void *buf, size_t nbytes);
ssize_t read(int filedes, void *buf, size_t nbytes);
「一共有四次用户空间与内核空间的上下文切换。四次数据copy,分别是两次CPU数据复制,两次DMA数据复制」
#include <sys/mman.h>
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
「通过mmap实现的零拷贝I/O进行了4次用户空间与内核空间的上下文切换,以及3次数据拷贝;其中3次数据拷贝中包括了2次DMA拷贝和1次CPU拷贝」
#include <sys/sendfile.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
「通过sendfile实现的零拷贝I/O使用了2次用户空间与内核空间的上下文切换,以及3次数据的拷贝。其中3次数据拷贝中包括了2次DMA拷贝和1次CPU拷贝」
「带有DMA收集拷贝功能的sendfile实现的I/O使用了2次用户空间与内核空间的上下文切换,以及2次数据的拷贝,而且这2次的数据拷贝都是非CPU拷贝。这样一来我们就实现了最理想的零拷贝I/O传输了,不需要任何一次的CPU拷贝,以及最少的上下文切换」
public void main(String[] args){
try {
FileChannel readChannel = FileChannel.open(Paths.get("./cscw.txt"), StandardOpenOption.READ);
FileChannel writeChannel = FileChannel.open(Paths.get("./siting.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
MappedByteBuffer data = readChannel.map(FileChannel.MapMode.READ_ONLY, 0, 1024 * 1024 * 40);
//数据传输
writeChannel.write(data);
readChannel.close();
writeChannel.close();
}catch (Exception e){
System.out.println(e.getMessage());
}
}
public void main(String[] args) {
try {
FileChannel readChannel = FileChannel.open(Paths.get("./cscw.txt"), StandardOpenOption.READ);
FileChannel writeChannel = FileChannel.open(Paths.get("./siting.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
long len = readChannel.size();
long position = readChannel.position();
//数据传输
readChannel.transferTo(position, len, writeChannel);
//效果和transferTo 一样的
//writeChannel.transferFrom(readChannel, position, len, );
readChannel.close();
writeChannel.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
[1]浅谈 Linux下的零拷贝机制: https://www.jianshu.com/p/e76e3580e356
[2]面试被问到“零拷贝”!你真的理解吗?: https://my.oschina.net/u/3990817/blog/3045359
[3]java NIO 的通道Channel的理解: https://blog.csdn.net/qq_27092581/article/details/78347198
[4]Channel基本使用——FileChannel类和内存映射的使用: https://blog.csdn.net/qq_45337431/article/details/99645809
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8