红页网站如何做,做网站建设与推广企业,廊坊网站建设策划,广州智能模板建站写在前面
本文看下什么是零拷贝#xff0c;以及其具体的应用场景有哪些。
1#xff1a;什么是零拷贝
想要解释清楚什么是零拷贝#xff0c;需要先来看下常规的阻塞io一次io的过程#xff0c;这里以从文件读取内容然后写到socket为例来看下#xff0c;如下#xff1a; …写在前面
本文看下什么是零拷贝以及其具体的应用场景有哪些。
1什么是零拷贝
想要解释清楚什么是零拷贝需要先来看下常规的阻塞io一次io的过程这里以从文件读取内容然后写到socket为例来看下如下
1发起read调用发生一次上下文切换从用户态转换为内核态
2内核拷贝数据到pagecahe
3发生一次上下文切换内核态转换为用户态用户进程将数据拷贝到用户缓冲区
4发生一次上下文切换用户态转换为内核态内核将数据拷贝socket缓冲区
5内核将数据拷贝到网卡pagecache是磁盘数据的缓冲区用来在一定程度上缓解磁盘速度和内存速度的差异起到预读缓存作用。 可以参考下下图
这里可能的性能瓶颈如下
1上下文切换
2数据拷贝所以我们如果是能够尽量上下文切换的次数以及数据拷贝的次数就能对性能有比较好的提升了。首先数据拷贝到用户缓冲区这一步其实是完全没有必要的因为应用程序只是捣一手而已所以如果是可以少了这个步骤那么数据拷贝到用户缓冲区和其之前的上下文切换以及之后的从用户缓冲区拷贝数据到socket缓冲区以及对应的上下文切换就可以避免了也就是如下的部分
此时就要需要将数据从pagecache拷贝到socket缓冲区因此还需要引入额外的一次数据拷贝但以少两次上下文切换两次数据拷贝为收益还是比较值得的。整个过程就变为下图 黄色框就是新机制额外引入的一次数据拷贝了。 其实还可以继续优化如果是应用程序直接告知数据要写到那个socket这个当然很容易做到那么就可以直接将数据从pagecache拷贝到网卡那么新引入的这次拷贝也可以被干掉并且从socket缓冲区拷贝到网卡这步也可以被干掉了就变为下图这样的过程 这其实就是零拷贝了所以很难给零拷贝下一个准确的定义。但我觉得可以这样来描述通过技术手段尽量的减少上下文切换和拷贝次数的io方式叫做零拷贝。
2零拷贝使用的场景
在前面的分析中零拷贝需要依赖于pagecache而这也决定了零拷贝使用的场景所以我们首先要来看下pagecache的作用是什么。pagecache最大的作用是预读什么意思呢假定你要读取15k的内容但是内核会假定你很快读取接下来的15k内容那么就会直接读取30k的内容这样接下来的15k内容就不需要读磁盘了还有一点就是“时间局部性”原理即刚被读过的数据被再次读取的概率很高所以此时pagecache起到了缓存数据的作用。而pagecache的大小是很有限的所以大文件的读取pagecache是无法发挥它的威力的甚至会拖后腿所以零拷贝的应用场景是小文件的读取。 如果是读取大文件会怎么样呢就会导致pagecache长时间被占满并且无法发挥其作用导致其他小文件的读取也无法享受到pagecache的好处。 所以如果你的场景中是小文件的读取或者是小文件频繁的读取可以优先考虑使用零拷贝。
3jdk对零拷贝的支持
在javaNIO中提供了对零拷贝的支持依赖于方法java.nio.channels.FileChannel.transferTo
public abstract long transferTo(long position, long count,WritableByteChannel target)throws IOException;使用实例
package org.example;//import lombok.extern.slf4j.Slf4j;
//
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;public class TestByteBufferTransferTO {public static void main(String[] args) {long startTime System.currentTimeMillis();try (FileChannel from new FileInputStream(d:\\test\\CentOS-7-x86_64-Minimal-2207-02.iso).getChannel();FileChannel to new FileOutputStream(centos System.currentTimeMillis() .iso).getChannel()) {long size from.size();for (long left size; left 0; ) {
// log.info(position:{},left:{}, size - left, left);System.out.println(position:{},left:{} (size - left) left);left - from.transferTo((size - left), left, to);}} catch (IOException e) {
// log.debug(e:{}, e);} finally {// 零拷贝耗时18679System.out.println(零拷贝耗时 (System.currentTimeMillis() - startTime));}}
}运行
4netty对零拷贝的支持
直接包装了jdk的零拷贝如下
写在后面
参考文章列表
04 | 零拷贝如何高效地传输文件。
零拷贝原理的文章网上满天飞但你知道如何使用零拷贝吗。