许多Web应用程序都提供了大量的静态内容,这相当于从磁盘读取数据并将完全相同的数据写回到响应socket。这个活动可能似乎只需要相对较少的CPU活动,但是效率有些低下:内核从磁盘读取数据,并将其从内核用户边界推送到应用程序,然后应用程序将其推回到内核用户边界写出来的socket。实际上,应用程序作为一个低效的媒介,从磁盘文件获取数据到socket。

每次数据遍历用户内核边界时,都必须进行复制,这会消耗CPU周期和内存带宽。幸运的是,您可以通过一种称为“适当地 - 零拷贝”的技术来消除这些副本。内核使用零拷贝的应用程序要求内核直接将数据从磁盘文件复制到套接字,而不通过应用程序。零拷贝大大提高了应用程序的性能,减少了内核和用户模式之间的上下文切换次数。

Java类库通过transferTo()in方法在 Linux和UNIX系统上支持零拷贝java.nio.channels.FileChannel。您可以使用该transferTo()方法将字节从其调用的通道直接传输到另一个可写字节通道,而不需要数据流经应用程序。本文首先演示了通过传统的复制语义进行简单文件传输所带来的开销,然后展示了如何使用零复制技术 transferTo()获得更好的性能。

日期转移:传统的方法

考虑从文件读取并通过网络将数据传输到另一个程序的情况。(本场景描述了许多服务器应用程序的行为,包括提供静态内容的Web应用程序,FTP服务器,邮件服务器等等)。操作的核心是清单1中的两个调用(参见下载链接完整的示例代码):

清单1.将文件中的字节复制到套接字
1
2
File.read(fileDesc, buf, len);
Socket.send(socket, buf, len);

虽然清单1在概念上很简单,但在内部,复制操作需要在用户模式和内核模式之间进行四次上下文切换,并且在操作完成之前将数据复制四次。图1显示了数据如何从文件内部移动到套接字:

图1.传统的数据复制方法

图2显示了上下文切换:

图2.传统的上下文切换

涉及的步骤是:

  1. read()调用导致从用户模式到内核模式的上下文切换(参见图2)。内部sys_read()发行一个(或等同的)来从文件中读取数据。第一个副本(见图1)由直接内存访问(DMA)引擎执行,该引擎从磁盘读取文件内容并将其存储到内核地址空间缓冲区中。
  2. 所请求的数据量从读取缓冲区复制到用户缓冲区,然后read()调用返回。调用返回导致另一个上下文从内核切换回用户模式。现在数据存储在用户地址空间缓冲区中。
  3. send()插座调用导致从用户模式到内核模式的上下文切换。执行第三个拷贝将数据再次放入内核地址空间缓冲区。但是这一次,数据被放入不同的缓冲区,一个与目标socket相关的缓冲区。
  4. send()系统调用返回,创造了第四上下文切换。独立地和异步地,当DMA引擎将数据从内核缓冲区传递到协议引擎时发生第四个副本。

使用中间内核缓冲区(而不是将数据直接传输到用户缓冲区)可能看起来效率低下。但是中间内核缓冲区被引入进程来提高性能。在读取端使用中间缓冲区允许内核缓冲区充当“预读缓存”,当应用程序没有要求与内核缓冲区一样多的数据时。当请求的数据量小于内核缓冲区大小时,这会显着提高性能。写入侧的中间缓冲区允许写入异步完成。

不幸的是,如果所请求数据的大小远远大于内核缓冲区的大小,这种方法本身可能会成为性能瓶颈。在磁盘,内核缓冲区和用户缓冲区最终传递到应用程序之前,数据被复制多次。

零拷贝通过消除这些冗余数据副本来提高性能。

数据传输:零拷贝方法

如果您重新检查传统方案,则会注意到第二个和第三个数据副本实际上不是必需的。除了缓存数据并将其传回到socket缓冲区之外,应用程序只做其他事情。相反,数据可以直接从读取缓冲区传输到socket缓冲区。该transferTo() 方法可以让你做到这一点。清单2显示了以下方法的签名 transferTo()

清单2. transferTo() 方法
1
public void transferTo(long position, long count, WritableByteChannel target);

transferTo()方法将数据从文件通道传输到给定的可写字节通道。在内部,它取决于底层操作系统对零拷贝的支持; 在UNIX和各种Linux中,这个调用被路由到sendfile() 系统调用,如清单3所示,它将数据从一个文件描述符传输到另一个:

清单3. sendfile()系统调用
1
2
#include <sys/socket.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

清单1中的file.read()socket.send() 调用的操作可以被一个调用所取代 ,如清单4所示:transferTo()

清单4.使用 transferTo()将数据从磁盘文件复制到socket
1
transferTo(position, count, writableChannel);

图3显示了transferTo()使用该方法时的数据路径:

图3.使用数据拷贝 transferTo()

图4显示了transferTo() 使用该方法时的上下文切换:

图4.使用的上下文切换 transferTo()

transferTo()在清单4中使用时所采取的步骤如下:

  1. transferTo()方法使得文件内容被DMA引擎复制到读缓冲器中。然后,数据被内核复制到与输出套接字关联的内核缓冲区中。
  2. 第三个副本是在DMA引擎将数据从内核socket缓冲区socket传递给协议引擎时发生的。

这是一个改进:我们已经将上下文切换次数从四次减少到两次,并将数据副本的数量从四个减少到三个(其中只有一个涉及CPU)。但是这还没有使我们达到零拷贝的目标。如果底层网络接口卡支持收集操作,我们可以进一步减少内核的数据重复。在Linux内核2.4和更高版本中,套接字缓冲区描述符被修改以适应这个要求。这种方法不仅减少了多个上下文切换,还消除了需要CPU参与的重复数据副本。用户端的使用情况仍然保持不变,但内在因素已经改变:

  1. transferTo()方法使得文件内容被DMA引擎复制到内核缓冲区中。
  2. 没有数据被复制到socket缓冲区中。相反,只有带有关于数据的位置和长度的信息的描述符被附加到socket缓冲区。DMA引擎直接将数据从内核缓冲区传递到协议引擎,从而消除了剩余的最终CPU副本。

图5显示了使用transferTo()收集操作的数据副本:

图5. transferTo()使用和收集操作时的数据拷贝

建立一个文件服务器

现在,让我们将零拷贝付诸实践,使用在客户机和服务器之间传输文件的相同示例(请参阅下载以获取示例代码)。TraditionalClient.java并 TraditionalServer.java基于传统的复制语义,使用File.read()Socket.send()TraditionalServer.java是一个服务器程序,它侦听特定的端口以供客户端连接,然后从套接字一次读取4K字节的数据。TraditionalClient.java连接到服务器,File.read()从文件读取(使用)4K字节的数据,并socket.send()通过socket将内容发送(使用)到服务器。

类似地,TransferToServer.java和 TransferToClient.java执行相同的功能,而是使用transferTo()方法(以及反过来的sendfile()系统调用)将文件从服务器传送到客户端。

性能比较

我们在运行2.6内核的Linux系统上执行示例程序,并测量传统方法和transferTo()不同尺寸方法的运行时间(以毫秒为单位)。表1显示了结果:

表1.性能比较:传统方法与零拷贝
文件大小 正常文件传输(毫秒) 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()与传统方法相比,该API将时间缩短了约65%。这对于从一个I / O通道向另一个I / O通道复制大量数据的应用程序(如Web服务器)具有显着的提高性能的潜力。

概要

我们已经展示了使用transferTo()相比于从一个通道读取并将相同数据写入另一个通道的性能优点 。中间缓冲区副本 - 即使是那些隐藏在内核中的副本 - 也会有可测量的成本。在通道之间大量复制数据的应用程序中,零复制技术可以显着提高性能。

kafka通过零拷贝实现高效的数据传输相关推荐

  1. 通过零拷贝实现高效的数据传输(操作系统)

    许多Web应用程序提供大量静态内容,这相当于从磁盘读取数据并将完全相同的数据写回到响应套接字.此活动似乎只需要相对较少的CPU活动,但效率有点低下:内核从磁盘读取数据并将其跨越内核用户边界推送到应用程 ...

  2. 零拷贝实现高效的数据传输 -Efficient data transfer through zero copy

    点击参考原文 - 1.概述 许多Web应用程序提供大量静态内容,相当于从磁盘读取数据原封不动地写回响应套接字. 这个操作似乎只需要相对较少的CPU时间,但它的效率有点低:内核从磁盘读取数据,并跨越内核 ...

  3. 通过零拷贝进行有效的数据传输(java、c)

    目录 日期转移:传统方法 数据传输:零复制方法 构建文件服务器 性能比较 概要 相关阅读 许多Web应用程序提供大量的静态内容,这相当于从磁盘上读取数据并将完全相同的数据写回响应套接字.该活动似乎需要 ...

  4. Kafka的零拷贝技术

    kafka中的消费者在读取服务端的数据时,需要将服务端的磁盘文件通过网络发送到消费者进程,网络发送需要经过几种网络节点.如下图所示: 传统的读取文件数据并发送到网络的步骤如下: (1)操作系统将数据从 ...

  5. Kafka和RocketMQ底层存储:零拷贝技术

    零拷本相关 <[转]零拷贝的实现原理> <[转]零拷贝的实现原理> <搞懂Linux零拷贝,DMA> <通过零拷贝进行有效的数据传输(java.c)> ...

  6. 为什么 P8 程序员的代码你写不出来?零拷贝了解一下

    计算机处理的任务大体可以分为两类:CPU密集型与IO密集型. 当前流行的互联网应用更多的属于IO密集型,传统的IO标准接口都是基于数据拷贝的,这篇文章我们主要关注该怎样从数据拷贝的角度来优化IO性能, ...

  7. Linux - 零拷贝技术

    Linux - 零拷贝技术 前言 一. 相关概念 1.1 缓冲区 1.1.1 内核缓冲区 1.1.2 用户缓冲区 1.2 DMA技术 1.3 虚拟内存 二. 零拷贝 2.1 传统文件传输流程 2.2 ...

  8. 浅析操作系统和Netty中的零拷贝机制

    点击关注公众号,Java干货及时送达 零拷贝机制(Zero-Copy)是在操作数据时不需要将数据从一块内存区域复制到另一块内存区域的技术,这样就避免了内存的拷贝,使得可以提高CPU的.零拷贝机制是一种 ...

  9. Linux Zero-copy零拷贝技术:源码示例

    <Linux Zero-copy零拷贝技术:源码示例> <Linux Zero-copy零拷贝技术全面揭秘> <什么是mmap?零拷贝?DMA?> <Linu ...

最新文章

  1. AI实时特效,魔幻修图,Adobe Photoshop相机拯救PS菜鸟
  2. 6,ORM组件XCode(撬动千万级数据)
  3. 【拥抱大厂系列】几个面试官常问的垃圾回收器,下次面试就拿这篇文章怼回去!
  4. 面向数智营销的 AI FAAS 解决方案
  5. Robotium_断言方法assert、is、search
  6. spring boot配置dubbo注意事项
  7. nfc卡模式与标准模式_渠道如何标准化管理,建立新的销售模式,提升业绩完成率...
  8. 超简单利用xposed框架破解钉钉打卡
  9. Python中的常用模块
  10. 详解CNN五大经典模型:Lenet,Alexnet,Googlenet,VGG,DRL
  11. 2018百度原创力排行榜公示(转载)
  12. 巧妇难为无米之炊 走出软件测试的困境
  13. java毕业设计成品源码网站基于SpringBoot旅游信息管理系统
  14. 网站服务器历史解析记录查询,域名解析ip历史查询
  15. win8提示当前页面的脚本发生错误如何解决
  16. LTE附着流程详解-UECapabilityInformation
  17. 46、微信-群聊列表
  18. word表格分开快捷键_word文档如何快速拆分表格,干货!怎样快速拆分表格以及快速合并表格技巧介绍...
  19. 组合逻辑电路的设计:竞争和险象
  20. 机器人来了!日本保险巨头启用AI替换30%理赔部员工

热门文章

  1. Mongodb和redis书籍调研
  2. django避免写models.py办法
  3. train和test的列分布差异(covariate shift)观察
  4. ubuntu16.04下面xfce4没有声音
  5. ImportError: No module named 'pip._vendor.retrying'
  6. 魔术方法php重定向,PHP魔术方法__get()
  7. mongodb 启动_精心总结--mongodb分片集群启动与关闭
  8. python 字符串 编码 解码_Python 字符串编解码研究
  9. Git submodule的使用
  10. amazeui学习笔记一(开始使用4)--Web App 相关