一文彻底弄懂零拷贝原理

2023-11-19

零拷贝

零拷贝(Zero-Copy)是一种 I/O 操作优化技术,可以快速高效地将数据从文件系统移动到网络接口,而不需要将其从内核空间复制到用户空间。其在 FTP 或者 HTTP 等协议中可以显著地提升性能。但是需要注意的是,并不是所有的操作系统都支持这一特性,目前只有在使用 NIOEpoll 传输时才可使用该特性。

需要注意,它不能用于实现了数据加密或者压缩的文件系统上,只有传输文件的原始内容。这类原始内容也包括加密了的文件内容。

传统I/O操作存在的性能问题

如果服务端要提供文件传输的功能,我们能想到的最简单的方式是:将磁盘上的文件读取出来,然后通过网络协议发送给客户端。

传统 I/O 的工作方式是,数据读取和写入是从用户空间到内核空间来回复制,而内核空间的数据是通过操作系统层面的 I/O 接口从磁盘读取或写入。

代码通常如下,一般会需要两个系统调用:

read(file, tmp_buf, len);
write(socket, tmp_buf, len);

代码很简单,虽然就两行代码,但是这里面发生了不少的事情。

首先,期间共发生了 4 次用户态与内核态的上下文切换,因为发生了两次系统调用,一次是 read() ,一次是 write(),每次系统调用都得先从用户态切换到内核态,等内核完成任务后,再从内核态切换回用户态。

上下文切换到成本并不小,一次切换需要耗时几十纳秒到几微秒,虽然时间看上去很短,但是在高并发的场景下,这类时间容易被累积和放大,从而影响系统的性能。

其次,还发生了 4 次数据拷贝,其中两次是 DMA 的拷贝,另外两次则是通过 CPU 拷贝的,下面说一下这个过程:

  • 第一次拷贝,把磁盘上的数据拷贝到操作系统内核的缓冲区里,这个拷贝的过程是通过 DMA 搬运的。
  • 第二次拷贝,把内核缓冲区的数据拷贝到用户的缓冲区里,于是我们应用程序就可以使用这部分数据了,这个拷贝到过程是由 CPU 完成的。
  • 第三次拷贝,把刚才拷贝到用户的缓冲区里的数据,再拷贝到内核的 socket 的缓冲区里,这个过程依然还是由 CPU 搬运的。
  • 第四次拷贝,把内核的 socket 缓冲区里的数据,拷贝到网卡的缓冲区里,这个过程又是由 DMA 搬运的。

这种简单又传统的文件传输方式,存在冗余的上文切换和数据拷贝,在高并发系统里是非常糟糕的,多了很多不必要的开销,会严重影响系统性能。

所以,要想提高文件传输的性能,就需要减少「用户态与内核态的上下文切换」和「内存拷贝」的次数

零拷贝技术原理

零拷贝主要是用来解决操作系统在处理 I/O 操作时,频繁复制数据的问题。关于零拷贝主要技术有 mmap+writesendfilesplice等几种方式。

虚拟内存

在了解零拷贝技术之前,先了解虚拟内存的概念。

所有现代操作系统都使用虚拟内存,使用虚拟地址取代物理地址,主要有以下几点好处:

  • 多个虚拟内存可以指向同一个物理地址。
  • 虚拟内存空间可以远远大于物理内存空间。

利用上述的第一条特性可以优化,可以把内核空间和用户空间的虚拟地址映射到同一个物理地址,这样在 I/O 操作时就不需要来回复制了。

如下图展示了虚拟内存的原理。

image-20210812181924274

mmap/write 方式

使用mmap/write方式替换原来的传统I/O方式,就是利用了虚拟内存的特性。下图展示了mmap/write原理:

image-20210812201839908

整个流程的核心区别就是,把数据读取到内核缓冲区后,应用程序进行写入操作时,直接把内核的Read Buffer的数据复制到Socket Buffer以便写入,这次内核之间的复制也是需要CPU的参与的。

上述流程就是少了一个 CPU COPY,提升了 I/O 的速度。不过发现上下文的切换还是4次并没有减少,这是因为还是要应用程序发起write操作。

那能不能减少上下文切换呢?这就需要sendfile方式来进一步优化了。

sendfile 方式

从 Linux 2.1 版本开始,Linux 引入了 sendfile来简化操作。sendfile方式可以替换上面的mmap/write方式来进一步优化。

sendfile将以下操作:

  mmap();
  write();

替换为:

 sendfile();

这样就减少了上下文切换,因为少了一个应用程序发起write操作,直接发起sendfile操作。

下图展示了sendfile原理:

image-20210812201905046

sendfile方式只有三次数据复制(其中只有一次 CPU COPY)以及2次上下文切换。

那能不能把 CPU COPY 减少到没有呢?这样需要带有 scatter/gathersendfile方式了。

带有 scatter/gather 的 sendfile方式

Linux 2.4 内核进行了优化,提供了带有 scatter/gather 的 sendfile 操作,这个操作可以把最后一次 CPU COPY 去除。其原理就是在内核空间 Read BUffer 和 Socket Buffer 不做数据复制,而是将 Read Buffer 的内存地址、偏移量记录到相应的 Socket Buffer 中,这样就不需要复制。其本质和虚拟内存的解决方法思路一致,就是内存地址的记录。

下图展示了scatter/gather 的 sendfile 的原理:

image-20210812201922193

scatter/gather 的 sendfile 只有两次数据复制(都是 DMA COPY)及 2 次上下文切换。CUP COPY 已经完全没有。不过这一种收集复制功能是需要硬件及驱动程序支持的。

splice 方式

splice 调用和sendfile 非常相似,用户应用程序必须拥有两个已经打开的文件描述符,一个表示输入设备,一个表示输出设备。与sendfile不同的是,splice允许任意两个文件互相连接,而并不只是文件与socket进行数据传输。对于从一个文件描述符发送数据到socket这种特例来说,一直都是使用sendfile系统调用,而splice一直以来就只是一种机制,它并不仅限于sendfile的功能。也就是说 sendfile 是 splice 的一个子集。

在 Linux 2.6.17 版本引入了 splice,而在 Linux 2.6.23 版本中, sendfile 机制的实现已经没有了,但是其 API 及相应的功能还在,只不过 API 及相应的功能是利用了 splice 机制来实现的。

和 sendfile 不同的是,splice 不需要硬件支持。

总结

无论是传统的 I/O 方式,还是引入了零拷贝之后,2 次 DMA copy是都少不了的。因为两次 DMA 都是依赖硬件完成的。所以,所谓的零拷贝,都是为了减少 CPU copy 及减少了上下文的切换。

下图展示了各种零拷贝技术的对比图:

CPU拷贝 DMA拷贝 系统调用 上下文切换
传统方法 2 2 read/write 4
内存映射 1 2 mmap/write 4
sendfile 1 2 sendfile 2
scatter/gather copy 0 2 sendfile 2
splice 0 2 splice 0
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一文彻底弄懂零拷贝原理 的相关文章

  • 无法禁用 Firestore 中的离线数据

    从我的数据中删除数据后Firestore Database 这需要我的Android app一段时间后才意识到数据已被删除 我认为这是由于自动数据缓存而发生的 我的应用程序与离线使用无关 我想禁用此功能 我已将其添加到我的自定义中Appli
  • 如何注入“运行时”依赖项,例如登录用户,该依赖项在应用程序启动时不可用?

    我只是不明白这个 我在我的 java GWT 应用程序中使用 Gin 来进行 DI 登录屏幕集成到完整的应用程序窗口中 用户登录后 我想将用户对象注入到我创建的其他类 例如 GUI Presenters 中 因此我相信存在某种运行时依赖性
  • 在 libgdx 中批处理多维数据集时出现问题

    我正在尝试开发一款游戏 在屏幕上渲染多达 300 个立方体 为每个多维数据集创建新的 modelInstance 时 modelBatch 的性能非常糟糕 据我所知 没有 3d 批处理可以将所有立方体批处理到一次绘制调用 所以我拼命地尝试以
  • Netbeans 雷达插件配置

    我使用的是 Netbeans 8 0 1 在提交到 SVN 之前 我需要从 IDE 运行并检查 SonarQube 分析 我已经安装了 Netbeans Radar 插件 用于启动本地分析并检查结果 这个插件有一个名为 Get Issues
  • rmi类找不到异常

    我使用 java rmi 编写了一个简单的项目并导出到可执行 jar 文件 当我尝试运行它时 有时会出现异常 有时会起作用 当我指定 Djava rmi server codebase file serverClasses 时 它似乎没有正
  • 当Java中set已经是原子的时候,为什么我们还需要compareAndSet呢?

    因为原子意味着线程安全 当 set 本身在java中是原子和线程安全的时候 我们什么时候使用compareAndSet 举例来说 我想以原子方式设置一个变量 以便每个其他线程都可以看到它 但我希望以线程安全的方式设置该变量 我可以简单地将其
  • 如何用 Java 制作 Windows 7 工具提示

    我一直在网上到处寻找 但没有找到这个小问题的答案 在 Windows 7 中 我认为在 Vista 中 您有一个漂亮的圆形银色工具提示 它看起来比旧的黄色盒装蹩脚工具提示要好得多 下面的 How do I make a Windows 7
  • Runtime.getRuntime().exec(cmd) 挂起

    我正在执行一个命令 该命令返回文件的修订号 文件名 但如果执行命令时出现问题 应用程序就会挂起 我可以做什么来避免这种情况 请在下面找到我的代码 String cmd cmd C si viewhistory fields revision
  • 控制启动时的竞争条件

    我有一些代码想要执行一些一次性初始化 但这段代码没有明确的生命周期 因此在初始化完成之前 我的逻辑可能会被多个线程调用 所以 我想基本上确保我的逻辑代码 等待 直到初始化完成 这是我的第一次剪辑 public class MyClass p
  • 无法从 PDFA1-a 格式文档中提取图像

    我正在使用以下代码从 PDFA1 a 格式的 pdf 中提取图像 但我无法获取图像 List
  • 如何在 HashiCorp Vault 中安全地存储 Spring Boot 应用程序的机密?

    我已阅读以下教程 保险库配置 https spring io guides gs vault config 好的 我们安装了 Vault 服务器并放置了 2 对秘密属性 vault kv put secret gs vault config
  • 在 XSSF 工作簿上设置密码保护

    我想为使用 poi 3 14 创建的 xlsx 文件添加密码保护 该文档声称 这是可能的 http poi apache org cryption html http poi apache org encryption html 使用我尝试
  • 使用 JPA 标准的“不在”约束

    我正在尝试写一个NOT IN约束使用JPA Criteria 我尝试过这样的事情 builder not builder in root get property1 虽然我知道这行不通 在上面的语法中 如何添加集合 列表property1会
  • CompletableFuture SupplyAsync

    我刚刚开始探索 Java 8 的一些并发特性 让我有点困惑的一件事是这两个静态方法 CompletableFuture
  • 错误:类 kotlin.reflect.jvm.internal.FunctionCaller$FieldSetter

    我已尝试一切方法来消除此错误 但它不断出现 Class kotlin reflect jvm internal FunctionCaller FieldSetter can not access a member of class com
  • 在Linux中执行jar文件[关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我创建了一个可执行的 Java jar 文件 也就是说 我将 java 程序正确打包到 jar 文件中 包括 META INF MANIFEST 文件
  • GSSAPI 中的 javax.naming.AuthenticationException

    我正在尝试使用 JAVA GSSAPI 执行 NTLM 绑定 我收到此错误 javax naming AuthenticationException GSSAPI 根异常是 javax security sasl SaslException
  • 何时对字符串文字使用 intern()

    我看到很多这样的遗留代码 class A public static final String CONSTANT value intern 我看不出使用 intern 的任何原因 因为在 Javadoc 中可以读到 所有文字字符串和字符串值
  • Java:易失性足以使类线程安全?

    我有一个关于 Java 中 volatile 语句的问题 请看这个构造的例子 class Master Foo is a class with thread safe methods public volatile Foo foo clas
  • Java Media API:java media api 下载

    我在哪里可以找到javax media jar 文件 在sun站点它下载一个安装程序 有没有可用的java媒体jar 没有 javax media 具体是 jar 文件 该包位于 jmf jar 文件中 您需要运行安装程序并取出 jar 或

随机推荐

  • 1.Cesium介绍及环境配置

    前言 鸽了半年 flag立的太多 稿子存了100多篇 都没有开始排版整理 这些天正好学习cesium 决定每天更新一篇 提提神 一 Cesium简介 Cesium是一个用于显示三维地球的开源库 旨在释放3D数据的力量 它基于WebGL技术
  • Kmeans K均值聚类,OpenCV实现

    Clustering 聚类 kmeans k均值聚类 Finds centers of clusters and groups input samples around the clusters 寻找clusters的中心 并且将输入的样本
  • java 管程

    管程即Monitor 监视器 也叫锁 Monitor其实是一种同步机制 保证只有一个线程可以访问被保护的数据和代码 JVM中同步是基于进入和退出监视器对象 Monitor 来实现的 每个对象实例都会有一个Monitor对象 和java对象一
  • public static void main(String[] args) { //填入通过分享获取到的抖音视频地址 String videoUrl = getVid...

    这段代码的作用是从抖音 douyin 分享链接中获取视频的无水印播放地址 首先 它通过调用 HttpRequest get url 方法获取抖音视频的分享页面的 HTML 源代码 然后 通过调用 sub 方法并传入 HTML 源代码 开始字
  • 华为面试之Hr面,这个套路把我坑惨了......

    作为技术类的测试工程师面试 往往要经过多次面试才能拿到心仪的offer 这里面有技术一面 二面 甚至总监面等 还有一个必不可少的就是HR面 一般HR会出现在你面试的最前面和最后面 前面是了解你的基本情况 后面就是你已经通过了技术面试 他是来
  • Python3多进程(mutiprocessing)

    和Threading的比较 多进程 Multiprocessing 和多线程 threading 类似 他们都是在 python 中用来并行运算的 不过既然有了 threading 为什么 Python 还要出一个 multiprocess
  • linux文件权限说明(drwxr-xr-x)

    在linux中查看文件的时候 在每一行的最前面会有这样的一串字符 drwxr x 如下面的样例 drwxr x 2 root root 37 Apr 18 10 50 data rw r r 1 root root 68549 Sep 26
  • 95-34-030-Context-DefaultChannelHandlerContext

    文章目录 1 概述 2 继承体系 3 源码 1 概述 2 继承体系 3 源码 final class DefaultChannelHandlerContext
  • 写个剧本,关于风花雪夜的

    起 一个寒冷的晚上 一位年轻的男子走在冰冷的雪地上 他只带着一把手电筒和一双雪靴 行走在漆黑的夜晚 细节 他看到了一片白色的风景 突然 一股强烈的风吹过 让他感觉到了一股温暖 他看到了一片花海 花朵们被风吹得起舞 像美丽的舞蹈一样 它们的芳
  • 错误码:events.js:141 throw er; // Unhandled ‘error’ event—解决办法

    错误码 events js 141 throw er Unhandled error event 解决办法 具体错误信息 dev events js 141 throw er Unhandled error event Error list
  • web做题记录(buuoj,jarvis,攻防世界,bugku,hackme)

    web做题笔记 文章目录 web做题笔记 buuoj easy tornado 随便注 warmup easysql 高明的黑客 jarvisoj re flag在管理员手上 api调用 chopper 图片上传漏洞 inject web
  • 福禄克DSX2-5000/8000 CH测试结果中为什么标记蓝色“i”?

    我们在使用福禄克线缆测试仪DSX2 5000 CH DSX2 8000 CH的过程中遇见过很多测试结果的测试参数后面标记着蓝色的 i information 而不是失败的红色叉号 也不是Pass的绿色对号 这是因为所选测试限制没有该测试的限
  • Django

    HTTP无状态协议 是指协议对于交互性场景没有记忆能力 每次客户端检索网页时 客户端打开一个单独的连接到 Web 服务器 服务器会自动不保留之前客户端请求的任何记录 创建用户对象的三种方法 create 创建一个普通用户 密码是明文的 cr
  • React-(4)React中的事件绑定

    React中的事件绑定 在 React 组件中 每个方法的上下文都会指向该组件的实例 即自动绑定 this 为当前组件 而且 React 还会对这种引用进行缓存 以达到 CPU 和内存的最优化 在使用 ES6 classes 或者纯函数时
  • vue3 + vite自定义封装vue + element-ui 表格组件,发布到npm包的全过程。

    一 前言 当我们项目中用到的表格太多的话 就会导致我们的代码量一直增加 所以我们要封装一个公共得组件 通过传参引入来使用 下面这篇文章主要给大家介绍了关于vue3 vite自定义封装vue组件发布到npm包的相关资料 需要的朋友可以参考下
  • 记一次悟空CRM ubuntu服务器环境搭建

    linux服务器创建用户并设置工作目录 为什么这么做 因为服务器root权限过大 如果误操作导致不可估计的后果 建议创建一个用户 在当前用户的home下运行项目 该用户在其他目录下没有权限 所以可防止误操作 并且很多第三方插件不允许直接用r
  • 安装docker guacamole

    环境 vmware centos 7 9 gucamole环境部署在centos中 其他机器通过浏览器访问 参考安装文档 Installing Guacamole with Docker Apache Guacamole Manual v1
  • C++——STL之list详解

    C STL之list详解 什么是list list的使用 splice unique remove sort list的实现 迭代器类 体会c 的优势 迭代器的构造 迭代器的模板参数 总结 先看这里 作者 江不平 博客 江不平的博客 学如逆
  • 操作系统PV操作及读者写者问题

    操作系统PV操作及读者写者问题 目录 1 信号量 2 P V操作原语可描述为以下式子 3 解释 4 互斥模式原理 5 同步模式原理 6 读者写者问题 1 信号量 PV操作与信号量的处理有关 信号量是表示资源的实体 是一个与队列有关的整型变量
  • 一文彻底弄懂零拷贝原理

    零拷贝 零拷贝 Zero Copy 是一种 I O 操作优化技术 可以快速高效地将数据从文件系统移动到网络接口 而不需要将其从内核空间复制到用户空间 其在 FTP 或者 HTTP 等协议中可以显著地提升性能 但是需要注意的是 并不是所有的操