【零拷贝】

目录

一:了解IO基础概念

二:数据流动的层次结构

三:零拷贝

1.传统IO文件读写

2.mmap 零拷贝技术

3.sendFile 零拷贝技术


一:了解IO基础概念

理解CPU拷贝和DMA拷贝

​         我们知道,操作系统对于内存空间,是分为用户态和内核态的。用户态的应用程序无法直接操作硬件,需要通过内核空间进行操作转换,才能真正操作硬件。这其实是为了保护操作系统的安全。正因为如此,应用程序需要与网卡、磁盘等硬件进行数据交互时,就需要在用户态和内核态之间来回的复制数据。而这些操作,原本都是需要由CPU来进行任务的分配、调度等管理步骤的,早先这些IO接口都是由CPU独立负责,所以当发生大规模的数据读写操作时,CPU的占用率会非常高。

之后,操作系统为了避免CPU完全被各种IO调用给占用,引入了DMA(直接存储器存储)。由DMA来负责这些频繁的IO操作。DMA是一套独立的指令集,不会占用CPU的计算资源。这样,CPU就不需要参与具体的数据复制的工作,只需要管理DMA的权限即可。

​ DMA拷贝极大的释放了CPU的性能,因此他的拷贝速度会比CPU拷贝要快很多。但是,其实DMA拷贝本身,也在不断优化。

​ 引入DMA拷贝之后,在读写请求的过程中,CPU不再需要参与具体的工作,DMA可以独立完成数据在系统内部的复制。但是,数据复制过程中,依然需要借助数据总进线。当系统内的IO操作过多时,还是会占用过多的数据总线,造成总线冲突,最终还是会影响数据读写性能。

​ 为了避免DMA总线冲突对性能的影响,后来又引入了Channel通道的方式。Channel,是一个完全独立的处理器,专门负责IO操作。既然是处理器,Channel就有自己的IO指令,与CPU无关,他也更适合大型的IO操作,性能更高。

​ 这也解释了,为什么Java应用层与零拷贝相关的操作都是通过Channel的子类实现的。这其实是借鉴了操作系统中的概念。

channel知识点:

在计算机系统中,“通道”(Channel) 的具体作用范围取决于上下文(如硬件架构、操作系统或编程框架)。以下是不同场景下的解释:


通道是 数据传输的路径或抽象机制,通常不直接等同于“操作系统内存 ↔ 外设”的物理传输,而是分层协作中的一环。

(1) 硬件层的通道(如传统大型机)

  • 功能
    某些系统(如 IBM 大型机)的 I/O 通道 是专用硬件,直接管理外设(磁盘、磁带)与内存的传输。

  • 特点

    • 通道是独立于 CPU 的处理器,可执行复杂的 I/O 指令(如协议解析、数据分块)。

    • 直接与外设控制器交互,完成物理数据传输(类似增强版 DMA)。

  • 示例
    大型机中,通道从磁盘读取数据到内存,无需 CPU 干预。

(2) 操作系统层的通道(如设备驱动)

  • 功能
    操作系统通过 设备驱动内核 I/O 子系统 管理外设与内存的交互。

  • 特点

    • 通道在此上下文中更接近 逻辑抽象(如 /dev 下的设备文件)。

    • 实际数据传输依赖 DMAPIO(编程 I/O)

(3) 编程框架中的通道(如 Java NIO)

  • 功能
    Java NIO 的 Channel(如 FileChannelSocketChannel)是 用户空间与内核空间之间的桥梁

  • 特点

    • 通过系统调用与内核交互,数据在用户缓冲区(如 ByteBuffer)和内核的 Page Cache 之间传输。

    • 不直接操作外设,物理传输由操作系统和 DMA 完成。

二:数据流动的层次结构

计算机系统中,数据从程序到磁盘(或反向)的流动通常经过以下层级:

程序中的 IO 流 → 用户空间缓冲区 → 操作系统缓存页(内核空间) → 磁盘驱动 → 物理磁盘

示意图

程序代码               用户空间              内核空间              硬件层
┌───────────┐       ┌─────────────┐      ┌──────────────┐      ┌────────┐
│  IO 流    │ → → → │ 用户缓冲区   │ → → → │ Page Cache    │ → → → │ 磁盘   │
└───────────┘       └─────────────┘      └──────────────┘      └────────┘(程序层)             (操作系统层)        (物理层)

 而零拷贝技术是减少用户空间和内存空间之间数据传输的次数,接下来,将进入零拷贝的讲解。

三:零拷贝

        零拷贝(Zero-copy) 是一种优化技术,旨在 减少或消除数据在内存中的冗余复制操作,从而提升 I/O 性能。它主要作用于 用户空间内存与内核空间内存之间的数据传输,但最终目标是减少整个数据链路(从磁盘到网络、或内存到外设)中的复制次数。

        对于Java应用层来说,零拷贝有mmap和sendFile两种方式。

1.传统IO文件读写

        说零拷贝技术之前,要先了解传统的IO文件读写是怎么样的,才能更好的理解零拷贝技术,下面先说传统IO的读写工作流程。

传统IO文件读写

      传统IO文件读写如下图1-1所示:

                                                图1-1 传统IO文件读写流程图

传统IO文件读写工作流程:

整体流程

Java 程序 → 用户空间 → 内核空间(Page Cache) → 磁盘(修改数据) ↑↓(读写)       (DMA 传输)

流程分三个阶段:读取数据修改数据写回数

详细步骤与层级交互

(1) 打开文件

  • Java 代码:使用 FileInputStreamFileChannelRandomAccessFile 打开文件。

  • 系统调用open(),触发内核创建文件描述符,建立程序与文件的连接。

(2) 读取数据(磁盘 → 内核空间 → 用户空间)

  1. 磁盘到内核空间(Page Cache)

    • DMA 传输:磁盘控制器通过 DMA(直接内存访问) 将文件数据直接读取到内核的 Page Cache,无需 CPU 参与。

    • 触发方式:Java 调用 FileChannel.read(ByteBuffer)InputStream.read(),底层触发 read() 系统调用。

  2. 内核空间到用户空间

    • 数据拷贝:内核将 Page Cache 中的数据复制到用户空间的缓冲区(如 byte[]ByteBuffer)。

    • 性能开销:此拷贝由 CPU 完成,是小文件读取的主要性能瓶颈。

(3) 修改数据(用户空间内操作)

  • Java 操作:在用户空间的缓冲区中修改数据(如字符串替换、字节操作)。

  • 示例

    String content = new String(buffer.array(), StandardCharsets.UTF_8);
    String modifiedContent = content.replace("old", "new");
    byte[] newData = modifiedContent.getBytes(StandardCharsets.UTF_8);

(4) 写回数据(用户空间 → 内核空间 → 磁盘)

  1. 用户空间到内核空间(Page Cache)

    • 数据拷贝:用户空间的修改后数据通过 FileChannel.write(ByteBuffer)OutputStream.write() 触发 write() 系统调用,将数据复制到内核的 Page Cache。

    • 延迟写入:数据暂存于 Page Cache,不会立即写入磁盘。

  2. 内核空间到磁盘

    • DMA 传输:操作系统通过 DMA 将 Page Cache 中的数据异步写入磁盘。

    • 刷盘时机

      • 定时刷盘:由内核线程(如 pdflush)定期将脏页(修改过的数据)写入磁盘。

      • 强制刷盘:调用 FileChannel.force(true) 触发 fsync() 系统调用,确保数据持久化。

(5) 关闭文件

  • Java 代码:调用 close() 释放文件描述符。

  • 系统调用close(),释放内核资源。

2.mmap 零拷贝技术

  mmap 零拷贝技术 的核心是通过内存映射文件(Memory-Mapped File)将文件内容直接映射到用户空间的虚拟内存,从而避免传统 I/O 中用户空间与内核空间之间的数据拷贝。

mmap零拷贝技术IO读写

       IO文mmap零拷贝技术文件读写如下图1-2所示:

                                        图2-1 mmap零拷贝技术文件读写流程图

mmap零拷贝IO文件读写工作流程:

工作流程概述

磁盘 → Page Cache →(内存映射)→ 用户空间虚拟内存 →(修改数据)→ Page Cache → 磁盘

 详细步骤

1. 打开文件并创建内存映射

  • 用户空间
    使用 FileChannel.map() 将文件映射到用户空间的虚拟内存。

  • 内核空间
    内核将文件的磁盘块映射到 Page Cache,并建立用户空间虚拟内存与 Page Cache 的映射关系。

  • 代码示例

    FileChannel channel = FileChannel.open(Paths.get("file.txt"), StandardOpenOption.READ, StandardOpenOption.WRITE);
    MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, channel.size());

2. 读取数据

  • 用户空间
    用户程序直接通过 MappedByteBuffer 访问数据,无需显式调用 read()

  • 内核空间
    若数据未加载到 Page Cache,触发缺页中断,内核从磁盘读取数据到 Page Cache。

  • 零拷贝
    数据直接从 Page Cache 映射到用户空间,无需复制到用户缓冲区。

3. 修改数据

  • 用户空间
    用户程序直接修改 MappedByteBuffer 中的数据。

  • 内核空间
    修改后的数据标记为 脏页(Dirty Page),暂存于 Page Cache。

4. 写回磁盘

  • 用户空间
    调用 buffer.force() 强制将脏页刷回磁盘。

  • 内核空间
    内核将脏页从 Page Cache 写回磁盘的对应位置。

  • 代码示例

    buffer.force(); // 强制刷盘

5. 关闭映射

  • 用户空间
    关闭 FileChannel,释放映射的内存区域。

  • 内核空间
    解除内存映射,释放相关资源。

  • 代码示例

    channel.close();

3.sendFile 零拷贝技术

        早期的sendFile其实和mmap一样,实现机制还是依靠CPU进行页缓存与socket缓存区之间的数据拷贝,如图3-1所示。

                                 图3-1 早期的sendFile 读写流程图

         从Linux内核2.6.33版本开始,引入了对 Scatter-Gather DMA(分散-聚集 DMA) 的支持,优化了实现机制,在拷贝过程中,并不直接拷贝文件的内容,而是只拷贝一个带有文件位置和长度等信息的文件描述符FD,这样子就大大减少了需要传递的数据。而真实的数据内容,会交由DMA控制器,从页缓存中打包异步发送到socket中。如图3-2所示。

                                       图3-2 Linux内核2.6.33 版本 sendFile 读写流程图

注意: sendfile 系统调用 的主要设计目标是 将文件数据高效地发送到网络套接字,因此它 不支持将用户空间的数据直接写入磁盘

工作流程概述

磁盘 → Page Cache →(sendfile)→ 网卡

详细步骤

1. 打开文件

  • 用户空间
    使用 FileChannel 打开文件。

  • 内核空间
    内核将文件的磁盘块映射到 Page Cache

  • 代码示例

    FileChannel fileChannel = new FileInputStream("file.txt").getChannel();

2. 打开网络套接字

  • 用户空间
    使用 SocketChannel 打开网络连接。

  • 内核空间
    内核创建 Socket Buffer,用于管理网络数据。

  • 代码示例

    SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("host", 8080));

3. 使用 sendfile 发送数据

  • 用户空间
    调用 FileChannel.transferTo(),底层使用 sendfile 系统调用。

  • 内核空间
    数据直接从 Page Cache 通过 DMA 发送到网卡,绕过用户空间。

  • 代码示例

    fileChannel.transferTo(0, fileChannel.size(), socketChannel); // 零拷贝发送

4. 关闭资源

  • 用户空间
    关闭 FileChannelSocketChannel,释放资源。

  • 内核空间
    释放 Page Cache 和 Socket Buffer 资源。

  • 代码示例

    fileChannel.close();
    socketChannel.close();

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

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

相关文章

全栈开发:使用.NET Core WebAPI构建前后端分离的核心技巧(一)

目录 cors解决跨域 依赖注入使用 分层服务注册 缓存方法使用 内存缓存使用 缓存过期清理 缓存存在问题 分布式的缓存 cors解决跨域 前后端分离已经成为一种越来越流行的架构模式,由于跨域资源共享(cors)是浏览器的一种安全机制,它会阻止前端应用…

《Linux服务与安全管理》| 数据库服务器安装和配置

《Linux服务与安全管理》| 数据库服务器安装和配置 目录 《Linux服务与安全管理》| 数据库服务器安装和配置 任务一: 安装PostgreSQL数据库,设置远程登录,客户端可以成功登录并操作数据库。 任务二: 安装MySQL数据库&#xf…

Linux系统之whereis命令的基本使用

Linux系统之whereis命令的基本使用 一、whereis命令介绍二、whereis命令的使用帮助2.1 whereis命令的帮助信息2.2 whereis命令帮助解释 三、whereis命令的基本使用3.1 查找命令的位置3.2 仅查找二进制文件3.3 仅查找手册页3.4 输出实际使用的查找路径3.5 指定自定义搜索路径 四…

Autosar-以太网是怎么运行的?(Davinci配置部分)

写在前面: 入行一段时间了,基于个人理解整理一些东西,如有错误,欢迎各位大佬评论区指正!!! 目录 1.Autosar ETH通讯软件架构 2.Ethernet MCAL配置 2.1配置对应Pin属性 2.2配置TXD引脚 2.3配…

【Block总结】CSAM,包含分割、关键点、切分等均适用!|即插即用

论文信息 标题: CSAM: A 2.5D Cross-Slice Attention Module for Anisotropic Volumetric Medical Image Segmentation 论文链接: https://arxiv.org/pdf/2311.04942 GitHub链接: https://github.com/aL3x-O-o-Hung/CSAM 创新点 CSAM(跨切片注意力模块&#xff…

解决PyG安装中torch-sparse安装失败问题:详细指南

1 问题描述 最近在学习GNN,需要使用PyTorch Geometric(PyG)库。在安装PyG的过程中,遇到了torch-sparse安装失败的问题,错误提示为: ERROR: Failed building wheel for torch-sparse本文将详细记录问题的解…

鸟哥Linux私房菜笔记(三)

鸟哥Linux私房菜笔记(三) 该第三部分和第四部分主要为原书的第十一章(正则表达式与文件格式化处理),第十二章学习shell脚本,第十六章(进程管理与SElinux初探部分),第十七…

python学opencv|读取图像(五十四)使用cv2.blur()函数实现图像像素均值处理

【1】引言 前序学习进程中,对图像的操作均基于各个像素点上的BGR值不同而展开。 对于彩色图像,每个像素点上的BGR值为三个整数,因为是三通道图像;对于灰度图像,各个像素上的BGR值是一个整数,因为这是单通…

Spring Boot 2 快速教程:WebFlux处理流程(五)

WebFlux请求处理流程 下面是spring mvc的请求处理流程 具体步骤: 第一步:发起请求到前端控制器(DispatcherServlet) 第二步:前端控制器请求HandlerMapping查找 Handler (可以根据xml配置、注解进行查找) 匹配条件包括…

小程序设计和开发:如何研究同类型小程序的优点和不足。

一、确定研究目标和范围 明确研究目的 在开始研究同类型小程序之前,首先需要明确研究的目的。是为了改进自己的小程序设计和开发,还是为了了解市场趋势和用户需求?不同的研究目的会影响研究的方法和重点。例如,如果研究目的是为了…

Vue3.0实战:大数据平台可视化(附完整项目源码)

文章目录 创建vue3.0项目项目初始化项目分辨率响应式设置项目顶部信息条创建页面主体创建全局引入echarts和axios后台接口创建express销售总量图实现完整项目下载项目任何问题都可在评论区,或者直接私信即可。 创建vue3.0项目 创建项目: vue create vueecharts选择第三项:…

Java自定义IO密集型和CPU密集型线程池

文章目录 前言线程池各类场景描述常见场景案例设计思路公共类自定义工厂类-MyThreadFactory自定义拒绝策略-RejectedExecutionHandlerFactory自定义阻塞队列-TaskQueue(实现 核心线程->最大线程数->队列) 场景1:CPU密集型场景思路&…

【VM】VirtualBox安装ubuntu22.04虚拟机

阅读本文之前,请先根据 安装virtualbox 教程安装virtulbox虚拟机软件。 1.下载Ubuntu系统镜像 打开阿里云的镜像站点:https://developer.aliyun.com/mirror/ 找到如图所示位置,选择Ubuntu 22.04.3(destop-amd64)系统 Ubuntu 22.04.3(desto…

C#,shell32 + 调用控制面板项(.Cpl)实现“新建快捷方式对话框”(全网首发)

Made By 于子轩,2025.2.2 不管是使用System.IO命名空间下的File类来创建快捷方式文件,或是使用Windows Script Host对象创建快捷方式,亦或是使用Shell32对象创建快捷方式,都对用户很不友好,今天小编为大家带来一种全新…

国产编辑器EverEdit - 输出窗口

1 输出窗口 1.1 应用场景 输出窗口可以显示用户执行某些操作的结果,主要包括: 查找类:查找全部,筛选等待操作,可以把查找结果打印到输出窗口中; 程序类:在执行外部程序时(如:命令窗…

Vue-data数据

目录 一、Vue中的data数据是什么?二、data支持的数据类型有哪些? 一、Vue中的data数据是什么? Vue中用到的数据定义在data中。 二、data支持的数据类型有哪些? data中可以写复杂类型的数据,渲染复杂类型数据时只要遵…

02.03 递归运算

使用递归求出 1 1/3 -1/5 1/7 - 1/9 ... 1/n的值。 1>程序代码 #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #inc…

数据分析系列--⑥RapidMiner构建决策树(泰坦尼克号案例含数据)

一、资源下载 二、数据处理 1.导入数据 2.数据预处理 三、构建模型 1.构建决策树 2.划分训练集和测试集 3.应用模型 4.结果分析 一、资源下载 点击下载数据集 二、数据处理 1.导入数据 2.数据预处理 三、构建模型 1.构建决策树 虽然决策树已经构建,但对于大多数初学者或…

100 ,【8】 buuctf web [蓝帽杯 2021]One Pointer PHP(别看)

进入靶场 没提示&#xff0c;去看源代码。 user.php <?php // 定义一个名为 User 的类&#xff0c;该类可用于表示用户相关信息或执行与用户有关的操作 class User{// 声明一个公共属性 $count&#xff0c;可在类的内部和外部直接访问// 这个属性可能用于记录与用户相关…

巧妙利用数据结构优化部门查询

目录 一、出现的问题 部门树接口超时 二、问题分析 源代码分析 三、解决方案 具体实现思路 四、优化的效果 一、出现的问题 部门树接口超时 无论是在A项目还是在B项目中&#xff0c;都存在类似的页面&#xff0c;其实就是一个部门列表或者叫组织列表。 从页面的展示形式…