深入分析通过Socket进行数据文件传递中的传统IO的弊端以及NIO的零拷贝实现原理,及用户空间和内核空间的切换方式

传统的IO流程

在这个过程中:

  1. 数据从磁盘拷贝进内核空间缓冲区
  2. 从内核空间缓冲区拷贝到用户空间缓冲区
  3. 从用户空间缓冲区拷贝回内核空间缓冲区
  4. 在从内核空间缓冲区拷贝到socket的缓冲区
  5. 由Socket缓存区传递给数据发送引擎发送

第三步的必要性:

IO操作涉及到本地方法,java担心,当使用native本地方法对堆内数组进行操作时发生GC, 因为堆内内存是受JVM影响的,一旦发生了垃圾回收机制就使得全部数据都是错乱的,而堆外内存是不受JVM控制的.

就这样, 前前后后一共发生了4次数据的拷贝,用户空间模式和内核空间模式来回切换了4次, 其中用户空间参与的第二次和第三次拷贝并没有对数据进行任何改动,它仅仅是起到了中转的作用; 这恰恰是传统的IO的局限性

NIO的零拷贝

在NIO的数据传递模型中可以看到,用户明显少了用户空间缓冲区缓存数据的步骤, 减少了两次不必要的数据的拷贝,以及不必要的上下文切换, 具体如下:

  1. 数据从磁盘写入内核空间缓冲区
  2. 再从内核空间缓冲区写入到Socket缓冲区
  3. 由Socket缓存区传递给数据发送引擎发送

然而这个模型中仍然有问题存在,在内核空间缓冲区中仍然存在数据的拷贝

  • 数据从内核空间缓冲区拷贝进了Socket缓冲区

这种现状也是有办法解决的

在2.X版本的linux中,NIO的零拷贝模型如下:

这个模型中充分利用了Scatter/Gather 分散和汇聚的特性

这张图是最完美的零拷贝模型,

  1. 首先文件从磁盘中加载进内核空间缓冲区
  2. CPU将内核空间缓冲区存储的数据的adress以及数据的大小存放进Socket
  3. 协议引擎根据socket提供的数据的描述,直接去内核缓冲区取出数据

第2步 一个完整的可用的buffer被分散在两个buffer中, 可以理解成是一个分散的过程 Scatter

第3步 操作系统去收集buffer,可以理解成一个Gather的过程

从而实现了真正的零拷贝

回到Java

除了上面的第一张图片以外,其他图片中数据全部在内核缓冲区,这部分空间对于人来说其实是一个黑盒,于是java提供了封装类帮我们和这块黑盒打交道

mappedByteBuffer



这是他的继承体系,和HeadByteBuffer位于同一级,我们称它为内存映射文件 他是通道的调用map()方法得来的, 这个mappedByteBuffer相对于普通的buffer而言,他并没有板板整整的维护自己的数组,相反直接关联着堆外内存,针对它的任何修改,操作系统都会自动的同步到文件中

如下修改内存buffer,却更新了文件

RandomAccessFile randomAccessFile = new RandomAccessFile("123.txt", "rw");  //class sun.nio.ch.FileChannelImpl
FileChannel channel = randomAccessFile.getChannel();
System.out.println(channel.getClass());
MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
// todo 接下来我们直接修改内存中的内容就行了,不需要修改文件
mappedByteBuffer.put(0, (byte) 'a');
mappedByteBuffer.put(3, (byte) 'b');
randomAccessFile.close();
channel.close();

关于FileChannel.MapMode文件通道的映射模型 是个枚举:

  • PRIVATE
  • READ_WRITE
  • READ_ONLY

    当我们想构建read_write类型的只能使用 RandomAccessFile类型的文件stream, 通过它的rw参数,设置为可读写的类型

关于ByteBufferByteBuffer.allocateDirect()

public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
} class DirectByteBuffer extends MappedByteBuffer implements DirectBuffer
{
...
}

最常用的ByteBuffer的allocateDirect()底层使用同样是MappedBytebuffer的实现类,DirectByteBuffer,这个对象相对于HeapByteBuffer来说,他并没有初始化父类ByteBuffer中的数组,但是它使用了超类BUffer中的Long类型的adress关键字

adress关键字的作用是 存放了一个堆外的地址,这个地址标记着一个堆外数组的位置,使得java可以使用unsafe类下的本地方法,操作adress标记的堆外内存,这样就省去了在第一张图片中的还要把堆内数组拷贝到堆外再进行读写的弊端,实现了零拷贝

scattering 和 gathering在NIO编程中的体现

scattering是一个分散的过程,即把一整块数据分散在不同的buffer中,而gathering与之相反,是一个聚集的过程,只有搜集全所有的全部的buffer得到的数据才是有意义的

例子: 自定义网络协议 将请求头分装成多个缓存buffer中,实现了天然的解析

ByteBuffer[] byteBuffers = new ByteBuffer[3];
byteBuffers[0] = ByteBuffer.allocate(2);
byteBuffers[1] = ByteBuffer.allocate(3);
byteBuffers[2] = ByteBuffer.allocate(4); SocketChannel client = serverSocketChannel.accept();
long read = client.read(byteBuffers);

NIO零拷贝的深入分析的更多相关文章

  1. NIO学习笔记,从Linux IO演化模型到Netty—— Java NIO零拷贝

    同样只是大致上的认识. 其中,当使用transferFrom,transferTo的时候用的sendfile(). 如果系统内核不支持 sendfile,进一步执行 transferToTrusted ...

  2. Java零拷贝

    1.摘要 零拷贝的“零”是指用户态和内核态间copy数据的次数为零. 传统的数据copy(文件到文件.client到server等)涉及到四次用户态内核态切换.四次copy.四次copy中,两次在用户 ...

  3. 深入剖析Linux IO原理和几种零拷贝机制的实现

    深入剖析Linux IO原理和几种零拷贝机制的实现 来源 https://zhuanlan.zhihu.com/p/83398714 零壹技术栈      公众号[零壹技术栈] 前言 零拷贝(Zero ...

  4. 【Netty技术专题】「原理分析系列」Netty强大特性之ByteBuf零拷贝技术原理分析

    零拷贝Zero-Copy 我们先来看下它的定义: "Zero-copy" describes computer operations in which the CPU does n ...

  5. Netty 零拷贝(一)NIO 对零拷贝的支持

    Netty 零拷贝(二)NIO 对零拷贝的支持 Netty 系列目录 (https://www.cnblogs.com/binarylei/p/10117436.html) 非直接缓冲区(HeapBy ...

  6. 零拷贝详解 Java NIO学习笔记四(零拷贝详解)

    转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...

  7. NIO学习笔记,从Linux IO演化模型到Netty—— Linux零拷贝

    这里只是感性地认识Linux零拷贝,不涉及具体细节. 1.Linux传统的数据拷贝 用户进程是不能直接访问文件系统的,要先切换到内核态,发起系统调用,DMA把磁盘中的数据写入内核空间,内核再把数据拷贝 ...

  8. NIO 与 零拷贝

    零拷贝介绍 零拷贝是网络编程的关键, 很多性能优化都需要零拷贝. 在 Java程序中, 常用的零拷贝方式有m(memory)map[内存映射] 和 sendFile.它们在OS中又是怎样的设计? NI ...

  9. Linux、JDK、Netty中的NIO与零拷贝

    一.先理解内核空间与用户空间 Linux 按照特权等级,把进程的运行空间分为内核空间和用户空间,分别对应着下图中, CPU 特权等级分为4个,Linux 使用 Ring 0 和 Ring 3. 内核空 ...

随机推荐

  1. 带你使用Visual Studio 2019创建一个MVC Web应用

    工欲善其事必先利其器,我们既然有Visual Studio2019这样的IDE为什么不用?学.Net Core而不用Visual Studio进行开发可谓是多么另类呀!既然你已经安装了VS2019的话 ...

  2. 关于eclipse码代码时光标自动消失要重新点击输入框的问题

    前几天码代码时在两个电脑都出现了同样的问题,就是在输入的时候,输入法突然从程序框切换到某不可名状的位置,要重新点击输入框才能解决.(后发现不但是eclipse,任何带有输入框的都会出现此问题) 经排查 ...

  3. 发布一个基于协程和事件循环的c++网络库

    目录 介绍 使用 性能 实现 日志库 协程 协程调度 定时器 Hook RPC实现 项目地址:https://github.com/gatsbyd/melon 介绍 开发服务端程序的一个基本任务是处理 ...

  4. 为什么使用HTTP2?

    最近我们公司的官网由原来的http1.1已升级到http2,而我们前端开发对http2还是一片懵懂,更不知道为何换成了这个,故此补充了下http2的相关知识. http1.1相比于http1.0有哪些 ...

  5. 原创 Hive left join 技巧总结

    根据工作中经验总结出来   left  join  常用的  使用注意点:    A     Left    join   B    on   A.id =  B.id 第一种情况: 如果 A 表  ...

  6. IE浏览器下载文件保存时提示:“你没有权限在此位置中保存文件”解决办法

    E浏览器下载文件保存时提示 解决办法: 1.Win + R,打开运行命令,输入gpedit.msc,如图所示 2.打开计算机本地组策略编辑器:选择计算机配置-windows设置-安全设置-本地策略-安 ...

  7. sudo 1.2.27 - Security Bypass

    EXP: https://www.exploit-db.com/exploits/47502?utm_source=dlvr.it&utm_medium=twitter 漏洞复现: 具体配置参 ...

  8. PMBOK 指南 第二章 项目运行环境

    2.1概述 事业环境因素(EEF)源于项目外部(往往企业外部) 组织过程资产(OPA)源于企业内部 2.2 事业环境因素 项目团队不能控制 2.2.1 组织内部的事业环境因素 组织文化.结构和治理 设 ...

  9. 修改Android源码实现原生应用双开,应用多开

    1. 准备 把某系统双开的两个app的信息进行对比 1.1 目录的对比 1.1.1 data目录对比 原应用: /data/user/0/com.luoyesiqiu.crackme/files 被复 ...

  10. Docker异常:/lib/x86_64-linux-gnu/libnss_files.so.2: symbol __libc_readline_unlocked, version GLIBC_PRIVATE not defined in file libc.so.6 with link time reference

    当使用docker cp 将容器内数据拷贝至主机时,或是使用docker export 命令归档容器文件时,出现下述异常: Error response from daemon: error proc ...