`
sesame84
  • 浏览: 8252 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类

有效的数据传输zero copy

阅读更多
原文件地址https://www.ibm.com/developerworks/linux/library/j-zerocopy/
 在看kafka时读到这篇文章,感觉不错就翻译了一下,由于E文不好忘海涵。
本文向你介绍一种有效改进I/O密集型JAVA应用的技术,这种技术被叫做zero copy,适用于linux或者unix平台。Zero copy 可以避免buffers间冗余的数据拷贝,减少用户空间和内核空间的context交互。
 
许多的应用提供大量的静态内容服务,及从硬盘中读取数据然后将同样的数据写入socket响应。这种类型的服务需要很少的cpu性能,但是会响应低下:内核从硬盘中读取数据然后通过内核-用户通道传输到相关应用,接着应用把这些数据又通过内核-用户通道写到socket中。实际上,在数据从硬盘到socket的传输中应用扮演了一个低效的中间者角色。
 
每次数据穿越用户-内核管道,它都会被copy,都需要消耗cpu周期和内存空间。幸运的是你通过一种叫做zero copy的技术来减少这些copy。应用通过这项技术将将数据直接从硬盘文件拷贝到socket中,而不再绕行应用。Zero copy提高了应用的表现并且减少了内核模型和用户模式之间的上下文交互。
 
Java的类库支持在Linux和Unix系统中通过java.nio.channels.FileChannel类的transferTo()方法来实现zero copy。transferTo()方法通过它所在的Channel将数据传入另一个可写入的通道而不将数据流绕行应用。本文首先讲述一般文件传输方法,然后再展示zero-copy技术是如何使用transferTo()获得更好的表现的。
 
数据传输:传统的方式
考虑这样一种场景,从一个文件中读取数据然后通过网络传输到其他系统中去(这个场景代表了很多应用服务,包括Web应用中提供静态资源,FTP服务,邮件服务等等)。操作的核心体现在list1代码中的两次调用。
Listing 1. 从文件向socket拷贝
File.read(fileDesc, buf, len);
Socket.send(socket, buf, len);
 
虽然listing1逻辑上非常简单,在整个过程中用户模式和内核模式之间上下文切换共有四次。Figure 1 展示了数据是如何从文件到socket的。
Figure 1. 传统的数据copy方式
Traditional data copying approach

 

Figure 2. 传统的上下文切换
Traditional context switches
 
步骤是:
  1. read()调用触发了从用户模式到内核模式的上下文交换(见Figure2)。在内核空间内一个sys_read()调用完成了从文件中读取数据。这第一次copy(见Figure1)是通过DMA引擎完成的,DMA引擎从硬盘中读取数据然后将他们存到内核地址空间的buffer中。
  2. 被请求的数据从内核的read buffer 拷贝到 用户buffer,并且read()调用返回。这个调用返回又引发了一次从内核到用户模式的交互。现在数据已经被加载在用户空间的buffer中了。
  3. send()socket请求将会引起从用户模式向内核空间的切换。第三次拷贝又将数据传到内核地址空间。但是这次数据是呗写入不同的buffer,目标为socket。
  4. send()系统调用,创建第四次上下文交换。第四次复制为异步的,DMA引擎将内核buffer中的数据写入协议引擎。
使用了中间层的内核buffer(而不是直接将数据写入用户buffer)可能看起来不直接。但是内核buffer的引入确实提高了系统表现。在读取端使用中间层buffer,使得内核buffer扮演了一个“预读高速缓存”的角色,当应用还没有请求如此多的数据。这极大提高了当请求数据量小于内核buffer时的性能。在写端的中间buffer则允许异步的完成写入。
 
不幸的是,当被请求的数据量大于内核buffer大小的时候,他本身成为了性能的瓶颈。数据被传到application前,在硬盘、内核buffer和用户buffer间的多次拷贝。
 
Zero copy 通过减少冗余的数据的拷贝提高性能。
 

数据传输:zero copy 方式

如果再次审视传统场景,你可能会注意到其实第二次和第三次数据拷贝不是必须的。应用除了缓存数据再将它写回socket buffer之外没有其他操作。其实,数据可以直接从read buffer直接写入socket buffer。这个transferTo()方法正是帮你做到了这一点。listing 2 向我们展示了transferTo()的细节:

listing 2. The transferTo() 方法

 
public void transferTo(long position, long count, WritableByteChannel target);
transferTo()将数据从file channel 写入给定的可写入channel 。实际上它依赖于系统对zero copy的支持,在unix和linux系统中,这个调用被叫做sendfile()系统调用。在listing3中,将具体展示:
listing 3. The sendfile() 系统调用
#include <sys/socket.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
listing1中的file.read()和socket.send()方法被简单的transferTo()调用取代,详见listing4
listing 4. Using transferTo() to copy data from a disk file to a socket 
transferTo(position, count, writableChannel);
Figure 3 展示了transferTo()的方法是如何被使用的
 
Figure 3. 数据拷贝通过 transferTo()
Data copy with transferTo()

 

Figure 4 展示了在transferTo()中如何完成上下文切换的
Figure 4. 上下文切换在 transferTo()
Context switching when using transferTo()
 
以下步骤将展示在transferTo()中具体是如何完成上下文切换的大帅府
  1. transferTo()中文件内容是被DMA引擎拷贝到一个可读的buffer。然后数据被内核直接写入与输出socket关联的内核buffer中。
  2. 第三次拷贝发生在DMA引擎将数据从socket buffer中拷贝到协议引擎。
这里有一个改进:我们将上下文切换从4次减少到2次,将copy数从4降低到3(只有一次用到了cpu)。但是仍然未达到我们的zero copy的目标。我们可以通过内核来减少数据重复,如果网卡支持进一步操作。在linux内核2.4及以后版本,socket buffer的descriptor被修改以来适应这种需求。这一修改无法减少上下文切换但是可以减少降低复制数据。在用户端使用是无区别的,但是内部已经发生改变:
  1. transferTo() 中文件内容被DMA引擎加载入内核buffer。
  2. 数据不再被拷入socket buffer。被取代的,只有descriptor关于位置和长度的信息被附加到socket buffer。这DMA引擎将数据直接从内核buffer拷入协议引擎,从而减少了cpu拷贝。
Figure 5. Data copies when transferTo() and gather operations are used
Data copies when transferTo() and gather operations are used

Building a file server

现在用zero copy做个联系,运用同样的例子。TraditionalClinet.java和TraditionServer.java是基于传统方法的,使用File.read()和Socket.send()。TraditionServer.java是一个服务程序在特定的端口监听客户端的请求,然后读取4KB的数据。TraditionalClinet.java连接到服务器端,从文件中读取4KB的数据,然后传输内容到服务器端。
 
同样,TransferToServer.java和TransferToClient.java完成同样的功能,但是代替的使用transferTo()方法(系统中使用sendfile()调用)来讲数据从服务器写入客户端。
 

性能测试

测试使用2.6的内核的linux,每次测试时间不少于百万秒级并且transferTo(),下表将展示结果:
Table 1. Performance comparison: Traditional approach vs. zero copy
File size Normal file transfer (ms) transferTo (ms)
7MB 156 45
21MB 337 128
63MB 843 387
98MB 1320 617
200MB 2124 1150
350MB 3631 1762
700MB 13498 4422
1GB 18399 8537
 
如你所见,transferTo()比较传统观方法大约节省65%的时间。
 
总结
我们展示了使用transferTo()的性能和传统的方式比较。中间buffer的拷贝(隐藏于内核)带来了可观的消耗。在应用中2个channel之间数据传输,zero-copy拷贝技术可以得到显著的性能提升。
分享到:
评论

相关推荐

    通过zero copy来实现高效的数据传输1

    【标题】: 通过zero copy实现高效的数据传输 【描述】: 本文深入探讨了如何在Linux或Unix系统上利用zero copy技术提升Java程序的IO性能,特别是在大量数据传输的场景下。通过避免不必要的数据复制和减少用户态与...

    j-zerocopy

    总结起来,"j-zerocopy"项目主要探讨了如何在Java环境中利用NIO和内存映射文件等技术实现零拷贝,以提高Socket通信的效率。通过对比传统Socket编程与零拷贝Socket编程,我们可以更好地理解这项技术的优势,并在实际...

    通过零拷贝实现有效数据传输

    通过零拷贝实现有效数据传输 Linux zero copy

    linux zero copy mmap

    零拷贝(Zero Copy)技术是现代操作系统中一项重要的优化技术,主要用于减少数据在不同内存区域之间的复制次数,从而提高系统的整体性能。在Linux系统中,mmap机制作为一种典型的零拷贝实现方式,在网络数据包处理...

    对于 Netty ByteBuf 的零拷贝(Zero Copy) 的理解1

    Netty ByteBuf 的零拷贝(Zero-Copy)理解 Netty 中的零拷贝(Zero-Copy)是指在操作数据时,不需要将数据 buffer ...Netty 的 Zero-Copy 机制可以有效地减少 CPU 的负载和内存带宽的占用,提高了数据传输的效率和性能。

    zero_copy_stream_impl.rar_Zero

    零拷贝(Zero-Copy)是一种优化I/O操作的技术,它的主要目标是减少CPU在处理数据时的内存拷贝操作,从而提高系统效率,尤其是在大数据传输场景下。 在描述中提到的"Copying File Input Stream for linux API Driver...

    zerocopy:Go的零复制IO原语和管道。 特定于Linux

    在无法进行I / O加速的情况下,管道透明地回退到用户空间数据传输。 当前,zerocopy软件包仅在Linux上以及特定类型的文件描述符中提供加速的I / O。 状态 该软件包是alpha质量软件。 它正在积极开发中,几乎可以...

    java与zero-copy

    文章提到,利用内核直接将数据从磁盘文件复制到socket中,而无需通过应用程序这一中介,从而实现了更高效的数据传输。这在Linux和UNIX系统中可以通过Java NIO(New Input/Output)中的FileChannel类的transferTo()...

    什么是Zero-Copy?1

    在传统的数据传输过程中,数据需要经过多次复制,从硬盘到用户空间,再到内核空间,然后再返回内核空间到网络缓冲区,最后发送到网络。这种过程既消耗了CPU资源,也增加了不必要的延迟。 在描述中提到的场景是静态...

    zero_copy_stream_unittest.rar_Zero

    零拷贝是一种优化数据传输的技术,旨在减少CPU在内存到I/O设备之间复制数据时的负担,从而提高系统效率。描述中的"Helper to write an array of data to an output stream"进一步确认了该代码可能涉及将数据从内存...

    Using Zero-Copy In ROS2

    在ROS2中,零拷贝(Zero-Copy)技术是一种优化数据传输的方法,尤其是在处理大量传感器数据时,可以显著提高系统性能。本文将深入探讨ROS2中的零拷贝通信以及相关的技术。 首先,我们需要理解什么是零拷贝数据传输...

    LyraNET: A Zero-Copy TCP/IP Protocol Stack for Embedded Operating的翻译

    在LyraNET中,通过巧妙的数据传输机制,网络数据直接从网络接口卡(NIC)传递到应用程序,或者反之,减少了CPU的介入和数据复制,从而减少了CPU负荷,提高了吞吐量,并降低了延迟。 具体实现上,LyraNET可能采用了...

    Linux I/O 原理和 Zero-copy 技术全面揭秘

    传统的Linux I/O机制在处理数据传输时,数据会在用户态缓冲区和内核态缓冲区之间多次拷贝,消耗了大量的CPU资源,降低了系统效率。 Linux I/O机制主要涉及以下几个关键环节: 1. **虚拟内存**:为了解决应用程序对...

    simpleZeroCopy.tar.gz_Zero_cuda

    在CUDA(Compute Unified Device Architecture)并行计算框架中,Zero Copy是一种优化技术,它允许GPU直接访问主机内存,从而减少数据传输的开销,提高计算效率。标题中的"simpleZeroCopy.tar.gz_Zero_cuda"暗示了一...

    理解Netty中的零拷贝(Zero-Copy)机制1

    "理解Netty中的零拷贝(Zero-Copy)机制1"这篇文章除了讲解Netty的核心特性——零拷贝之外,还涉及到Linux和Java的相关知识。 零拷贝技术是一种优化数据传输的方法,它减少了CPU在数据传输过程中的参与,提高了系统...

    Design_and_implementation_of_zero-copy_data_path_for_efficient_file_transmission

    **零拷贝(Zero-Copy)**技术是指在数据传输过程中,数据在用户空间和内核空间之间的移动无需经过CPU的拷贝操作。传统的文件传输过程中,数据往往需要经历多次拷贝操作,包括但不限于从磁盘读取数据到内核缓冲区、再...

    [done]Zero-copy Receive for vhost.pdf

    4. 零拷贝(Zero-copy)技术: - 零拷贝技术是指数据在传输过程中避免重复拷贝,从而减少CPU资源消耗和提高数据处理速度。 - 通过零拷贝技术可以避免从网络设备拷贝数据到用户空间的内存中,再从用户空间拷贝到...

    Go-ZSocket是LinuxZero-copysockets的一个Golang封装库

    Go-ZSocket库是针对Linux平台上的零拷贝(Zero-copy)套接字技术的一个Golang实现,它旨在提供高效的数据传输能力,优化网络I/O性能。在传统的数据传输过程中,数据在内核空间和用户空间之间多次拷贝,消耗了大量的...

    C# 软件之间通讯之WM_COPYDATA

    请注意,实际应用中可能需要处理错误,例如目标窗口未找到或数据传输失败等。 在提供的文件列表中,有两个可执行文件(testmessage 1.exe 和 testmessage.exe)可能代表了发送方和接收方的应用程序实例。同时,源...

    Linux零拷贝原理.pdf

    零拷贝(Zero Copy)是指在数据传输过程中,尽量减少甚至避免数据在内核空间缓冲区和用户空间缓冲区之间的不必要复制。在传统的数据传输模式下,数据需要从磁盘读取到内核缓冲区,然后从内核缓冲区复制到用户缓冲区...

Global site tag (gtag.js) - Google Analytics