框架篇:Linux零拷贝机制和FileChannel (2)

通过mmap实现的零拷贝I/O进行了4次用户空间与内核空间的上下文切换,以及3次数据拷贝;其中3次数据拷贝中包括了2次DMA拷贝和1次CPU拷贝

sendfile实现的零拷贝 #include <sys/sendfile.h> ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

框架篇:Linux零拷贝机制和FileChannel

1)发出sendfile系统调用,导致用户空间到内核空间的上下文切换,然后通过DMA引擎将磁盘文件中的内容复制到内核空间缓冲区中,接着再将数据从内核空间缓冲区复制到socket相关的缓冲区

2)sendfile系统调用返回,导致内核空间到用户空间的上下文切换。DMA异步将内核空间socket缓冲区中的数据传递到网卡

通过sendfile实现的零拷贝I/O使用了2次用户空间与内核空间的上下文切换,以及3次数据的拷贝。其中3次数据拷贝中包括了2次DMA拷贝和1次CPU拷贝

带有DMA收集拷贝功能的sendfile实现的零拷贝

从Linux 2.4版本开始,操作系统提供scatter和gather的SG-DMA方式,直接从内核空间缓冲区中将数据读取到网卡,无需将内核空间缓冲区的数据再复制一份到socket缓冲区

框架篇:Linux零拷贝机制和FileChannel

1)发出sendfile系统调用,导致用户空间到内核空间的上下文切换。通过DMA引擎将磁盘文件中的内容复制到内核空间缓冲区

2)这里没把数据复制到socket缓冲区;取而代之的是,相应的描述符信息被复制到socket缓冲区。该描述符包含了两种的信息:A)内核缓冲区的内存地址、B)内核缓冲区的偏移量

3)sendfile系统调用返回,导致内核空间到用户空间的上下文切换。DMA根据socket缓冲区的描述符提供的地址和偏移量直接将内核缓冲区中的数据复制到网卡

带有DMA收集拷贝功能的sendfile实现的I/O使用了2次用户空间与内核空间的上下文切换,以及2次数据的拷贝,而且这2次的数据拷贝都是非CPU拷贝。这样一来我们就实现了最理想的零拷贝I/O传输了,不需要任何一次的CPU拷贝,以及最少的上下文切换

java提供的零拷贝方式

java NIO的零拷贝实现是基于mmap+write方式

FileChannel的map方法产生的MappedByteBuffer
FileChannel提供了map()方法,该方法可以在一个打开的文件和MappedByteBuffer之间建立一个虚拟内存映射,MappedByteBuffer继承于ByteBuffer;该缓冲器的内存是一个文件的内存映射区域。map方法底层是通过mmap实现的,因此将文件内存从磁盘读取到内核缓冲区后,用户空间和内核空间共享该缓冲区。用法如下

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

FileChannel的transferTo、transferFrom
如果操作系统底层支持的话,transferTo、transferFrom也会使用相关的零拷贝技术来实现数据的传输。用法如下

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()); } } 欢迎指正文中错误 关注公众号,一起交流

框架篇:Linux零拷贝机制和FileChannel

参考文章

浅谈 Linux下的零拷贝机制

面试被问到“零拷贝”!你真的理解吗?

java NIO 的通道Channel的理解

Channel基本使用——FileChannel类和内存映射的使用

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyyppf.html