ZeroCopy た 入场券 2022-02-05 05:11 257阅读 0赞 # 操作系统的ZeroCopy # ## 简单操作 ## byte[] buf=new byte[1024]; InputStream inputStream=new FileInputStream("in.txt"); OutputStream outputStream = new FileOutputStream("out.txt"); int read; while ((read=inputStream.read(buf))!=-1){ outputStream.write(buf,0,read); } inputStream.close(); outputStream.close(); 这一段代码是一个经典的将in.txt文件复制到out.txt的一段代码,看起来非常简单,然而其中包含着非常多的操作,下面的图表明了这段代码发生的事情 ![read write][] 其中至少包含了4次数据的复制,并且包含了多次用户空间和内核空间的切换.其中\*\*DMA(Direct Memory Access)\*\*叫做直接内存访问 1. read将数据从通过DMA将硬件数据复制到old fd相关的内核缓冲区中 2. 内核数据缓冲区的数据复制到用户空间 3. 用户空间复制到new fd对应的内核缓冲区 4. DMA将内核空间缓冲区数据复制到协议引擎,第四次复制 5. 协议引擎进行数据写入 第一次不需要将DMA数据复制到fd关联的数据缓冲区的原因是,这个硬件本身就是通过fd查找到的. write的时候需要复制到fd相关的内核缓冲区中,因为是另外一个fd ## 使用内存映射优化的操作 ## 对于数据的复制,用户空间的数据其实是不需要的,因此可以针对这种问题进行优化,即不进行内核态到用户态的数据复制,即使用**内存映射**. 内存映射可以将内核空间中的内存块关联到用户空间,使得用户空间看起来在操作内核空间一样,底层是使用**mmap**函数 使用这个函数可以去除内核空间复制到用户空间的操作,但仍然需要从old fd复制到new fd空间 因此,数据的复制操作可以减少为3次.但用户态的切换次数仍然不变 在Java中使用**MappedByteBuffer**实现这个操作,实际上底层操作的是**DirectByteBuffer** FileChannel channel=... MappedByteBuffer map=channel.map(FileChannel.MapMode.READ_WRITE,0,5); map.set(1,'b') FileChannel可以来自于File和IO ## 使用sendFile的操作 ## ### 最少用户态切换的sendFile操作 ### 除上述操作之外,还可以使用**内核2.1**之后提供的**sendFile**操作,进行直接复制的操作来避免用户态的切换 如下图 ![1556981497491][] 1. sendFile将硬件数据复制到old fd关联的内核缓冲区中 2. 将数据复制到new fd关联的内核缓冲区 3. DMA将内核缓冲区复制到协议引擎 4. 协议引擎进行数据写入 一共进行3次数据复制,2次上下文切换 ### 完全消除不必要复制的sendFile ### 这个方式还可以进行优化,其中数据复制到新文件描述符的操作不是必要的,可以通过内存地址的方式寻址到第一次复制的数据.即关联old fd和new fd即可 ![1556982326290][] 1. sendFile将硬件数据复制到内核缓冲区中 2. 将old fd和new fd进行关联 3. 将old fd中的数据写入到硬件 在Java中使用**FileChannel**的**transferFrom**函数和**transferTo**函数实现 上述使用**sendFile**的方式仅适用于不需要操作数据的简单复制,如果需要操作数据,最优的方式是**mmap**的方式 # netty的ZeroCopy # 与操作系统的ZeroCopy,netty由于本身不仅仅是数据的复制,大部分情况下,netty都需要进行数据的操作,因此netty更加关注的是**避免复制**. netty提供了许多种避免复制的方式,但本质上都是一样的,即通过**视图**的方式进行,如果熟悉数据库的话马上就能够理解意思了.视图不是真实存在的数据,而是对真实数据的一种访问方式.另外对于不需要操作数据的情况,jdk本身就已经提供了**transferTo**和**transferFrom**函数,netty进行了一点封装以便ByteBuf使用 ## 多个buffer的合并视图 ## **CompositeByteBuf** JDK的ByteBuffer未找到类似的操作 netty使用compositeByteBuf.addComponents相关的函数进行操作 ## 原生类型的视图 ## JDK通过ByteBuffer.wrap相关函数进行操作 netty使用Unpooled.wrappedBuffer相关的函数进行操作 ## 单个buffer的部分视图 ## JDK使用ByteBuffer.slice()函数进行操作 netty使用ByteBuf.slice相关的函数进行操作 ## 直接复制 ## netty使用FileRegion,实际上底层还是使用的JDK的transfer [read write]: /images/20220205/736f9a5a034042f3b98738c4e6ca9639.png [1556981497491]: /images/20220205/b9ee1283334148158aedd35070c0730c.png [1556982326290]: /images/20220205/826543c82c514533b3078d8712c90692.png
相关 JAVA NIO 中的 zerocopy 技术提高IO性能 关于一篇更详细更好的介绍 ZeroCopy技术的文章,可参考:[JAVA IO 以及 NIO 理解][JAVA IO _ NIO] 这篇文章介绍了 zerocopy技 「爱情、让人受尽委屈。」/ 2022年06月13日 03:26/ 0 赞/ 205 阅读
相关 linux之零拷贝(ZeroCopy) linux之零拷贝(ZeroCopy) 传统的数据传输方式: 像这种在文件读取数据然后将数据通过网络传输给其他的程序的方式(大部分应用服务器都是这种方式,包括web 雨点打透心脏的1/2处/ 2022年06月07日 01:14/ 0 赞/ 222 阅读
相关 ZeroCopy 操作系统的ZeroCopy 简单操作 byte[] buf=new byte[1024]; InputStream inputStream=new た 入场券/ 2022年02月05日 05:11/ 0 赞/ 258 阅读
还没有评论,来说两句吧...