玖叶教程网

前端编程开发入门

零拷贝ZeroCopy,如何提高传输效率?4个场景深入理解

在追求低延迟的传输场景中,经常使用到零拷贝(zero-copy),它是一种高效的数据传输机制,非常实用。

零拷贝在很多框架中得到了广泛应用,比如Netty、Kafka、Spark,下面通过4个场景、10张图详细深入介绍概念原理。

一,简单数据传输

在Web应用中,经常需要上传文件或者数据,通过网络传输,直接看代码:


操作流程解释,注意系统一共发生了4次用户态内核态模式切换,数据也被拷贝了4次:

1. JVM向OS发出read()系统调用,触发模式切换,从用户态切换到内核态。

2. 从存储设备(比如硬盘)读取文件内容,通过直接内存访问(DMA)存入内核缓冲区。

3. 将数据从内核缓冲区拷贝到用户缓冲区,read()系统调用返回,从内核态切换回用户态。

4. JVM向OS发出write()系统调用,触发模式切换,从用户态切换到内核态。

5. 将数据从用户缓冲区拷贝到内核中与目的地Socket关联的缓冲区。

6. 数据经由Socket通过DMA传送到硬件(比如网卡)缓冲区,write()系统调用返回,从内核态切换回用户态。

时序图如下:


流程图:


模式切换示意图:


二,Zero-copy优化

零拷贝时,数据直接从内核缓冲区送入Socket缓冲区,也就是优化了简单数据传输时的第二次和第三次数据复制。

优化后的时序图如下:


流程图:


模式切换示意图:


在Unix系统中,sendfile()实现了该机制,数据拷贝次数变成了3次,模式切换次数也减少到了2次

Java NIO包提供了零拷贝机制对应的API,FileChannel.transferTo()方法。具体实现在sun.nio.ch.FileChannelImpl类中,代码调用示例:


三,真正的零拷贝

在Scatter/Gather DMA机制中,预先维护一个物理上不连续的块描述符链表,描述符中包含有数据的起始地址和长度。

数据传输时,只需要遍历链表,按序传输数据,全部完成后发起一次中断即可,效率比Block DMA要高。

借助于Scatter/Gather DMA,硬件可以直接从内核缓冲区中取得全部数据,不需要再从内核缓冲区向Socket缓冲区拷贝数据,这样就实现了真正的zero-copy。

时序图:


流程图:


四,零拷贝时数据修改

如果需要在数据传输时进行修改,就要用到操作系统提供的内存映射机制(mmap/munmap),将文件数据映射到内核地址空间,直接进行操作,完成之后再刷回去。

Java NIO包中提供了MappedByteBuffer,支持mmap的零拷贝时序图:


在使用mmap支持数据修改机制时,需要权衡效率。为了实现数据修改,代价是4次模式切换,并且需要在快表(TLB)中维护所有数据对应的地址空间,直到刷写完成,由此增加了处理缺页的overhead。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言