高性能——零拷贝

这篇具有很好参考价值的文章主要介绍了高性能——零拷贝。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一.背景

二.零拷贝定义

三.传统I/O执行流程

四.零拷贝相关

1.内核空间与用户空间

为什么有空间划分?

责任划分

2.用户态与内核态定义

3.上下文切换

1.什么是CPU上下文

2.什么是CPU上下文切换

4.虚拟内存

5.DMA

    1.定义

    2.DMA帮做的事情

    3.为什么需要DMA

五.实现零拷贝的方式

mmap + write

sendfile

sendfile+DMA scatter/gather 实现的零拷贝

三种“零拷贝”方式对比

六.java提供的零拷贝技术

java NIO对mmap的支持

java NIO对sendfile的支持


一.背景

零拷贝算是一个老生常谈的问题啦,很多顶级框架都用到了零拷贝来提升性能,比如我们经常接触到的Kafka 、RocketMQ、Netty 。

二.零拷贝定义

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

计算机执行 I/O 操作时,CPU 不需要将数据从一个存储区域复制到另一个存储区域,从而可以减少上下文切换以及 CPU 的拷贝时间。它是一种 I/0 操作优化技术。

零拷贝并不是没有拷贝数据,而是减少用户态/内核态的切换次数以及 CPU 拷贝的次数。

三.传统I/O执行流程

        以【文件下载】功能为例,前端发起请求后,服务端的任务就是:将主机磁盘中的文件通过已连接的Socket发出。

        关键代码如下:    

while((n = read(diskfd,buf,BUF SIZE)) > 0)
write(sockfd,buf ,n);

        传统的IO流程,包括read和write两个过程。

        read:把数据从【磁盘】--读取到-->【内核缓冲区】--读取到-->【用户缓冲区】

        write:把数据写入【socket缓冲区】--写入到-->【网卡设备】

        流程图如下:

高性能——零拷贝

  • 用户应用进程调用 read 函数,向操作系统发起 I/O 调用,上下文从用户态转为内核态(切换 1)
  • DMA 控制器把数据从磁盘中,读取到内CPU 把内核缓冲区
  • CPU 把内核缓冲区数据,拷贝到用户应用缓冲区,上下文从内核态转为用户态 (切换 2),read 函数返回
  • 用户应用进程通过 write 函数,发起 1O 调用,上下文从用户态转为内核态 (切换 3)
  • CPU 将应用缓冲区中的数据,拷贝到socket缓冲区
  • DMA 控制器把数据从 socket 缓冲区拷贝到网卡设备,上下文从内核态切换回用户态 (切换4),write 函数返回

从流程图中可以看出,传统的IO读写流程,包括了4次上下文切换(4次用户态和内核态的切换),4次数据拷贝(两次CPU拷贝及两次DMA拷贝)

四.零拷贝相关

1.内核空间与用户空间

为什么有空间划分?

        应用程序其实是需要经过操作系统,才能做一系列特殊操作如:磁盘文件读写、内存读写等等。这些都是有一定风险的操作,不能由应用程序乱来,只能交给底层操作系统来。

        因此操作系统为每个进程都分配了内存空间,一部分是内存空间,一部分是用户空间。

        内核空间是操作系统内核访问的区域,是受保护的内存空间.

        用户空间是用户应用程序访问的内存区域。

        以32位操作系统为例,它会为每个进程分配4G(2的32次方)的内存空间。

责任划分

        内核空间:主要提供进程调度、内存分配、连接硬件资源等功能

        用户空间:提供给各个程序进程的空间,它不具有访问内核空间资源的权限。【若应用程序需要使用到内核空间的资源,则需要通过系统调用来完成】。进程从用户空间切换到内核空间,完成相关操作后,再从内核空间切换回用户空间。

2.用户态与内核态定义

        如果进程运行于内核空间,则称为【进程的内核态

        如果进程运行于用户空间,则称为【进程的用户态

3.上下文切换

1.什么是CPU上下文

是指CPU执行程序时,保存有关程序运行状态的信息集合。这些信息包括程序计数器、寄存器中的值以及堆栈指针等。

当CPU切换到另一个程序时,它会保存当前程序的上下文并加载下一个程序的上下文。这样可以确保程序能够正确地继续执行,并避免数据损坏或丢失。

2.什么是CPU上下文切换

内核(操作系统的核心)在CPU上对进程或者线程进行切换。

进程从用户态到内核态的转变,需要系统调用来完成,系统调用的过程,会发生CPU上下文切换。
上一行“系统调用的过程”,展开来说:
CPU寄存器里原来用户态的指令位置,需要先保存起来。接着,为了执行内核态代码,CPU寄存器需要更新为内核态指令的新位置。最后才是跳转到内核态执行任务。

高性能——零拷贝

4.虚拟内存

虚拟地址取代物理地址,使用其可以有2个好处

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

正是由于多个虚拟内存可以指向同一个物理地址,可以把内核空间和用户空间的虚拟地址映射为同一个物理地址,急可以减少IO拷贝次数。

5.DMA

    1.定义

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

    2.DMA帮做的事情

高性能——零拷贝

  1.  用户应用进程调用 read 函数,向操作系统发起IO调用,进入阻塞状态,等待数据返回。
  2. CPU 收到指令后,对 DMA 控制器发起指令调度
  3. DMA 收到IO 请求后,将请求发送给磁盘;
  4. 磁盘将数据放入磁盘控制缓冲区,并通知 DMA
  5. DMA 将数据从磁盘控制器缓冲区拷贝到内核缓冲区
  6. DMA 向 CPU 发出数据读完的信号,把工作交换给 CPU,由 CPU 负责将数据从内核缓冲区拷贝到用户缓冲区
  7.  用户应用进程由内核态切换回用户态,解除阻塞状态

 由此可知:DMA是帮CPU转发请求并进行数据拷贝

    3.为什么需要DMA

俩字:效率,它帮忙 CPU 做事情,CPU 就可以闲下来去做别的事情,提高了 CPU 的利用效率。

五.实现零拷贝的方式

  1. mmap+write
  2. sendfile
  3. 带有 DMA 收集拷贝功能的 sendfile

mmap + write


具体实现步骤如下:

  1. 调用mmap函数将文件映射到进程的地址空间中,得到一个指向映射区域的指针,称为mmap指针。这个过程只需要CPU执行一次,将文件内容映射到内存中,不需要进行数据拷贝。
  2. 将mmap指针传递给write或sendfle等函数,直接将数据从mmap指针指向的内存区域发送到网络或磁盘中,而不需要先将数据从应用程序的缓冲区复制到内核缓冲区,再写入磁盘或网络中,避免了数据的反复拷贝,降低了CPU和内存的压力。
  3. 写操作完成后,调用munmap程放mmap指针对应的内存区域。这个过程只需要CPU执行-一次,将内存中的数据同步到盘上,并回收已使用的内存区域,不需要进行数据拷贝和额外的内存分配。

因此,使用mmap+write的零拷贝方法可以有效减少数据拷贝的次数,提高数据传输的效率和速度,降低系统资源的消耗。这种零拷贝技术适用于大文件、高并发、低延迟等需求较高的场景。

高性能——零拷贝

I/O发生了4次用户空间和内核空间的上下文切换

3次数据拷贝(2次DMA拷贝、1次CPU拷贝)

mmap 是将内核缓冲区的地址和用户缓冲区的地址进行映射,内核缓冲区和应用缓冲区共享,所以节省了一次 CPU 拷贝并且用户进程内存是虚拟的,只是映射到内核的读缓冲区,可以节省一半的内存空间。

sendfile

sendfile 表示在两个文件描述符之间传输数据,它是在操作系统内核中操作的,避免了数据从内核缓冲区和用户缓冲区之间的拷贝操作,因此可以使用它来实现零拷贝。

高性能——零拷贝

 具体实现步骤如下

  1. 调用sendfile函数指定源文件描述符和目标文件描述符。目标文件描述符通常指向网络套接字或磁盘文件
  2. sendfile内部会将源文件描述符和目标文件描述符之间建立一个管道,数据不需要经过应用程序的空间,而是直接从内核中缓存区向目标文件描述符所对应的文件或者socket进行传输,彻底避免了数据在用户态进程地址空间和内核态缓冲区之间的拷贝。
  3. sendfile函数返回后,数据已经从源文件描述符发送到了目标文件描述符,不再需要其他的操作

因此,使用sendfie函数可以实现零拷贝技术,减少了CPU和内存的开销,提高了系统的性能。sendfle适用于大文件、高并发、低延迟等需求较高的场景,在Web服务器、文件传输等领域得到广泛应用。

I/O共发生了2次上下文切换,3次数据拷贝(一次CPU拷贝,两次DMA拷贝)

sendfile+DMA scatter/gather 实现的零拷贝

0次CPU拷贝

高性能——零拷贝

下面是 sendfile+DMA scatter/gather 实现零拷贝的流程:

  1. 打开源文件和目标文件,获取文件描述符。
  2. 调用 sendfile 函数,将源文件中的数据写入到内核缓冲区中,并返回写入的字节数
  3. 使用 DMA scater/gather 技术将内核缓冲区中的数据直接传输到网络适配器的缓冲区中,而不需要经过用户空间缓冲区的拷贝
  4. 等待数据传输完成后,调用 send 函数将数据发送出去。
  5. 如果还有剩余数据需要写入,则重复执行步骤 2 到步骤 4,直到所有数据传输完成。
  6. 关闭文件描述符。

整个过程中,只有在第二步中将数据从磁盘读入到内核缓中区时需要进行数据拷贝,而其他步骤都避免了数据拷贝,从而实现了零接贝,
需要注意的是,要使用 DMA scate/othr 技术,需要具备硬件支持,即网络适配器须支持 DMA SCateotb 能。此外,还需要对 DMA 进行配置和管理,以确保数据传输的正确件和安全性。

sendfiletDMA scatter/gather 实现的零拷贝,I/0 发生了2次用户空间与内核空间的上下文可以切换,以及 2 次数据拷贝。其中 2 次数据拷贝都是DMA 拷贝。这就是真正的 零拷贝,全程都没有通过 CPU 来搬运数据,所有的数据都是通过 DMA 来进行传输的。

三种“零拷贝”方式对比

mmap+write sendfile sendfile+DMA scatter/gathe
上下文切换次数 4 2 2
数据拷贝次数 3 = 2(DMA拷贝) +1(CPU拷贝) 3 = 2(DMA拷贝) +1(CPU拷贝) 2 = 2(DMA拷贝) +0(CPU拷贝)

六.java提供的零拷贝技术

java NIO对mmap的支持

高性能——零拷贝

java NIO对sendfile的支持

高性能——零拷贝文章来源地址https://www.toymoban.com/news/detail-433185.html

收队!

到了这里,关于高性能——零拷贝的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • uniapp - [ H5 网页 / App ] 高性能 tabbar 底部菜单凸起效果,原生系统自定义底部菜单不卡顿、切换页面不闪烁、自动缓存页面(底部菜单中间自定义一个图片并悬浮起来)

    网上有很多自定义 tabbar 底部菜单的教程,但终归是组件形式,避免不了切换页面卡顿、闪屏闪烁、各平台不兼容等一系列问题。 本文 基于 uniapp 系统原生 tabbar 底部菜单,植入一个向上凸起的 “图片” 菜单,并支持点击触发事件, 您可以直接复制代码,换个中间凸起的菜

    2024年02月21日
    浏览(55)
  • 《高性能MySQL》——创建高性能的索引(笔记)

    索引(在MySQL中也叫做“键(key)”) 是存储引擎用于快速找到记录的一种数据结构。 索引对于良好的性能非常关键。尤其是当表中的数据量越来越大时,索引对性能的影响愈发重要。 在数据量较小且负载较低时,不恰当的索引对性能的影响可能还不明显,但当数据量逐渐增大时

    2024年02月07日
    浏览(115)
  • 【Linux高性能服务器编程】——高性能服务器框架

      hello !大家好呀! 欢迎大家来到我的Linux高性能服务器编程系列之高性能服务器框架介绍,在这篇文章中, 你将会学习到高效的创建自己的高性能服务器,并且我会给出源码进行剖析,以及手绘UML图来帮助大家来理解,希望能让大家更能了解网络编程技术!!! 希望这篇

    2024年04月25日
    浏览(62)
  • 读高性能MySQL(第4版)笔记08_创建高性能索引(上)

    2.4.2.1. 按照索引列中的数据大小顺序存储的 2.4.3.1. 键前缀查找只适用于根据最左前缀的查找 2.4.4.1. 在查询某些条件的数据时,存储引擎不再需要进行全表扫描 2.4.4.2. 通过比较节点页的值和要查找的值可以找到合适的指针进入下层子节点,这些指针实际上定义了子节点页中

    2024年02月08日
    浏览(50)
  • 读高性能MySQL(第4版)笔记09_创建高性能索引(下)

    1.4.4.1. InnoDB的二级索引在叶子节点中保存了记录的主键值,所以如果二级索引能够覆盖查询,则可以避免对主键索引的二次查询 7.1.5.1. 常见的类似错误通常是由于尝试使用rsync备份InnoDB导致的 7.3.3.1. 否则,对于范围查询、索引覆盖扫描等操作来说,速度可能会降低很多 7

    2024年02月08日
    浏览(63)
  • 《高性能MYSQL》-- 查询性能优化

    查询性能优化 深刻地理解MySQL如何真正地执行查询,并明白高效和低效的原因何在 查询的生命周期(不完整):从客户端到服务器,然后服务器上进行语法解析,生成执行计划,执行,并给客户端返回结果。 一条查询,如果查询得很慢,原因大概率是访问的数据太多 对于低

    2024年03月11日
    浏览(74)
  • 高性能MySQL实战(三):性能优化

    大家好,我是 方圆 。这篇主要介绍对慢 SQL 优化的一些手段,而在讲解具体的优化措施之前,我想先对 EXPLAIN 进行介绍,它是我们在分析查询时必要的操作,理解了它输出结果的内容更有利于我们优化 SQL。为了方便大家的阅读,在下文中规定类似 key1 的表示二级索引,key_

    2024年02月11日
    浏览(73)
  • 《高性能MySQL》——查询性能优化(笔记)

    将查询看作一个任务,那么它由一系列子任务组成,实际我们所做的就是: 消除一些子任务 减少子任务的执行次数 让子任务运行更快 查询的生命周期大概可分为 = { 客户端 服务器 : 进行解析 , 生成执行计划 执行:包括到存储引擎的调用,以及用后的数据处理 { 排序 分组

    2024年02月13日
    浏览(57)
  • MYSQL高性能索引

    正确的选择和创建索引是实现高性能查询的基础,以下是高效使用索引的方法 演示的sql 独立的列 独立的列指的是索引既不是表达式的一部分也不是函数的参数。 前缀索引 如果索引是很长的列,那么索引会变得很大,并且导致索引数层数变高。通常可以索引的部分字符,这

    2024年01月20日
    浏览(45)
  • 高性能JavaScript

    管理浏览器中的JavaScript代码是个棘手的问题,因为代码执行阻塞了其他浏览器处理过程,注入用户界面回执。每次遇到 script 便签,页面必须停下来等待代码下载(如果是外部的)并执行,然后再继续处理页面其他部分。但是,有几种方法可以减少JavaScript对性能的影响: 1、

    2024年02月11日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包