零拷贝:原理与实现

news2024/9/23 9:35:25

在这里插入图片描述

1. 简介

I/O 或输入/输出通常意味着中央处理器 (CPU) 与外部设备如磁盘、鼠标、键盘等)之间的读写。在深入研究零拷贝之前,有必要指出磁盘 I/O包括磁盘设备和其他块导向设备)和网络 I/O 之间的区别。

磁盘 I/O 的常用接口是 read()、write() 和 seek()。同时,网络 I/O 的接口通常是套接字相关接口。套接字接口背后发生的事情是发送方和接收方计算机创建自己的套接字,并设置发送或接收文件的连接。如今,越来越多的 Web 应用程序已经实现了从 CPU 绑定到 I/O 绑定的转变,这意味着 I/O 的性能通常是这些应用程序的瓶颈。

一般来说,用户不能直接在内核操作任何数据,包括读写。数据必须从内核复制到用户内存,而这个操作必须由 CPU 来完成,因此带来了很大的性能损失。这时零拷贝就派上用场了。零拷贝的主要原理是尽可能地消除或减少 CPU 在用户内存和内核内存之间的数据复制,从而减少相应的中断和模式切换次数,从而提高网络 I/O 的 I/O 性能。

2. DMA(直接存储器访问) 的缺陷

直接内存访问(DMA)是一个好主意,在减轻 CPU 负载和避免直接将数据从磁盘复制到磁盘方面效果很好。但仍有改进的空间。
在这里插入图片描述图 1. 使用 DMA 的 read() 时序图

典型的 read() 过程如图 1 所示。

  1. 首先,当应用程序调用 read() 命令时,会发生从用户模式到内核模式的切换。然后 CPU 发起 DMA 传输,DMA 发起从磁盘的 I/O 传输。要完成此 DMA 传输,数据应传输到磁盘缓存,以便 DMA 能够将数据传输到内核缓冲区。
  2. 然后 DMA 中断 CPU 执行其他操作以发出传输完成信号。然后 CPU 将数据从内核缓冲区传输到用户缓冲区。
  3. 最后,发生从内核到用户的另一种模式切换并将数据返回给应用程序。

现在,我们来看一个更复杂的例子,从磁盘读取数据并将其写入互联网接口。此操作可能是客户端-服务器模式下 Web 应用程序中最常见的操作之一。
在这里插入图片描述在这个例子中,首先执行 read() 命令并导致 CPU 模式切换,然后触发 DMA 数据从磁盘复制到内核缓冲区。然后 CPU 负责将数据复制到用户缓冲区,并将另一个模式从内核切换到用户。 write() 命令对网络接口和套接字缓冲区执行类似操作。CPU 将数据从用户缓冲区复制到套接字缓冲区并生成用于传输的头和尾信息,然后进行模式切换。然后 DMA 将数据复制到网络接口。最后,模式切换回用户模式。

这个过程绝对不能说是高效的,因为总共四次数据复制,四次模式切换,确实增加了系统的负荷,降低了响应效率。

3. 零拷贝原理

前面已经说了 DMA 的局限性,在 DMA 的帮助下,即使 CPU 不需要进行任何计算,也需要进行两次 CPU 复制,以及四次模式切换。零拷贝的目的很简单,就是消除或减少 CPU 在内核缓冲区和用户缓冲区之间不必要的数据复制,从而减少模式切换,从而实现性能的提升。

零拷贝是一个通用概念,也是一组实现的通用名称。多年来,人们一直在探索和改进它的实现。对于这个项目,我将深入研究一些流行的实现。

4. 零拷贝​​的实现

4.1. 使用 mmap()

在操作系统中,虚拟内存 (VM) 通过分页表映射到物理内存。多个 VM 地址可以映射到单个物理内存地址。此实现的思想是将用户虚拟内存的地址映射到内核内存的地址。这样 CPU 就不必来回复制数据。mmap() 代表内存映射。它是一个系统调用,可以将内核缓冲区中的数据映射到用户内存。幕后发生的事情是内核内存和用户内存中的虚拟地址指向物理地址的同一位置(共享内存)。
在这里插入图片描述图 3. 使用 mmap() 从磁盘读取到网络接口

如图 3 所示,应用程序调用 mmap() 而不是 read()。由于用户内存和内核内存共享相同的物理内存地址,内核缓冲区中的数据将由 CPU 直接复制到套接字缓冲区。在这种情况下,没有数据复制到用户内存。

使用 mmap(),我们在本例中成功摆脱了一次 CPU 复制。但我们仍然需要四次模式切换、三次数据复制和昂贵的 VM 映射操作。还有进一步改进的空间。

4.2. 使用 sendfiles()

Linux 内核 2.1 为我们提供了一个新的系统调用 sendfile(),用于替代 read() 和 write(),用于某些类似上述示例的用例。只需一个系统调用而不是两个,我们就可以省去两次模式切换。
在这里插入图片描述图 4. 使用 sendfile() 从磁盘读取到网络接口

图 4 显示了同一个示例的过程,但使用 sendfile() 实现零拷贝。应用程序这次调用 sendfile() 系统调用。DMA 将数据复制到内核缓冲区,然后数据由 CPU 复制到套接字缓冲区。与第一种实现相比,使用 sendfile() 为我们带来了两次模式切换、三次数据复制(包括一次 CPU 复制)。

4.3. 使用 sendfiles() 和 DMA Gather

Linux 2.4 对 sendfile() 系统调用进行了一些改进,其中最重要的就是 DMA Scatter/Gather 的出现。通过这项改进,我们终于可以消除上述场景中的所有 CPU 拷贝,实现真正的零拷贝(Zero-copy)
在这里插入图片描述图 5. 使用 sendfile() 和 DMA 收集从磁盘读取到网络接口

图 5 显示了该过程。当应用程序调用 sendfile() 时,DMA 控制器通过 DMA scatter 将数据从磁盘复制到内核缓冲区,这意味着您不需要连续的内存空间来存储数据。然后 CPU 将文件描述符附加到套接字缓冲区,DMA 控制器生成相应的网络数据包的头和尾。最后,DMA 控制器按照套接字缓冲区上的描述,复制数据,然后将数据包从内核缓冲区发送到网络接口进行网络传输。

5. Java零拷贝实验

现在我们对 Zero-copy 有了大致的了解,包括它的目的、原理和多种实现方法。在这一部分,我评估了使用 Zero-copy 对性能的提升。在 Java 中,FileChannel 类中有 API 分别使用了 mmap() 和 sendfile() 的机制。在本次实验中,我使用了三种方法在 Ubuntu 22.04 上复制了一个 880 MB 的文件。结果如下。

private static void sendfileCopyFile(String inputFilePath, String outputFilePath) {

    long start = System.currentTimeMillis();

    try (
            FileChannel channelIn = new FileInputStream(inputFilePath).getChannel();
            FileChannel channelOut  = new FileOutputStream(outputFilePath).getChannel();
    ) {
        channelIn.transferTo(0, channelIn.size(), channelOut);

    } catch (IOException e) {
        e.printStackTrace();
    }

    long end = System.currentTimeMillis();
    System.out.println("Total time spent: " + (end - start));
}

private static void mmapCopyFile(String inputFilePath, String outputFilePath) {

    long start = System.currentTimeMillis();

    try (
            FileChannel channelIn = new FileInputStream(inputFilePath).getChannel();
            FileChannel channelOut = new RandomAccessFile(outputFilePath, "rw").getChannel();

    ) {
        long size = channelIn.size();
        MappedByteBuffer mbbi = channelIn.map(FileChannel.MapMode.READ_ONLY, 0, size);
        MappedByteBuffer mbbo = channelOut.map(FileChannel.MapMode.READ_WRITE, 0, size);
        for (int i = 0; i < size; i++) {
            byte b = mbbi.get(i);
            mbbo.put(i, b);
        }

    } catch (Exception e) {
        e.printStackTrace();
    }

    long end = System.currentTimeMillis();
    System.out.println("Total time spent: " + (end - start));
}

private static void bufferInputStreamCopyFile(String inputFilePath, String outputFilePath) {

    long start = System.currentTimeMillis();
    try(
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(inputFilePath));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outputFilePath));
    ){
        byte[] buf = new byte[1];
        int len;
        while ((len = bis.read(buf)) != -1) {
            bos.write(buf);
        }

    }catch(Exception e){
        e.printStackTrace();
    }
    long end = System.currentTimeMillis();
    System.out.println("Total time spent: " + (end - start));
}

在这里插入图片描述
图 6. 使用 Java 11 进行性能评估的结果

六, 结论

对于这个项目,我介绍了零拷贝的几乎所有基本元素。从其背景到设计原理,然后我详细介绍了业界最流行的三种实现,并附上了示意图。

在实验部分,我使用 java 来模拟数据传输的过程。从结果来看,两种类型的零拷贝实现都​​有显着的改进。mmap() 的速度提高了 81%,sendfile() 的速度提高了 91%,这回答了为什么人们在业界大量使用这种技术的问题。同时,也有需要改进的空间,例如映射操作的高成本、sendfile() 的用例有限以及空间限制。

希望我们将来能看到更多关于它们的改进。这个项目我介绍了零拷贝的几乎所有基本元素,从背景到设计原理,然后详细介绍了业界最流行的三种实现方式,并附上了示意图。在实验部分,我用java模拟了数据传输的过程。

从结果来看,两种零拷贝实现方式都有显著的提升。mmap()的速度提升了81%,sendfile()的速度提升了91%,这也回答了为什么业界如此热衷于使用这种技术。但同时也存在一些需要改进的地方,比如映射操作的开销大、sendfile()的使用场景有限、空间受限等。希望未来我们能看到更多关于这方面的改进。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1993807.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue3简单介绍和快速体验

目录 前言 1. Vue3介绍 1.1 Vue的两个核心功能&#xff1a; 1.2 Vue作者介绍 2. Vue3快速体验(非工程化方式) 2.1 所有代码 2.2 导入js 2.3 一些基本指令 2.4 app对象的创建和挂载 ​ 前言 在学习Vue3之前,自己已经学习过的vue2了,在学习Vue3后还是能感觉到2和3在使用…

媒体资讯视频数据采集-yt-dlp-python实际使用-下载视频

对于视频二创等一些业务场景&#xff0c;可能要采集youtube等的相关媒体视频资源&#xff0c;使用[yt-dlp](https://github.com/yt-dlp/yt-dlp)是一个不错的选择&#xff0c;提供的命令比较丰富&#xff0c;场景比较全面yt-dlp 是一个用 Python 编写的命令行工具&#xff0c;主…

数据结构和算法|递归算法那些事(递归算法的时间复杂度和尾递归优化)

对于文章的第一部分&#xff0c;递归算法的时间复杂度&#xff0c;来自于代码随想录文章:通过一道面试题目&#xff0c;讲一讲递归算法的时间复杂度&#xff01; 对于第二节尾递归优化来自于B站&#xff1a;尾递归优化&#xff1a;你的递归调用是如何被优化的&#xff1f; 关于…

什么是占空比?什么是周期?什么是频率?

一、什么是占空比&#xff1f; 占空比(Duty Cycle)是脉冲信号中高电平持续时间与整个周期时间的比率。它通常用于描述脉冲宽度调制(PWM)信号&#xff0c;其中信号在一定频率下在高电平和低电平之间切换。 图1.1 60%占空比信号 占空比计算公式如下&#xff1a; 脉冲高电平持续时…

护眼首选,一线智联!AOC Q27E12C商用显示器,摆脱束缚高效办公!

摘要&#xff1a;QHD广色域护眼显示器&#xff0c;减负提效商务利器&#xff01; 不管是日常处理报表、制作方案还是进行文字创作、设计剪辑&#xff0c;都离不开商用显示器的身影。相较于传统显示器&#xff0c;商用显示器往往更有助于减负提效&#xff0c;提高企业生产力&am…

开源24Mhz采样逻辑分析仪-信息搜集

引用&#xff1a; 开源4Mhz采样逻辑分析仪-信息搜集&#xff1a; Sigrok逻辑分析仪软件(基于CY7C68013A)-CSDN博客 迷你逻辑分析仪TYPE C接口单片机ARM FPGA调试工具24M采样8通道_逻辑分析仪24m能测stm32吗-CSDN博客 nanoDLA逻辑分析仪上手教程-CSDN博客 基于cy7c68013的逻…

嵌入式初学-C语言-练习四

一维数组练习题 1.键盘录入一组数列&#xff0c;利用冒泡排序将数据由大到小排序 代码&#xff1a; 1 /*2 需求&#xff1a;一维数组案例-冒泡排序3 */4 #include <stdio.h>5 6 int main()7 {8 //创建一个数组&#xff0c;用来存储排序的数列9 int arr[10];1…

Redis远程字典服务器(2) —— 全局命令

一&#xff0c;使用官方文档 学会使用文档&#xff0c;是一个优秀程序员的必备技能。Redis的命令非常多&#xff08;上百个&#xff09;&#xff0c;因为Redis是通过键值对存储数据的&#xff0c;key为string类型&#xff0c;但是value可以是其它的数据类型&#xff08;字符串…

javascript-动态增加和删除表格的行

本地环境&#xff1a;win10 / centos6 &#xff0c; python3 实现效果 点击添加峰图按钮即可增加一行&#xff0c;点击每行右侧的删除按钮即可删除行。 初始状态&#xff1a; 点击后&#xff1a; 实际生成的html内容类似下图&#xff0c;可以看到&#xff0c;只有id这样需要…

VMware capacity mismatch for disk错误解决办法

因为业务或者学习很多时候我们要用到虚拟机&#xff0c;当我们使用vmware去打开VirtualBox导出的文件虚拟机镜像时会发生capacity mismatch for disk错误&#xff0c;同时在网上查询没有完整通俗易懂的解决方案&#xff0c;我本人也遇到了这个错误&#xff0c;经过测试以后写下…

arcgis server 发布地图服务相关问题

地图类型选择feature access 发布后&#xff0c;存在个别图层失败&#xff0c; 配置符号后&#xff0c;发布地图服务时&#xff0c;选择了Map Server 和Feature Access后&#xff0c;地图发布成功&#xff0c;但是对于feature server 类型中部分图层失败&#xff0c;在服务目录…

【题解】【数学】—— [CSP-J2019 江西] 次大值

【题解】【数学】—— [CSP-J2019 江西] 次大值 [CSP-J2019 江西] 次大值题目描述输入格式输出格式输入输出样例输入 #1输出 #1输入 #2输出 #2输入 #3输出 #3 提示 解法1.暴力枚举(非正解)1.1.题意分析1.2.代码 解法2.总结规律(正解)2.1.题意分析2.1.1.解题思路2.2.2.功能拆解 …

5个适用于Linux系统的PDF转Word工具

凭借其跨平台和设备的统一标准、兼容性和规模小巧等主要优点&#xff0c;可携带文档格式&#xff08;PDF&#xff09;可谓最主流的文件格式之一。 市面上有许多查看PDF文件的强大工具&#xff0c;因此所有Linux系统的用户都可以根据自身喜好找到合适的PDF查看工具。然而&#x…

三星、小米和 OPPO设备实验室将采用Android设备流技术

早在 5 月份的年度开发者大会上&#xff0c;Google就发布了 Android 设备流测试版。开发人员可以在Google数据中心的真实物理设备上更轻松、更互动地测试自己的应用程序&#xff0c;这些设备会直接串流到 Android Studio。今天&#xff0c;Google宣布与三星、小米和 OPPO 合作扩…

染色法+组合数学,CF 557D - Vitaly and Cycle

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 557D - Vitaly and Cycle 二、解题报告 1、思路分析 t 的取值范围为&#xff1a;[0, 3] 0&#xff1a;本身有奇环&#xff0c;样例说明方案为1 1&#xff1a;存在偶数长度路径&#xff0c;连接一下就行&a…

【数据结构】顺序表实现

0. 前言 小伙伴们大家好&#xff0c;从今天开始&#xff0c;我们就开始学习《数据结构》这门课程~ 首先想给大家讲讲什么是数据结构&#xff1f; 0.1 数据结构是什么&#xff1f; 数据结构是由“数据”和“结构”两词组合⽽来。 什么是数据&#xff1f; 比如常⻅的数值1、…

Visionpro二次开发学习笔记8-如何将静态或交互式图形添加到CogDisplay中

如何将静态或交互式图形添加到CogDisplay中 CogDisplay支持两种类型的图形&#xff1a;静态图形和交互式图形。静态图形一旦添加到显示中&#xff0c;就无法移动或更改。静态图形不能添加提示文本&#xff08;TipText&#xff09;。 交互式图形在启用图形的交互属性时&#x…

【数据结构】-----二叉搜索树(C++)

目录 前言 一、是什么 ​编辑 二、实现 Ⅰ、结点类 Ⅱ、结构及基本接口实现 ​编辑 ①插入 ②查找 ③删除(重难点) 情况一&#xff1a;待删除结点为叶子结点(无孩子) 情况二&#xff1a;待删除结点存在一个孩子结点(可能左/右) 情况三&#xff1a;待删除结点存在…

【iOS】—— 事件传递链和响应者链总结

事件传递链和响应者链总结 1. 事件传递链&#xff1a;事件传递链&#xff1a;传递流程&#xff1a;总结第一响应者&#xff1a; 2. 响应者链响应者链传递流程总结响应者链流程 总结&#xff1a; 之前也学习过这个内容这次在复习的时候&#xff0c;就想着写一下总结&#xff1a;…

Linux部署python3.0版本——及基本操作

&#xff08;一&#xff09;部署环境 首先查看列表&#xff0c;找到python3.0的包 yum list installed|grep python 如果没有&#xff0c;是因为yum源的问题&#xff0c;可部署阿里云镜像然后下载epel包&#xff0c;这里的内容可参考前面的阿里云镜像部署 然后进行下载 yum…