在了解java零拷贝技术之前,先来回顾下传统的java数据流方法:
传统数据传输.png
由上图可以看出,内核从磁盘文件读取数据,然后将数据从内核空间拷贝到用户进程空间,最后应用程序将数据拷贝回内核空间并通过套接字发送。在整个流程中应用程序仅充当一个将数据从磁盘拷贝到套接字的低效中间层。
这个过程产生的系统消耗是:
- 4次数据拷贝
- 4次内核和应用程序上下文之间的切换
拷贝操作消耗CPU和内存带宽,但是有一种称为"零拷贝"的技术可以减少数据传输过程中数据从内核到应用程序之间的拷贝数次。
transferTo方式.png
零拷贝的概念:内核将磁盘数据直接拷贝到套接字而不再经过应用,零拷贝可以极大的提高应用的性能并减少上下文在内核态和用户态之间的切换次数。回想使用传统语义传递数据的场景,你会发现第二次和第三次数据拷贝并不是真的需要,数据可以直接从内核的读缓冲区传输到套接字缓冲区。
在类Linux系统中,可通过java.nio.channels.FileChannel
的transgerTo
方法支持零拷贝。可以使用transgerTo
方法在两个通道之间直接传递数据,而不要求数据经过应用程序。如下图
使用 transferTo() 时上下文切换:
1、transgerTo方法调用触发DMA引擎将文件上下文信息拷贝到内核读缓冲区,接着内核将数据从内核缓冲区拷贝到与外出套接字相关联的缓冲区
2、DMA引擎将数据从内核套接字缓冲区传输到协议引擎(第三次数据拷贝)
transferTo的改进之处:上下文切换的次数从4次减少到2次,数据拷贝的次数从4次减少到3次(仅有一次数据拷贝消耗CPU资源)。然而,这并没有实现零拷贝的目标,Linux 内核 从2.4 版本开始修改了套接字缓冲区描述符以满足这个要求。这种方法不仅减少了多个上下文切换,还消除了消耗CPU的重复数据拷贝。用户使用的方法没有任何变化,依然通过transferTo方法,
但是方法的内部实现发生了变化:
- transferTo方法调用触发 DMA 引擎将文件上下文信息拷贝到内核缓冲区。
-
数据不会被拷贝到套接字缓冲区,只有数据的描述符(包括数据位置和长度)被拷贝到套接字缓冲区。DMA 引擎直接将数据从内核缓冲区拷贝到协议引擎,这样减少了最后一次需要消耗CPU的拷贝操作。如下图所示:
image.png
网友评论