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

一、摘要

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

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

  • 拷贝:就是指数据从一个存储区域转移到另一个存储区域。
  • 零:它表示拷贝数据的次数为 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.mfbz.cn/a/660020.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

宠物空气净化器:猫毛过敏者的终极解决方案,养猫不再是梦!

我有一位朋友,猫猫的深度爱好者。但是每当与猫咪接触的时候就会出现过敏反应,例如浑身发痒、打喷嚏、呼吸困难、起红疹等。对此,有专家给出权威的解释:引发铲屎官过敏的“罪魁祸首”是一种叫做Feld1的蛋白质成分,而这种…

性能击败99%PC用户,旗舰性能CPU 13700KF +技嘉Z790 + 德静界 机散装机配置思路分享

性能击败99%PC用户,旗舰性能CPU 13700KF 技嘉Z790 德静界 机散装机配置思路分享 又到了一年一度的618年中促销,今年的618比往年要来的更早。 很多人已经感受到,5月各大电脑相关配件都开始了今年的促销让利。 凑此机会,我又装了…

IT学习笔记--Kubernetes

kubernetes组件: 一个Kubernetes集群主要是由控制节点(master)、工作节点(node)构成,每个节点都会安装不同的组件。 master:集群的控制平面,负责集群的决策 ApiServer&#xff1a…

蓝桥杯物联网竞赛_STM32L071KBU6_对于EEPROM的新理解

EEPROM写函数: void Function_GetEepromData(){Function_EepromRead(4, BUFF);OLED_ShowChar(0, 0, BUFF[0] 0);OLED_ShowChar(0, 2, BUFF[1] 0); BUFF[0] ;BUFF[1] ;HAL_FLASHEx_DATAEEPROM_Unlock();HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WOR…

项目经理催开发进度的5大技巧

项目经理在催促开发进度时,采用的技巧对于项目成功至关重要。这不仅影响到项目的按时完成,还关系到团队的士气、效率和最终产品的质量。项目经理如果不去主动催促开发进度,可能导致项目延期,团队偏离原定路线,工作重点…

用于脑肿瘤分割的跨模态深度特征学习| 文献速递-深度学习肿瘤自动分割

Title 题目 Cross-modality deep feature learning for brain tumor segmentation 用于脑肿瘤分割的跨模态深度特征学习 01 文献速递介绍 作为最致命的流行病,脑肿瘤的研究越来越受到关注。本文研究了一种基于深度学习的自动分割胶质瘤的方法,称为脑…

java版CRM客户关系管理系统源码-CRM客户关系管理系统的技术架构与功能实现

CRM客户关系管理系统的技术架构与功能实现 一、引言 随着市场竞争的日益激烈,客户关系管理(CRM)已成为企业赢得市场、提升客户满意度、促 进业务增长的关键手段。本文旨在介绍一款先进的CRM客户关系管理系统的技术架构与功能实现&#xff0…

认识NoSql

SQL是结构化的,NoSql是非结构化的 SQL是关联的: Nosql是无关联的: SQL采用的是SQL查询: 语法固定,好处是:只要是关系型数据库(Mysql,Oracle),都能够使用相同的语句进行查…

2024年PMP考试备考需要多长时间,每天学习多长时间?

这取决于您在PMP上投入的时间和效率,通常情况下,2-3个月就够了。如果您平时工作很忙,每天可以挤出一个小时来学习;如果时间比较充裕,可以花两个小时看书、做题和参加直播课。在备考之前,要先了解PMP每年的考…

PostgreSQL数据库提权

前面讲述了mysql、SqlServer、Redis数据库相关的提权方式,有兴趣的也可以去看看。 这里讲的postgreSQL数据库提权就是任意命令执行漏洞(CVE-2019-9193)。 目录 数据库简介 漏洞原理 影响版本 漏洞利用 利用前提 漏洞复现 复现准备 复现过程 漏洞修复 数据…

JVM学习-字节码指令集(二)

对象的创建与访问指令 创建指令 虽然类实例和数组都是对象,但Java虚拟机对类实例和数组的创建和操作使用了不同的字节码指令创建类实例指令:new 它接收一个操作数,指向常量池的索引,表示要创建的类型,执行完成后&am…

基于匹配追踪和最大重叠离散小波变换的ECG心电信号R波检测(MATLAB 2018a)

准确识别心电信号的R波是进行HRV分析的前提。因此,开发出准确的心电信号R波检测方法十分重要。近几十年来,提出的R峰检测方法主要分为两个阶段。第1阶段是预处理阶段,目的是对受不同噪声影响的原始心电信号进行降噪处理,从而实现增…

1794 jsp蛋糕店管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 jsp 蛋糕店管理系统 是一套完善的web设计系统,对理解JSP java编程开发语言有帮助采用了java设计,系统具有完整的源代码和数据库,系统采用web模式,系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&…

Pycharm打开django支持

在 PyCharm 中打开 “Settings/Preferences” -> “Languages & Frameworks” -> “Django”。 勾上Enable Django support 然后配置好文件根目录就好了

STM32FLASH闪存

文章目录 前言首先来回顾一下存储器映像FLASH简介闪存模块组织Flash基本结构(关系)图Flash解锁使用指针访问存储器FLASH操作Flash全擦除Flash页擦除Flash写入 选项字节选项字节操作选项字节擦除选项字节写入 器件电子签名注意闪存控制寄存器一览 前言 本…

Spring Boot 2 入门基础

学习要求 ● 熟悉Spring基础 ● 熟悉Maven使用 环境要求 ● Java8及以上 ● Maven 3.3及以上:https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started.html#getting-started-system-requirements 学习资料 ● 文档地址: htt…

松下MINAS A6B系列旋转电机规格书--A系列

一、松下电机型号的识别方法 二、松下标准型电机型号大全 三、松下电机的规格 四、电机外观 五、XA,XB连接器 六、USB连接器 七、EtherCAT用连接器X2A、X2B 八、IO连接器X4 输入输出信号接口 九、编码器连接器 十、模拟监视器用连接器X7 十一、电源连接器以及端子台…

python-web框架应用程序-Django环境搭建

python-web应用程序-Django环境搭建 一、Django入门 使用Django(http://djangoproject.com/)来开发一个名为“学习笔记”(Learning Log)的项目,这是一个在线日志系统,让你能够记录所学习的有关特定主题的知…

DiskCatalogMaker for Mac:您的磁盘目录管理专家

对于需要管理大量磁盘文件的用户来说,DiskCatalogMaker for Mac无疑是一款不可或缺的工具。这款专为Mac用户设计的磁盘目录制作软件,以其简洁的操作界面和强大的功能,帮助您轻松创建和管理磁盘目录。 DiskCatalogMaker支持多种磁盘格式&…

CSS学习笔记之高级教程(一)

1、圆角:border-radius border-radius 属性可以接受一到四个值。规则如下: 四个值 - border-radius: 15px 50px 30px 5px;(依次分别用于:左上角、右上角、右下角、左下角) 三个值 - border-radius: 15px 50px 30px;&a…