彻底搞懂零拷贝技术( DMA、PageCache)

DMA

直接内存访问(Direct Memory Access)

什么是DMA?

在进行数据传输的时候,数据搬运的工作全部交给 DMA 控制器,而 CPU 不再参与,可以去干别的事情。

传统I/O

在没有 DMA 技术前,全程数据拷贝都需要CPU来做,严重消耗CPU。

添加图片注释,不超过 140 字(可选)

利用DMA的IO

利用DMA之后:

  • 4次数据拷贝,其中DMA和CPU分别拷贝2次(CPU的时间多宝贵啊)

  • 2次系统调用导致的4 次用户态与内核态的上下文切换

DMA 控制器进行数据传输的过程:

  • 用户进程调用 read 方法,向操作系统发出 I/O 请求,请求读取数据到自己的用户缓冲区中,进程进入阻塞状态,用户态切换至内核态;

  • 操作系统收到请求后,进一步将 I/O 请求发送 DMA,然后让 CPU 可以执行其他任务;

  • DMA 进一步将 I/O 请求发送给磁盘;

  • 磁盘收到 DMA 的 I/O 请求,把数据从磁盘读取到磁盘控制器的缓冲区中,当磁盘控制器的缓冲区被读满后,向 DMA 发起中断信号,告知自己缓冲区已满;

  • DMA 收到磁盘的信号,将磁盘控制器缓冲区中的数据拷贝到内核缓冲区中,此时不占用 CPU,CPU 可以执行其他任务;

  • 当 DMA 读取了足够多的数据,就会发送中断信号给 CPU;

  • CPU 收到 DMA 的信号,知道数据已经准备好,于是将数据从内核拷贝到用户空间,系统调用返回,内核态切换至用户态;

利用DMA的IO完整流程图:

1、CPU 需对 DMA 控制器下发指令,告诉它想读取多少数据,读完的数据放在内存;

2、接下来,DMA 控制器会向磁盘控制器发出指令,通知它从磁盘读数据到其内部的缓冲区中,

3、接着磁盘控制器将缓冲区的数据传输到内存;

4、数据拷贝成功之后,磁盘控制器在总线上发出一个确认成功的信号到 DMA 控制器;

5、DMA 控制器收到信号后,DMA 控制器通过中断通知 CPU 指令完成,CPU 就可以直接取内存里面现成的数据了;

可以看到,仅仅在传送开始和结束时需要 CPU 干预,其他任务交由DMA处理。

因为发生了read+write两次系统调用,所以一共发生了 4 次用户态与内核态的上下文切换

上下文切换的成本并不小,一次切换需要耗时几十纳秒到几微秒

还发生了 4 次数据拷贝,其中两次是 CPU参与的拷贝。

如何优化?

减少「用户态与内核态的上下文切换」和「数据拷贝」的次数。

1、如何减少「用户态与内核态的上下文切换」的次数呢?

读取磁盘数据的时候,之所以要发生上下文切换,是因为用户空间没有权限操作磁盘或网卡,这些操作设备的过程只能交由OS内核来完成。所以需要系统调用进行上下文切换,切换到内核态。

所以,减少上下文切换到次数的办法就是:

减少系统调用的次数

2、如何减少「数据拷贝」的次数?

从内核的读缓冲区-----用户的缓冲区里----- socket 的缓冲区里,这个过程是没有必要的。

因为文件传输的应用场景中,在用户空间我们并不会对数据「再加工」,所以数据实际上可以不用搬运到用户空间,因此用户的缓冲区是没有必要存在的。

相关视频推荐

手写用户态协议栈以及零拷贝的实现

程序性能上不去怎么办? 异步来解决你的问题

从 8 个方面看项目迭代优化之路

Linux C/C++开发(后端/音视频/游戏/嵌入式/高性能网络/存储/基础架构/安全)

需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

零拷贝

零拷贝技术实现的方式通常有 2 种:

  • mmap(内存映射) + write

  • sendfile

mmap + write

在前面我们知道,read() 系统调用的过程中会把内核缓冲区的数据拷贝到用户的缓冲区里,于是为了减少这一步开销,我们可以用 mmap() 替换 read() 系统调用函数。

mmap系统调用函数会直接把内核缓冲区里的数据共享到用户空间,这样,操作系统内核与用户空间就不需要再进行任何的数据拷贝操作。

具体过程如下:

  • 应用进程调用了 mmap后,DMA 会把磁盘的数据拷贝到内核的缓冲区里。接着,应用进程跟操作系统内核「共享」这个缓冲区;

  • 应用进程再调用 write,操作系统直接将内核缓冲区的数据拷贝到 socket 缓冲区中,这一切都发生在内核态,由 CPU 来搬运数据;

  • 最后,把内核的 socket 缓冲区里的数据,拷贝到网卡的缓冲区里,这个过程是由 DMA 搬运的。

性能如何?

  • 可以减少一次数据拷贝的过程。

  • 但这还不是最理想的零拷贝,因为 把内核缓冲区的数据拷贝到 socket 缓冲区里的工作仍然需要通过CPU完成,

  • 而且仍然需要 4 次上下文切换,因为系统调用还是 2 次。

mmap详解

是什么?

mmap是一种实现内存映射文件的方法。

即:将一个文件映射到用户进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。

实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作,又不必再调用read,write等系统调用函数。

相应地,内核空间对这段区域的修改也直接反映到用户空间,从而可以实现不同用户进程间的文件共享。

mmap内存映射的实现过程,总的来说可以分为三个阶段:

1、进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域

2、调用mmap实现文件的物理地址和进程虚拟地址的一一映射关系

注:前两个阶段仅在于创建虚拟区间并完成地址映射,还没有将任何文件数据拷贝至主存。真正的文件读取是当进程发起读或写操作时开始。

3、进程发起对这片映射空间的访问,引发缺页中断,实现文件到内核缓冲区的拷贝

  • 进程的读或写操作访问虚拟地址空间这一段映射地址,通过查询页表,发现这一段地址并不在物理页面上。因为目前只建立了地址映射,真正的硬盘数据还没有拷贝到内存中,因此引发缺页异常。

  • 缺页异常进行一系列判断,确定无非法操作后,内核发起请求调页过程。

  • 调页完成后。进程即可对这片内核缓冲区进行读写操作,如果写操作改变了其内容,一定时间后系统会自动回写脏页面到对应磁盘地址,也即完成了写入到文件的过程。

mmap的功能:

1、上面已经分析了,mmap最大的功能就是减少了数据的拷贝次数

2、提供了进程间共享内存及相互通信的方式。

不管是父子进程还是无亲缘关系的进程,都可以将自身用户空间映射到同一个文件。从而通过各自对映射区域的改动,达到进程间通信和进程间共享的目的。

同时,如果进程A和进程B都映射了区域C,当A第一次读取C时通过缺页从磁盘复制文件页到内存中;但当B再读C的相同页面时,虽然也会产生缺页异常,但是不再需要从磁盘中复制文件过来,而可直接使用已经保存在内存中的文件数据。

3、可用于实现高效的大规模数据传输。

内存空间不足,是制约大数据操作的一个方面,解决方案往往是借助硬盘空间协助操作,补充内存的不足。但是进一步会造成大量的文件I/O操作,极大影响效率。这个问题可以通过mmap映射很好的解决。换句话说,但凡是需要用磁盘空间代替内存的时候,mmap都可以发挥其功效。

sendfile

3次数据拷贝,其中CPU拷贝一次 1次系统调用 2 次用户态与内核态的上下文切换

在 Linux 内核版本 2.1 中,提供了一个专门发送文件的系统调用函数 sendfile。

首先,它可以替代前面的 read() 和 write() 这两个系统调用,这样就可以减少一次系统调用,也就减少了 2 次上下文切换的开销。

其次,该系统调用,可以直接把内核缓冲区里的数据拷贝到 socket 缓冲区里,不再拷贝到用户态,这样就减少了一次数据拷贝,

现在一共只有 2 次上下文切换,和 3 次数据拷贝。如下图:

但是这还不是真正的零拷贝技术。

真正的零拷贝

2次数据拷贝,无CPU参与拷贝 
1次系统调用 
2 次用户态与内核态的上下文切换

从 Linux 内核 2.4 版本开始起,sendfile() 系统调用的过程发生了点变化,具体过程如下:

  • 通过 DMA 将磁盘上的数据拷贝到内核缓冲区里;

  • 只将缓冲区描述符和数据长度传到 socket 缓冲区,而内核缓存中的数据则通过网卡的 SG-DMA 控制器直接拷贝到网卡的缓冲区里,这样就减少了一次数据拷贝;

所以,这个过程之中,只进行了 2 次数据拷贝,如下图:

性能如何?

  • 全程没有通过 CPU 来搬运数据,所有的数据都是通过 DMA 来进行传输的。

  • 只需要 2 次上下文切换和2次数据拷贝,就可以完成文件的传输,

所以,总体来看,零拷贝技术可以把文件传输的性能提高至少一倍以上。

kafka和Nginx都使用了零拷贝技术

为什么需要内核缓存区?

现在回过头再来看,为什么不直接将磁盘数据拷贝到网卡,而要在中间加一个内核缓存区呢? ——核心原因是磁盘读写太慢了

内核缓存区做了什么?

  • 缓存最近被访问的数据;

  • 预读功能;

  • 内核的 I/O 调度算法会缓存尽可能多的 I/O 请求在 内核缓存区中,最后「合并」成一个更大的 I/O 请求再发给磁盘,这样做是为了减少磁盘的寻址操作;

1、缓存最近被访问的数据

最近访问过的数据接下来很可能还会被访问,所以利用PageCache 缓存最近被访问的数据,读磁盘数据的时候,优先在 PageCache 找,如果数据存在则可以直接返回;如果没有,则从磁盘中读取,然后缓存在 PageCache 中。当PageCache的空间不足时,淘汰最久未被访问的缓存。

2、预读功能

利用空间局部性原理,假设 read 方法每次只会读32 KB的字节,虽然 read 刚开始只会读0 ~ 32 KB的字节,但内核会把其后面的 32~64 KB 也读取到 PageCache,这样后面读取 32~64 KB 的成本就很低,如果在 32~64 KB 淘汰出 PageCache 前,进程读取到它了,收益就非常大。

这两个做法都在于解决读写磁盘相比读写内存的速度慢太多了这一痛点,大大提高了读写磁盘的性能。

所以零拷贝使用 内核缓存区技术进一步提升性能。

但是由于内核缓存区不适合传输大文件,所以零拷贝不适合传输大文件 因为每当用户访问这些大文件的时候,内核就会把它们载入 内核缓存区中,于是 内核缓存区空间很快被这些大文件占满。其他「热点」的小文件可能就无法充分使用到 内核缓存区,于是这样磁盘读写的性能就会下降了;

所以,内核缓存区中的大文件数据,不但没有享受到缓存带来的好处,却还耗费 DMA 多拷贝到 内核缓存区一次;

那针对大文件的传输,我们应该使用什么方式呢?

大文件传输:异步IO+直接IO

回顾最初的例子,当调用 read 方法读取文件时,进程实际上会阻塞在 read 方法调用,因为要等待磁盘数据的返回,如下图:

对于阻塞的问题,可以用异步 I/O来解决,它的工作方式如下图:

可以发现,异步 I/O 并没有涉及到 内核缓存区。

绕开 内核缓存区的 I/O 叫直接 I/O,使用 内核缓存区的 I/O 则叫缓存 I/O。 通常,对于磁盘,异步 I/O 只支持直接 I/O。

所以,针对大文件的传输的方式,应该使用异步 I/O + 直接 I/O来替代零拷贝技术。

总结

DMA和传统IO

早期 I/O 操作,内存与磁盘的数据传输的工作都是由 CPU 完成的,而此时 CPU 不能执行其他任务,会特别浪费 CPU 资源。

于是,为了解决这一问题,DMA 技术就出现了,实际数据传输工作由 DMA 控制器来完成,CPU 不需要参与数据传输的工作。

零拷贝

传统 IO的工作方式,从硬盘读取数据,然后再通过网卡向外发送,需要进行 4次上下文切换,和 4 次数据拷贝,更糟糕的是其中两次都是CPU完成的。

为了提高文件传输的性能,于是就出现了零拷贝技术,只有一个sendfile系统调用导致的2 次用户态与内核态的上下文切换,只进行了2 次数据拷贝(磁盘——pageCache——网卡),全程没有通过 CPU 来搬运数据,所有的数据都是通过DMA来进行传输的。

需要注意的是,零拷贝技术中,数据没有进入用户缓冲区,所以用户进程无法对文件内容作进一步的加工的,比如压缩数据再发送。

内核缓存区

零拷贝技术是基于 内核缓存区的,内核缓存区具有

  • 缓存最近访问的数据

  • 预读数据

  • 协助 I/O 调度算法实现了 IO 合并

提升了访问缓存数据的性能,解决了磁盘IO慢的问题,进一步提升了零拷贝的性能。

大文件传输

当传输大文件时,不能使用零拷贝,因为可能由于 内核缓存区 被大文件占据,而导致其他的「热点」小文件无法利用到 内核缓存区,并且大文件的缓存命中率不高,这时就需要使用「异步 IO + 直接 IO 」的方式。

异步对应同步

直接对应缓存

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AIGC之Image2Video(一)| Animate Anyone:从静态图像生成动态视频,可将任意图像角色动画化

近日,阿里发布了Animate Anyone,只需一张人物照片,结合骨骼动画,就能生成人体动画视频。 项目地址:https://humanaigc.github.io/animate-anyone/ 论文地址:https://arxiv.org/pdf/2311.17117.pdf Github…

YOLOv8 YoLov8l 模型输出及水果识别

🍨 本文为[🔗365天深度学习训练营学习记录博客 🍦 参考文章:365天深度学习训练营 🍖 原作者:[K同学啊 | 接辅导、项目定制] 🚀 文章来源:[K同学的学习圈子](https://www.yuque.com/m…

Android app性能优化指南

Android应用性能优化指南 提高应用程序的性能以实现更流畅的用户体验和更高的可见度。 性能在任何应用程序的成功中发挥着重要的作用。为用户提供流畅无缝的体验应该是开发人员的重点。 应用程序大小 在用户开始使用我们的应用程序之前,他们需要下载应用程序并将…

TypeScript入门实战笔记 -- 开篇 为什么要选择 TypeScript ?

typescript 在线编辑器http://typescript.p2hp.com/play?#code/JYOwLgpgTgZghgYwgAgJIFUDO1Uhge2QG8AoZc5YAEwC5kQBXAWwCNoBuMikOJiOzGCigA5pwrI4ANzhg4UAPwChozgF8SmmAxAIwwfCGRYcefAAoADlHyXMdDNii4CASmJdyCQ5nwAbCAA6P3wRKxs7ABpkAHJrW0wY1xINEhNnM3MiSlpkAEZonj46GIBrROQ1…

C/C++之输入输出

文章目录 一.C语言的输入输出1.printfi. 输出整数ii. 浮点数iii.字符 & 字符串 2.scanfi.整数ii.浮点数iii. 字符 & 字符串 3.特殊用法i. * 的应用ii. %n 的应用iii. %[] 的应用 二.C中的输入输出1.couti. 缓冲区(buffer)ii. cout之格式化输出 2…

区块链optimism主网节点搭建

文章目录 官方参考资料编译环境搭建编译Optimism Monorepo编译op-geth 执行下载数据快照生成op-geth和op-node通信密钥op-geth执行脚本 op-node执行脚本 启动日志op-gethop-node 本文是按照官方参考资料基于源码的方式成功搭建optimism主网节点。 官方参考资料 源码&#xff1…

【设计模式-4.4】行为型——模板方法模式

说明:本文介绍设计模式中行为型设计模式中的,模板方法模式; 生存 模版方法模式是行为型设计模式,关注于对象的行为。在《秒懂设计模式》(刘韬 著)中举了一个例子,例如哺乳动物的生存技能&…

1688API接口系列,商品详情数据丨搜索商品列表丨商家订单类丨1688开放平台接口使用方案

1688商品详情接口是指1688平台提供的API接口,用于获取商品详情信息。通过该接口,您可以获取到商品的详细信息,包括商品标题、价格、库存、描述、图片等。 要使用1688商品详情接口,您需要先申请1688的API权限,并获取ac…

【开源】基于Vue+SpringBoot的河南软件客服系统

文末获取源码,项目编号: S 067 。 \color{red}{文末获取源码,项目编号:S067。} 文末获取源码,项目编号:S067。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统管理人员2.2 业务操作人员 三、…

uniapp 云打包 生成安卓证书文件

现在使用uniapp来开发小程序,H5,APP越来越多了,目前开发了一款APP,使用的也是uniapp。在此记录下用uniapp开发app云打包时约到的一些问题吧。 前因是我司安卓同学休产假,像云打包时需要的证书文件只能自己动手来搞。看…

Avaya Aura Device Services 任意文件上传漏洞复现

0x01 产品简介 Avaya Aura Device Services是美国Avaya公司的一个应用软件。提供一个管理 Avaya 端点功能。 0x02 漏洞概述 Avaya Aura Device Services 系统PhoneBackup接口处存在任意文件上传漏洞,攻击者可绕过验证上传任意文件获取服务器权限。 0x03 影响范围…

使用Jmeter进行http接口测试

前言: 本文主要针对http接口进行测试,使用Jmeter工具实现。 Jmter工具设计之初是用于做性能测试的,它在实现对各种接口的调用方面已经做的比较成熟,因此,本次直接使用Jmeter工具来完成对Http接口的测试。 一、开发接口…

Mac电脑vm虚拟机 VMware Fusion Pro中文 for mac

VMware Fusion Pro是一款功能强大的虚拟机软件,适用于需要在Mac电脑上运行其他操作系统的用户。它具有广泛的支持、快速稳定的特点以及多种高级功能,可以满足用户的各种需求和场景。 多操作系统支持:VMware Fusion Pro允许在Mac电脑上运行多…

平台工程文化:软件开发的创新路径和协作之道

在快速发展的软件开发领域,具有前瞻性思维的企业组织正在拥抱平台工程文化的变革力量。这种创新方法强调创建共享平台、工具和实践,使开发人员能够更快、更高效地交付高质量的软件。在本文中,我们将深入探讨平台工程文化的核心原则和深远的好…

Scala 从入门到精通

Scala 从入门到精通 数据类型 pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http:…

基于STM32 + DMA介绍,应用和步骤详解(ADC多通道)

前言 本篇博客主要学习了解DMA的工作原理和部分寄存器解析&#xff0c;针对ADC多通道来对代码部分&#xff0c;应用部分作详细讲解&#xff0c;掌握代码编程原理。本篇博客大部分是自己收集和整理&#xff0c;如有侵权请联系我删除。 本次博客开发板使用的是正点原子精英版&am…

springboot_java养老院老年人问诊服务预约系统ssm

系统的目标是为管理员&#xff0c;服务者&#xff0c;医生和用户搭建一个网上沟通平台&#xff0c;保证四方的安全&#xff0c;并使四方的利益最大化。 .用户&#xff1a; &#xff08;1&#xff09;用户注册登入页面&#xff1a;用户进行操作时需要是已注册登入用户 &#xff…

WireShark监控浏览器登录过程网络请求

软件开发中经常前后端扯皮。一种是用Chrome浏览器的开发者工具 来看网络交互&#xff0c;但是前提是 网络端口的确是通的。 WireShark工作在更低层。 这个工具最大的好处&#xff0c;大家别扯皮&#xff0c;看网络底层的log&#xff0c;到底 你的端口开没开&#xff0c; 数据…

结合ColorUI组件开发微信小程序

1.自定义组件生命周期函数&#xff1a; Component({data: {},attached() {console.log("自定义组件生命周期函数 attached--先执行");this.getPos();},ready() {console.log("ready生命周期函数---在attached之后执行")},methods: {getPos() {var that th…

uniapp 使用 $emit和$on——$on中无法为data中的变量赋值

问题在于this的指向&#xff0c; 解决办法是使用变量保存$on&#xff0c;其次再为data中的值赋值 以下是具体代码&#xff1a; 1、html代码&#xff1a; <view class"form_picker" click"selePositionFun()"><view class""><inp…