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

article/2025/10/7 3:26:24

目录

日期转移:传统方法

数据传输:零复制方法

构建文件服务器

性能比较

概要

相关阅读


 

许多Web应用程序提供大量的静态内容,这相当于从磁盘上读取数据并将完全相同的数据写回响应套接字。该活动似乎需要较少的CPU活动,但效率较低:内核从磁盘读取数据并将其跨内核用户边界推送到应用程序,然后应用程序将其跨内核用户边界推送回写入插座。实际上,该应用程序充当了效率低下的中介,将数据从磁盘文件获取到套接字。

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

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

 

日期转移:传统方法

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

 

清单1.将字节从文件复制到套接字

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()插座调用导致从用户模式到内核模式的上下文切换。执行第三次复制以再次将数据放入内核地址空间缓冲区。但是,这次将数据放入另一个缓冲区中,该缓冲区与目标套接字关联。
  4. send()系统调用返回,创造了第四上下文切换。独立且异步地,当DMA引擎将数据从内核缓冲区传递到协议引擎时,发生第四次复制。

使用中间内核缓冲区(而不是将数据直接传输到用户缓冲区中)似乎无效。但是将中间内核缓冲区引入了该过程以提高性能。在读取端使用中间缓冲区可以在应用程序未要求内核缓冲区容纳的数据量时,将内核缓冲区用作“预读缓存”。当请求的数据量小于内核缓冲区大小时,这将显着提高性能。写侧的中间缓冲区允许写异步完成。

不幸的是,如果请求的数据大小比内核缓冲区的大小大得多,则此方法本身可能会成为性能瓶颈。数据在最终交付给应用程序之前,已在磁盘,内核缓冲区和用户缓冲区之间多次复制。

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

数据传输:零复制方法

如果您重新检查传统方案,您会发现实际上并不需要第二和第三数据副本。除了缓存数据并将其传输回套接字缓冲区外,该应用程序什么也不做。相反,数据可以直接从读取缓冲区传输到套接字缓冲区。该transferTo()方法使您可以精确地做到这一点。清单2显示了方法签名transferTo()

 

清单2. transferTo()方法

public void transferTo(long position, long count, WritableByteChannel target);

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

 

清单3. sendfile()系统调用

#include <sys/socket.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

清单1中的file.read()and socket.send()调用的动作可以由一个调用代替,如清单4所示:transferTo()

 

清单4.使用transferTo()将数据从磁盘文件复制到套接字

transferTo(position, count, writableChannel);

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

图3.用transferTo()复制数据

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

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

transferTo()如清单4所示使用时所采取的步骤是:

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

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

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

图5显示了transferTo()与gather操作一起使用的数据副本:

图5.使用transferTo()和gather操作时的数据副本

 

构建文件服务器

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

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

 

性能比较

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

表1.性能比较:传统方法与零副本

文件大小正常文件传输(毫秒)transferTo(毫秒)
7MB15645
21MB337128
63MB843387
98兆字节1320617
200MB21241150
350MB36311762
700MB134984422
1GB183998537

如您所见,transferTo()与传统方法相比,API将时间减少了大约65%。对于将大量数据从一个I / O通道复制到另一个I / O通道的应用程序(例如Web服务器),这可能会显着提高性能。

 

概要

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

 

相关阅读

搞懂Linux零拷贝,DMA:https://rtoax.blog.csdn.net/article/details/108825666

 


http://chatgpt.dhexx.cn/article/gSEYdJ2c.shtml

相关文章

NIO中的零拷贝--transferTo

1、我们说零拷贝&#xff0c;是从操作系统的角度来说的。因为内核缓冲区之间&#xff0c;没有数据是 重复的&#xff08;只有 kernel buffer 有一份数据&#xff09;。 2、零拷贝不仅仅带来更少的数据复制&#xff0c;还能带来其他的性能优势&#xff0c;例如更少的上下 文切…

使用transferTo上传文件容器为jetty的问题

报错 springboot 内嵌的 tomcat替换jetty 9.4.44 后出现的问题。 jetty容器使用transferTo 上传问题会提示找不到文件。 ava.io.FileNotFoundException: C:\Users\XXX\AppData\Local\Temp\D:\workspace\1675246694267.xlsx(文件名、目录名或卷标语法不正确。) 跟进代码发现 是下…

Java很简单的文件上传(transferTo方式)

采用file.Transto 来保存上传的文件&#xff0c;代码简单&#xff0c;速度快. package com.springbootemaildemo.controller;import com.springbootemaildemo.entity.ResponseEntity; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import…

动态规划题目2

动态规划 跳跃游戏 VI (滑动窗口动态规划)丑数统计字典序元音字符串的数目最长重复子串分隔数组以得到最大和最低票价回文子串最长重复子数组最长回文子序列摆动序列旋转函数统计各位数字都不同的数字个数 (动规排列组合)最大整除子集猜数字大小 II超级丑数预测赢家栅栏涂色丑数…

动态规划专题精讲1

致前行的人&#xff1a; 要努力&#xff0c;但不要着急&#xff0c;繁花锦簇&#xff0c;硕果累累都需要过程&#xff01; 前言&#xff1a; 本篇文章为大家带来一种重要的算法题&#xff0c;就是动态规划类型相关的题目&#xff0c;动态规划类的题目在笔试和面试中是考察非常高…

Java实现动态规划经典题目

动态规划入门请看&#xff1a; DP动态规划专题&#xff08;一&#xff09;动态规划基本模型 前言 【说明】 关于动态规划的见解&#xff1a;动规和递归有很多相似的地方&#xff0c;最显著的特征可以说是阶段性&#xff0c;二者都有很明显的阶段划分&#xff0c;所以&#xf…

十一、动态规划题目相关

学习来源&#xff1a; 代码随香录&#xff1a;https://www.programmercarl.com/ labuladong算法&#xff1a;https://labuladong.github.io/algo/ 动态规划 动态规划五部曲 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 确定递推公式 dp数组如何初始化 确定遍历…

动态规划题目汇总

文章目录 序言题目一&#xff1a;古生物血缘远近判定题目二&#xff1a;迷宫I题目三&#xff1a;迷宫II题目四&#xff1a;出界的路径数题目五&#xff1a;最长公共字串题目六&#xff1a;最长递增子序列题目七&#xff1a;递增的三元子序列题目八&#xff1a;最长回文字串题目…

动态规划经典例题:不同路径

力扣上比较简答的一道动态规划题目。 方程&#xff1a; class Solution { public:int uniquePaths(int m, int n) {const int M m;const int N n;int dp[M][N];memset(dp, 0, sizeof(dp));for (int i 0; i < m; i) dp[i][0] 1;for (int i 0; i < n; i) dp[0][i] 1;…

动态规划算法与典型例题

目录 前言 一、动态规划要素&#xff08;条件&#xff09; 二、动态规划算法设计步骤 三、复杂度分析 四、典型例题1——游艇租聘 五、典型例题2——0-1背包问题 六、典型例题3——跳台阶问题 七、典型例题4——强盗抢劫问题 总结 前言 动态规划也是一种分治思想&…

动态规划问题经典例题

目录 前言一、字符串分割二、三角矩阵的最小路径和三、路径总数四、最小路径和五、背包问题六、 回文串分割七、编辑距离八、不同的子序列 前言 DP&#xff08;Dynamic Programming&#xff09;定义&#xff1a; 动态规划是分治思想的延伸&#xff0c;通俗一点来说就是大事化小…

动态规划经典题目总结

微信公众号 在算法中&#xff0c;动态规划题目算是比较经典的一类题目。在找工作中&#xff0c;不管是笔试&#xff0c;还是面试&#xff0c;我们经常会遇到用动态规划来解决问题的情况&#xff0c;有时候面试官还需要我们现场手写出动态规划解法的代码。因此&#xff0c;在求职…

【动态规划】经典例题

一.动态规划三要素 1.最优子结构 2.状态转移方程 &#xff08;核心&#xff09;&#xff08;一般用打表找出规律&#xff09; 3.边界值 二.背包问题 &#xff08;一.题目&#xff09; 1.1题目描述 现在有一个背包但容量有限&#xff0c;最多只能装m千克宝石!有n个宝石&…

【动态规划专栏】--基础-- 动态规划经典题型

目录 动态规划 动态规划思维&#xff08;基础&#xff09; 状态表示&#xff08;最重要&#xff09; 状态转移方程&#xff08;最难&#xff09; 初始化&#xff08;细节&#xff09; 填表顺序&#xff08;细节&#xff09; 返回值&#xff08;结果&#xff09; 1、第 …

动态规划入门详解 内含12道经典动态规划编程题

动态规划入门详解 一 什么是动态规划&#xff1f;&#xff1f; 算法导论中介绍&#xff0c;动态规划和分治方法类似&#xff0c;都是听过子问题的解来解决原问题。下面说一下这2者之间的分别&#xff0c;分治方法将原问题划分为互不相交的子问题&#xff0c;而后将子问题组合…

【刷题日记】动态规划经典题目

&#x1f600;大家好&#xff0c;我是白晨&#xff0c;一个不是很能熬夜&#x1f62b;&#xff0c;但是也想日更的人✈。如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一下&#x1f440;白晨吧&#xff01;你的支持就是我最大的动力&#xff01;&#x1f4…

Linux命令-sftp文件传输

搭建SFTP服务详见博文&#xff1a;https://blog.csdn.net/cen50958/article/details/90722874 连接SFTP 可使用&#xff1a;sftp --help 查看SFTP的连接参数 [rootstudy ~]# sftp --help usage: sftp [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config] [-o ssh_option…

Linux命令(三):SFTP

目录 1、登录 2、文件上传 3、文件下载 4、删除文件/文件夹 5、实战 1、登录 sftp userip 你要用sftp, 当然得登录到sftp服务器&#xff0c; 在linux的shell中执行上面的命令后&#xff0c; linux shell会提示用户输入密码&#xff0c; 我们就输入password吧。 这样就成功…

Linux常用命令——sftp命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) sftp 交互式的文件传输程序 补充说明 sftp命令是一款交互式的文件传输程序&#xff0c;命令的运行和使用方式与ftp命令相似&#xff0c;但是&#xff0c;sftp命令对传输的所有信息使用ssh加密&#xff0c;它还…

SFTP命令常用操作

SFTP相关(等价于rz/sz&#xff0c;此方式适用于没有工具的情况下&#xff0c;前提是保证sftp默认端口22开放) lcd 本地文件路径 进入到本地的某个目录下 cd 远程文件路径 进入到远程的某个目录下 lpwd 显示本地的当前目录的路径 pwd 显示远程的当前目录的路径 这里只介绍常…