深入分析通过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. linux目录的读(r)、写(w)、执行(x)权限说明

    linux目录的读.写.执行权限说明 1.可读r #表示具有浏览目录下面文件及子目录的权限.即ls dir 1)如果没有x权限,不能进到目录里,即无法 cd dir 2)如果没有x权限,ls列表可以看 ...

  2. 《Java基础知识》序列化与反序列化详解

    序列化的作用:为了不同jvm之间共享实例对象的一种解决方案.由java提供此机制. 序列化应用场景: 1. 分布式传递对象. 2. 网络传递对象. 3. tomcat关闭以后会把session对象序列 ...

  3. GHOST CMS - Package.json

    Package.json The package.json file is a set of meta data about a theme. package.json 文件是一组关于主题的元数据. ...

  4. [ASP.NET Core 3框架揭秘] 依赖注入[8]:服务实例的生命周期

    生命周期决定了IServiceProvider对象采用怎样的方式提供和释放服务实例.虽然不同版本的依赖注入框架针对服务实例的生命周期管理采用了不同的实现,但总的来说原理还是类似的.在我们提供的依赖注入 ...

  5. Jsonp的js实现,跨域请求,同源策略机制

    Jsonp的js实现,跨域请求,同源策略机制1.跨域请求:请求URL的协议,域名,端口三者之间任意一个与当前页面地址不同即为跨域 存在跨域的情况: 网络协议不同,端口不通,域名不同,子域名不同,域名和 ...

  6. 今天遇到的一些关于vsftp的坑!!!

    起因:最近在捣鼓Linux,配置vsftp的时候发现权限问题很坑,头疼了一天,发现了一点小问题 常见的身份验证方式: 1.使用系统的用户认证 例如vsftp这种软件,如果想建立一个可以登录FTP的用户 ...

  7. mongodb-API

    mongodb-API 连接mongo(该操作一般在初始化时就执行) 出现 由于目标计算机积极拒绝,无法连接的错误时 查看是否进行虚拟机的端口转发 将 /etc/ 目录下的mongodb.conf 文 ...

  8. 软帝学院:一万字的Java基础知识总结大全(实用)

    Java基础总结大全(实用) 一.基础知识: 1.JVM.JRE和JDK的区别: JVM(Java Virtual Machine):java虚拟机,用于保证java的跨平台的特性. java语言是跨 ...

  9. Java之缓冲流(字节/字符缓冲流)

    概述 缓冲流,也叫高效流,是对4个基本的 FileXxx 流的增强,所以也是4个流,按照数据类型分类: 字节缓冲流: BufferedInputStream , BufferedOutputStrea ...

  10. 牛客集训 湖南省赛E题 Grid 动态开点线段树

    国庆牛客集训的题,正好准备好好训练线段树,想起来就补一下. 题意很简单,两种操作行合并或者列合并,每个操作后计算有多少个子块. 这题应该先推导公式,行操作或者列操作只有一种的时候,很简单,总数就是n* ...