Linux零拷贝,java软拷贝和硬拷贝

  Linux零拷贝,java软拷贝和硬拷贝

  

Linux传统IO

  大家好,我是一段躺在Linux磁盘上的数据。现在要将我从磁盘发送到网卡,我需要经过以下步骤:

  

读操作

  如何解决写爬虫IP受阻的问题?立即使用。

  如上所示,操作系统将内存分为内核空间和用户空间。首先,位于用户空间的应用发起数据读取操作,比如JVM发起read()系统调用。此时,操作系统将进行从用户空间到内核空间的上下文切换切换。

  然后内核空间通知磁盘,内核把我从磁盘复制到内核缓冲区。这个过程是由一块叫做“DMA(直接内存访问)”的硬件来完成的,所以不需要CPU的参与。

  然后内核把我从内核缓冲区复制到应用缓冲区,这需要copy的参与。

  最后切换上下文,切换回用户空间的上下文。

  整个阅读操作过程需要两次上下文切换和两次copy

  

写操作

  写操作类似于读操作,只是方向相反,它仍然需要两次上下文切换和两个数据副本。我可能被写到磁盘或网卡上。

  

内存映射

  从上面的过程可以看出,如果要把我从磁盘发送到网卡,一共需要4次上下文切换和4次复制操作。我被操作系统在内核空间和用户空间之间来回复制,但其实这期间我什么都没做,什么都没变。它只是复制,所以这种IO模式是对操作系统资源的浪费。被抄了那么多次,身心俱疲。而且操作系统的资源非常宝贵~

  现在主流操作系统都用虚拟内存。简单来说就是用虚拟地址取代物理地址,可以让多个虚拟内存只想要同一个物理地址,虚拟内存的空间可以比物理内存大很多。

  如果操作系统能把用户空间的应用缓冲区和内核空间的内核缓冲区映射到同一个物理地址,岂不是省去了很多复制?如下图:

  

Linux零拷贝

  所以为了解决这个问题,聪明的Linux开发者编写了一些新的系统调用来做到这一点。主要有两种方式:

  Mmap writesendfile

mmap + write

   mmap()系统调用会先通过DMA复制把我从磁盘读到内核缓冲区,再通过内存映射,使用户缓冲区和内核读缓冲区的内存地址为同一内存地址,也就是说,不需要CPU再将我从内核读缓冲区复制到用户缓冲区啦!

  使用write()系统调用时,CPU直接把我从内核缓冲区(相当于用户缓冲区)写到要发送的内核缓冲区,比如网络发送缓冲区(socket buffer),然后通过DMA把我转到网卡驱动(或者磁盘)上发送。

  mmap写方法需要两个系统调用、四个上下文切换、两个DMA副本和一个CPU副本来读写数据。

  

sendfile

  发送文件也是一个系统调用。实际上,它本质上是将上述两个系统调用的功能合并为一个调用。这样做的好处是操作系统只需要两次上下文切换,减少了两次上下文切换的开销。

  

gather

   Linux2.4内核优化了sendfile,提供了gather操作,可以去掉上图中最后一个CPU副本。其原理是不复制数据,而是将前一个内核缓冲区(例如,图中的情况是Read Buffer)中数据的内存地址和偏移量记录发送到目标内核缓冲区(例如,图中的Socket Buffer),这样就可以在最后的DMA复制阶段保存指针。

  

Java NIO使用零拷贝

   Linux零拷贝确实可以节省一些操作系统资源。所以Java的NIO提供了一些类来支持零拷贝:

  DirectByteBufferFileChannel大概在之前的文章《Java NIO - Buffer》中介绍过DirectByteBuffer。ByteBuffer主要有两个实现,一个是DirectByteBuffer,一个是HeapByteBuffer。

  其中DirectByteBuffer直接在堆外分配内存,底层是通过JNI直接调用操作系统的NIO系统调用,所以性能会更高。HeapByteBuffer是堆内内存,数据需要再复制一次,所以性能比较低。

  File是Java NIO提供的用于复制文件的类,可以复制到磁盘或网络。

  map方法实际上使用了操作系统中的内存映射方法,在内核缓冲区的内存和用户缓冲区的内存之间进行地址映射。

  Transfer方法直接把当前通道的内容转移到另一个通道,也就是说不会有内核缓冲区到用户缓冲区的读写问题。是基础sendfile系统调用。transferFrom方法是相同的。

  示例代码:

  File file=新文件( test . txt );RandomAccessFile RAF=new RandomAccessFile(file, rw );file channel file channel=RAF . get channel();socket channel socket channel=socket channel . open(new InetSocketAddress(,8080));//transferTo()直接用于通道间的数据传输。Transferto (0,filechannel.size(),套接字通道);作者:微信官方账号_xy的技术圈

  链接:www.imooc.com/article/289550

  来源:海量开放在线课程网

  以上内容来自窗帘课网。

  

零拷贝的再次理解

  零拷贝,从操作系统的角度。因为内核缓冲区之间没有重复的数据(只有内核缓冲区有数据的副本)。

  零拷贝不仅带来了更少的数据复制,还带来了其他的性能优势,比如更少的上下文切换,更少的CPU缓存伪共享,没有CPU校验和计算。

  

mmap和sendFile的区别

   mmap适合读写小数据,sendFile适合传输大文件。

  Mmap需要4个上下文切换和3个数据副本;发送文件需要3次上下文切换和至少2个数据副本。

  SendFile可以用DMA减少CPU拷贝,mmap不行(必须从内核拷贝到Socket buffer)。

  以上就是Linux和Java零拷贝的细节。更多请关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: