IO系列(九) -什么是零拷贝

news2024/11/17 11:38:24

一、摘要

相信不少的网友,在很多的博客文章里面,已经见到过零拷贝这个词,会不禁的发出一些疑问,什么是零拷贝

从字面上我们很容易理解出,零拷贝包含两个意思:

  • 拷贝:就是指数据从一个存储区域转移到另一个存储区域。
  • 零:它表示拷贝数据的次数为 0。

合起来理解,零拷贝就是不需要将数据从一个存储区域复制到另一个存储区域。

果真是这样的吗

最早的零拷贝定义,来源于 Linux 系统的 sendfile 方法逻辑!

在 Linux 2.4 内核中,sendfile 系统调用方法,可以将磁盘数据通过 DMA 拷贝到内核态 Buffer 后,再通过 DMA 拷贝到 NIC Buffer(socket buffer),无需 CPU 拷贝,这个过程被称之为零拷贝。

从这段描述里面我们可以得知,站在操作系统的角度,零拷贝没有说不需要拷贝数据,而是省掉了 CPU 拷贝环节,减少了不必要的拷贝次数,提升数据拷贝效率

要想深度的了解这里面的原理,我们还得从 IO 拷贝机制说起!

二、IO 拷贝机制介绍

2.1、传统数据拷贝流程

以客户端从服务器下载文件为例,熟悉服务端开发的同学可能知道,服务端需要做两件事:

  • 第一步:从磁盘中读取文件内容
  • 第二步:将文件内容通过网络传输给客户端

事实上看似简单的操作,里面的流程却没那么简单,例如应用程序从磁盘中读取文件内容的操作,大体会经过以下几个流程:

  • 第一步:用户应用程序调用 read 方法,向操作系统发起 IO 请求,CPU 上下文从用户态转为内核态,完成第一次 CPU 切换
  • 第二步:操作系统通过 DMA 控制器从磁盘中读数据,并把数据存储到内核缓冲区
  • 第三步:CPU 把内核缓冲区的数据,拷贝到用户缓冲区,同时上下文从内核态转为用户态,完成第二次 CPU 切换

整个读取数据的过程,完成了 1 次 DMA 拷贝,1 次 CPU 拷贝,2 次 CPU 切换;反之写入数据的过程,也是一样的。

整个拷贝过程,可以用如下流程图来描述!

从上图,我们可以得出如下结论,4 次拷贝次数、4 次上下文切换次数。

  • 数据拷贝次数:2 次 DMA 拷贝,2 次 CPU 拷贝
  • CPU 切换次数:4 次用户态和内核态的切换

而实际 IO 读写,有时候需要进行 IO 中断,同时也需要 CPU 响应中断,拷贝次数和切换次数比预期的还要多,以至于当客户端进行资源文件下载的时候,传输速度总是不尽人意。

那有没有好的办法来提升资源拷贝的速度呢?

答案是肯定的,传统的数据拷贝流程还有很大的优化空间。

下面我们一起来看看几种其它的拷贝方式。

2.2、mmap 内存映射拷贝流程

mmap 内存映射的拷贝,指的是将用户应用程序的缓冲区和操作系统的内核缓冲区进行映射处理,数据在内核缓冲区和用户缓冲区之间的 CPU 拷贝将其省略,进而加快资源拷贝效率。

整个拷贝过程,可以用如下流程图来描述!

mmap 内存映射拷贝流程,从上图可以得出如下结论:

  • 数据拷贝次数:2 次 DMA 拷贝,1 次 CPU 拷贝
  • CPU 切换次数:4 次用户态和内核态的切换

整个过程省掉了数据在内核缓冲区和用户缓冲区之间的 CPU 拷贝环节,在实际的应用中,对资源的拷贝能提升不少。

2.3、Linux 系统 sendfile 拷贝流程

在 Linux 2.1 内核版本中,引入了一个系统调用方法:sendfile

当调用 sendfile() 时,DMA 将磁盘数据复制到内核缓冲区 kernel buffer;然后将内核中的 kernel buffer 直接拷贝到 socket buffer;最后利用 DMA 将 socket buffer 通过网卡传输给客户端。

整个拷贝过程,可以用如下流程图来描述!

Linux 系统 sendfile 拷贝流程,从上图可以得出如下结论:

  • 数据拷贝次数:2 次 DMA 拷贝,1 次 CPU 拷贝
  • CPU 切换次数:2 次用户态和内核态的切换

相比 mmap 内存映射方式,Linux 2.1 内核版本中 sendfile 拷贝流程省掉了 2 次用户态和内核态的切换,同时内核缓冲区和用户缓冲区也无需建立内存映射,对资源的拷贝能提升不少。

2.4、sendfile With DMA scatter/gather 拷贝流程

在 Linux 2.4 内核版本中,对 sendfile 系统方法做了优化升级,引入 SG-DMA 技术,需要 DMA 控制器支持。

其实就是对 DMA 拷贝加入了 scatter/gather 操作,它可以直接从内核空间缓冲区中将数据读取到网卡。使用这个特点来实现数据拷贝,可以多省去一次 CPU 拷贝。

整个拷贝过程,可以用如下流程图来描述!

Linux 系统 sendfile With DMA scatter/gather 拷贝流程,从上图可以得出如下结论:

  • 数据拷贝次数:2 次 DMA 拷贝,0 次 CPU 拷贝
  • CPU 切换次数:2 次用户态和内核态的切换

可以发现,sendfile With DMA scatter/gather 实现的拷贝,其中 2 次数据拷贝都是 DMA 拷贝,全程都没有通过 CPU 来拷贝数据,所有的数据都是通过 DMA 来进行传输的,这就是操作系统真正意义上的零拷贝(Zero-copy) 技术,相比其他拷贝方式,传输效率最佳。

2.5、Linux 系统 splice 零拷贝流程

在 Linux 2.6.17 内核版本中,引入了 splice 系统调用方法,和 sendfile 方法不同的是,splice 不需要硬件支持。

它将数据从磁盘读取到 OS 内核缓冲区后,内核缓冲区和 socket 缓冲区之间建立管道来传输数据,避免了两者之间的 CPU 拷贝操作。

整个拷贝过程,可以用如下流程图来描述!

Linux 系统 splice 拷贝流程,从上图可以得出如下结论:

  • 数据拷贝次数:2 次 DMA 拷贝,0 次 CPU 拷贝
  • CPU 切换次数:2 次用户态和内核态的切换

Linux 系统 splice 方法逻辑拷贝,也是操作系统真正意义上的零拷贝

三、IO 拷贝机制对比

从上面的 IO 拷贝机制可以看出,无论是传统 IO 方式,还是引入零拷贝之后,2次 DMA copy 是都少不了的,唯一的区别就是省掉 CPU 参与环节的方式不同。

以 Linux 系统为例,拷贝机制对比结果如下!

需要注意的地方是,零拷贝所有的方式,都需要操作系统支持,具体采用哪种方式,由操作系统来决定。

四、相关概念说明

上文中我们提到了几个很少见的名词,比如 DMA,用户空间,内核空间等。它们到底是什么意思呢?

4.1、DMA 介绍

DMA,英文全称是 Direct Memory Access,即直接内存访问。DMA 本质上是一块主板上独立的芯片,允许外设设备和内存存储器之间直接进行 IO 数据传输,其过程不需要 CPU 的参与。

4.2、内核空间和用户空间介绍

操作系统的核心是内核,与普通的应用程序不同,它可以访问受保护的内存空间,也有访问底层硬件设备的权限。

为了避免用户进程直接操作内核,保证内核安全,操作系统将虚拟内存划分为两部分,一部分是内核空间(Kernel-space),一部分是用户空间(User-space)。 在 Linux 系统中,内核模块运行在内核空间,对应的进程处于内核态;而用户程序运行在用户空间,对应的进程处于用户态。

内核空间总是驻留在内存中,它是为操作系统的内核保留的。应用程序是不允许直接在该区域进行读写或直接调用内核代码定义的函数。

当启动某个应用程序时,操作系统会给应用程序分配一个单独的用户空间,其实就是一个用户独享的虚拟内存,每个普通的用户进程之间的用户空间是完全隔离的、不共享的,当用户进程结束的时候,用户空间的虚拟内存也会随之释放。

同时处于用户态的进程不能访问内核空间中的数据,也不能直接调用内核函数的,如果要调用系统资源,就要将进程切换到内核态,由内核程序来进行操作。

五、Java 零拷贝实现介绍

Linux 提供的零拷贝技术,Java 并不是全部支持,目前只支持以下 2 种。

  • mmap(内存映射)
  • sendfile
5.1、Java NIO 对 mmap 的支持

Java NIO 有一个MappedByteBuffer的类,可以用来实现内存映射。它的底层是调用了 Linux 内核的mmap的 API。

实例代码如下:

public static void main(String[] args) {
    try {
        FileChannel readChannel = FileChannel.open(Paths.get("a.txt"), StandardOpenOption.READ);
        // 建立内存文件映射
        MappedByteBuffer data = readChannel.map(FileChannel.MapMode.READ_ONLY, 0, 1024 * 1024 * 40);
        FileChannel writeChannel = FileChannel.open(Paths.get("b.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
        // 拷贝数据
        writeChannel.write(data);

        // 关闭通道
        readChannel.close();
        writeChannel.close();
    }catch (Exception e){
        System.out.println(e.getMessage());
    }
}

其中MappedByteBuffer的作用,就是将内核缓冲区的内存和用户缓冲区的内存做了一个地址映射,读取小文件,效率并不高;但是读取大文件,效率很高。

5.2、Java NIO 对 sendfile 的支持

Java NIO 中的FileChannel.transferTo方法,底层调用的就是 Linux 内核的sendfile系统调用方法。Kafka 这个开源项目就用到它,平时面试的时候,如果面试官问起你为什么这么快,就可以提到sendfile零拷贝系统调用方法来回答。

实例代码如下:

public static void main(String[] args) {
    try {
        // 原始文件
        FileChannel srcChannel = FileChannel.open(Paths.get("a.txt"), StandardOpenOption.READ);
        // 目标文件
        FileChannel destChannel = FileChannel.open(Paths.get("b.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
        // 拷贝数据
        srcChannel.transferTo(0, srcChannel.size(), destChannel);

        // 关闭通道
        srcChannel.close();
        destChannel.close();
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
}

Java NIO 提供的FileChannel.transferTo并不保证一定能使用零拷贝。实际上是否能使用零拷贝与操作系统相关,如果操作系统提供sendfile这样的零拷贝系统调用方法,那么会充分利用sendfile零拷贝的优势,否则并不能实现零拷贝。

六、小结

本位主要围绕零拷贝的逻辑,结合网友的知识分享,进行一次知识整理和总结。

从上面的内容总结可以看出,所谓的零拷贝,其目的并不是说不需要拷贝数据,而是通过一些手段省略 CPU 拷贝环节,减少了不必要的拷贝次数,提升数据拷贝效率。

以 Linux 操作系统为例,真正意义上大家比较认可的零拷贝主要有 sendfile、splice 等方法,它们完全通过 DMA 控制器来实现数据的拷贝,无需 CPU 来参与数据拷贝的过程,这个过程被称为零拷贝

但是比较糟心的是,一些机构、团队,在产品推广中过度包装零拷贝这个概念,名曰“采用零拷贝,性能有多高等等…”,这样的宣传似乎会让很多人觉得很厉害。

作为一线技术者,应该多多深入了解,识透其真相,弄清楚是偏向于优化数据操作,还是真正切合场景、灵活运用了操作系统意义上的零拷贝,大家可以多深入分析。

七、参考

1、消失er - Java中的零拷贝

2、捡田螺的小男孩 - 看一遍就理解:零拷贝原理详解

八、写到最后

不会有人刷到这里还想白嫖吧?点赞对我真的非常重要!在线求赞。加个关注我会非常感激!

最近也无意间获得一份阿里大佬写的技术笔记,内容涵盖 Spring、Spring Boot/Cloud、Dubbo、JVM、集合、多线程、JPA、MyBatis、MySQL 等技术知识。需要的小伙伴可以点击如下链接获取,资源地址:技术资料笔记。

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

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

相关文章

记录贴 Elasticsearch的RestClient进行DSL查询

must:必须匹配每个子查询,类似“与” should:选择性匹配子查询,类似“或” must_not:必须不匹配,不参与算分,类似“非” filter:必须匹配,不参与算分 import cn.huto…

nginx+keepalived实现nginx高可用集群

实现nginx的高可用 一、简要介绍 ​ nginx作为一款企业级的代理服务器,在各种企业事业单位中,均有广泛的使用,尤其是在前后端分离的项目中,nginx作为路由转发的功能是非常常用的。在一些流量比较大的项目中,为了应对…

idea+tomcat+mysql 从零开始部署Javaweb项目(保姆级别)

文章目录 新建一个项目添加web支持配置tomcat优化tomcat的部署运行tomcatidea数据库连接java连接数据库 新建一个项目 new project;Java;选择jdk的版本;next;next;填写项目名字,选择保存的路径;…

raid 磁盘阵列

raid 磁盘阵列: **条带化存储:**数据分散在多个物理磁盘上的存储方式。利用多个磁盘并行读取和写入。 条带化的存储性能和读写性能是最好的。 磁盘的组成方式不同,也就是不同的raid级别: **raid 0(条带化存储&#x…

.net JQ AJAX 请求 FromBody 接收格式

$.ajax({ url: “/api/banchang/EmpTble/Login2”, type: ‘Post’, data: JSON.stringify({ Emppassword: pass, EmpName: name }), contentType: ‘application/json’, beforeSend: function () { // 禁用按钮防止重复提交 /* $(“#create”).attr({ disabled: “disabled”…

SpringBoot与Spring Framework提供的缓存抽象

目录 缓存 项目总结 新建一个SpringBoot项目 pom.xml application.properties CacheConfig Book BookRepository接口 BookService服务类 BookController控制器 SpringbootCacheApplication启动类 启动项目,使用Postman测试 参考博文: 1、使用…

短视频脚本创作的五个方法 沈阳短视频剪辑培训

说起脚本,我们大概都听过影视剧脚本、剧本,偶尔可能在某些综艺节目里听过台本。其中剧本是影视剧拍摄的大纲,用来指导影视剧剧情的走向和发展,而台本则是综艺节目流程走向的指导大纲。 那么,短视频脚本是什么&#xf…

服务器端请求伪造--SSRF

SSRF 简介 ##SSRF定义 SSRF(Server-Side Request Forgery:服务器端请求伪造)是一种由 攻击者构造形成,由服务端发起请求 的一个安全漏洞。一般情况下,SSRF攻击的目标是从 外网无法访问的内部系统(正是因为它是由服务端发起的,所…

FineReport帆软设计器,远程连接服务器

FineReport报表工具一款纯Java编写的企业级web报表软件工具。它能够全面支持主流的B/S架构以及传统的C/S架构,部署方式简单而灵活. 需要使用FineReport帆软设计器,配置远程服务器的方式如下: 1、打开帆软设计器,点击文件&#x…

工程项目管理系统的Java实现:高效协同与信息共享

在当今的工程领域,项目管理的高效协同和信息共享是提升工作效率、降低成本的关键。本文将向您介绍一款基于Java技术构建的工程项目管理系统,该系统采用前后端分离的先进技术框架,功能全面,能够满足不同角色的需求,从项…

中英文语音合成芯片(TTS芯片)WT3000T8-在ETC上的应用案例

一:开发背景: 随着智能化浪潮的推进,ETC(电子不停车收费)系统已逐渐成为现代交通的重要组成部分。在这一背景下,唯创知音自主研发的WT3000T8中文转语音合成芯片(TTS芯片)以其卓越的性…

基于TAE的数字钥匙自动化测试解决方案

方案概述 在汽车发展和用户需求的推动下,汽车钥匙开始从传统的机械钥匙向数字化、智能化方向发展。目前常见的数字钥匙集成了蓝牙、NFC、UWB等技术实现了移动设备与车端的通信,可以帮助用户便捷的实现车辆功能控制。随着数字钥匙的广泛应用,相…

振弦式位移计在各类工程中的应用

振弦式位移计,作为一种高精度、高可靠性的测量工具,在各类工程中发挥着至关重要的作用。它通过测量弦的振动频率来间接得出结构的位移变化,为工程师和科学家们提供了精确的数据支持,从而确保工程的安全性和稳定性。 点击输入图片描…

YOLOv5改进 | 注意力机制 | 添加双重注意力机制 DoubleAttention【附代码/涨点能手】

💡💡💡本专栏所有程序均经过测试,可成功执行💡💡💡 在图像识别中,学习捕捉长距离关系是基础。现有的CNN模型通常通过增加深度来建立这种关系,但这种形式效率极低。因此&…

Hudi之数据读写探究

Hudi之数据读写深入探究 1. Hudi数据写入 1-1. 写操作 Hudi数据湖中的数据更新、插入和删除操作,是一个基于Apache Hadoop的库,为数据湖提供了一种有效的方法来处理更新和增量数据,并支持基于时间的快照和增量数据处理。Hudi支持三种主要的…

Linux源码编译安装MySQL + Qt连接MySQL

一、准备工作 1. 编译环境: 银河麒麟V10 飞腾D2000 CPU 2. 下载MySQL源码 这里编译的是5.7.44版本,带Boost库,这是官网的下载地址:MySQL :: Download MySQL Community Server (Archived Versions) 3. 解压压缩包 tar -zxvf mys…

Guns框架:基于主流技术Spring Boot2 + Vue3 + Antd Vue的现代Java应用开发新纪元

Guns框架:基于主流技术Spring Boot2 Vue3 Antd Vue的现代Java应用开发新纪元 摘要:随着信息技术的飞速发展,软件开发框架在提升开发效率、降低成本方面扮演着至关重要的角色。Guns框架,作为一个现代化的Java应用开发框架&#x…

vue3 组件的动态渲染 <component :is=“componentTag“ />

1、动态渲染组件 <component :is""></component> 通过isShow来切换显示A、B组件 首先创建父组件.vue文件和两个子组件A、B文件&#xff0c;并引入。 template&#xff1a; <div><h3>我是父组件dynamicComp.vue</h3><button clic…

firebase如何自定义上传日志

我们可以很轻松的得到2个代码&#xff1a; Firebase.crashlytics.log(str) Firebase.crashlytics.recordException(ex)这就是firebase提供的自定义日志和excption上传的方法。 但是如果你认为log函数调用后&#xff0c;直接就能查看到日志就错了。 我们在这个page是找不到日志…

端午节粽子龙舟主题互动趣味小游戏效果是什么

端午三天乐&#xff0c;无论节日当天还是之前&#xff0c;行业商家都可以自己的品牌为主借势营销&#xff0c;趣味活动形式玩法和内容呈现达成多种效果&#xff0c;品牌传播、公众号涨粉、线下互动、商品促销、用户促活等。 在【雨科】平台拥有多款端午节互动小游戏类型&#…