关于Linux中的零拷贝技术以及java中的零拷贝

    所谓的零拷贝并不是真正的没有拷贝的过程,个人认为这里的零拷贝只是针对于用户空间和内核空间的拷贝而言的,如果内核空间和用户空间之间没有发生拷贝则是零拷贝,零拷贝依赖与操作系统的很多技术支持,今天就来聊下零拷贝技术以及发展。
传统的拷贝技术:
    传统的拷贝技术是指在零拷贝出现之前当应用程序进行网络通信时候需要首先由应用程序发起read()系统调用,系统中断此时由用户态切换至内核态,系统将数据从磁盘拷贝到内核缓冲区(buffer),然后将数据从内核缓冲区拷贝至用户空间,此时从内核态切换至用户态,至此读操作完成;然后用户进程开始执行代码逻辑,当进行网络通信时会调用write()发起系统调用将数据从用户空间拷贝值socket缓冲区,此时再次进行状态切换,由用户态切换至内核态,然后由系统将数据拷贝至协议引擎,在这个传统的拷贝中共发生了四次拷贝三次上下文切换,如果我们没有对数据进行任何的操作,但是却多出了没有意义的多次拷贝,具体的流程图如下:
在这里插入图片描述
mmap()+write实现的零拷贝技术:
    传统的拷贝技术存在这性能比较低的问题所以就出现了零拷贝技术,首先就是用mmap和write实现的零拷贝,mmap是一种内存映射技术的实现,应用程序调用mmap(),磁盘上的数据会通过DMA被拷贝的内核缓冲区,接着操作系统会把这段内核缓冲区与应用程序共享,这样就不需要把内核缓冲区的内容往用户空间拷贝。应用程序再调用write(),操作系统直接将内核缓冲区的内容拷贝到socket缓冲区中,这一切都发生在内核态,最后,socket缓冲区再把数据发到协议引擎去。但是此种方式虽然少了一次拷贝但是上下文的切换并没有减少,因为系统还是要调用一次write()才能够操作写(此时有三次上下文切换三次拷贝一次cpu拷贝(内核缓冲区–>socket缓冲区)和两次DMA拷贝)具体图如下:
在这里插入图片描述
sendfile():
    sendfile的使用就会减少一次上下文的切换,此时发出sendfile系统调用,导致用户空间到内核空间的上下文切换(第一次上下文切换)。通过DMA将磁盘文件中的内容拷贝到内核空间缓冲区中(第一次拷贝: hard driver ——> kernel buffer)。DMA发出中断,CPU处理中断,将数据从内核空间缓冲区拷贝到内核中与socket相关的缓冲区(第二次拷贝: kernel buffer ——> socket buffer)。sendfile系统调用返回,导致内核空间到用户空间的上下文切换(第二次上下文切换)。通过DMA引擎将内核空间socket缓冲区中的数据传递到网卡(第三次拷贝: socket buffer ——>协议引擎)。
在这里插入图片描述
sendfile+gather:
    最后这种方式在Linux2.4之后开始得到支持,此种方式将CPU的那次拷贝彻底取消,sendfile 系统调用使得文件内容被DMA引擎拷贝到内核缓冲区。没有数据被拷贝到套接字缓冲区。相反,只有具有关于数据的下落和长度信息的描述符被附加到套接字缓冲区。DMA引擎将数据直接从内核缓冲区传递到协议引擎,从而消除了最后剩下的一次数据拷贝。
    以上便是Linux中的零拷贝技术的实现,当基于Linux系统的jvm在进行零拷贝时当然也依赖于Linux的零拷贝技术,通过上述介绍会发现一个问题点就是当使用sendfile无法实现对数据的修改,只有使用mmap的时候会建立起内存的映射关系此时是可以对数据进行修改的,所以java NIO中的零拷贝的实现分为了两种,一种是基于mmap+write的MappedByte的实现,向ByteBuffer中的DirectByteBuffer这种堆外内存就是利用mmap实现的内存映射,然后可以通过用户空间的地址指针对堆外内存进行操作。此种可以实现数据的修改。另外一种就是Java NIO transferTO这种模式不需要对数据进行修改是利用了sendfile这种调用方式实现的零拷贝。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值