零拷贝原理与实现

news2024/7/4 4:46:55

1.传统拷贝

FileInputStream、FileOutputStream

read:将数据从磁盘读取到内核态缓冲区,再从内核态缓冲区拷贝到用户缓冲区

write:将数据从用户缓冲区写入到socket缓冲区,再从socket缓冲区写入到网卡设备

内核空间:

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

用户空间:

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

内核态:

进程运行在内核空间,被称为进程的内核态

用户态:

进程运行在用户空间,被称为进程的用户态。

DMA技术

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

主要流程就是:CPU通知DMA将磁盘数据拷贝到内核态后,CPU就可以无需等待执行其他任务。DMA通知磁盘将数据从磁盘放到磁盘缓冲区,磁盘完成后通知DMA,DMA将数据从磁盘缓冲区放到内内核缓冲区中。完成后,通知CPU来取数据。

1.应用程序调用read函数,向操作系统发起IO调用,上下文从用户态切换至内核态
2.DMA控制器把数据从磁盘中读取到内核缓冲区
3.CPU把内核缓冲区数据拷贝到用户应用缓冲区,上下文从内核态切换至用户态,此时read函数返回
4.用户应用进程通过write函数,发起IO调用,上下文从用户态切换至内核态
5.CPU将缓冲区的数据拷贝到socket缓冲区
6.DMA控制器将数据从socket缓冲区拷贝到网卡设备,上下文从内核态切换至用户态,此时write函数返回

 从这里可以看到做一次从磁盘到用户缓冲区的数据提取,需要cpu切换2次,cpu拷贝次数1次,DMA拷贝1次,如果是复制那就需要4次CPU切换,2次CPU拷贝,2次DMA拷贝,所以需要减少CPU在用户态与内核态之间的切换次数以及拷贝次数。

2. 零拷贝

什么是零拷贝?

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

虚拟内存

虚拟内存远大于物理内存空间。且多个虚拟内存可以映射同一个物理地址。这样当DAM将数据拷贝到内核缓存后,无需cpu将数据再拷贝到用户空间了。

实现零拷贝的方式

1. mmap + write

2. sendfile

3. 带有DMA收集拷贝功能的sendfile

MMAP拷贝 

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

1.用户进程通过调用mmap方法向操作系统内核发起IO调用,上下文从用户态切换至内核态
2.CPU利用DMA控制器,将数据从硬盘拷贝到内核缓冲区
3.上下文从内核态切换回用户态,mmap方法返回
4.用户进程通过调用write方法向操作系统内核再次发起IO调用,上下文从用户态切换至内核态
5.CPU将内核缓冲区的数据拷贝到socket缓冲区
6.CPU利用DMA控制器,将数据从socket缓冲器拷贝到网卡,上下文从内核态切换至用户态,write方法返回

使用mmap + write拷贝,CPU上下文切换4次,拷贝3次(1次CPU拷贝,2次DMA拷贝),由此可见减少了一次CPU拷贝。

sendfile拷贝

sendfile是Linux2.1版本后内核引入 的一个系统调用函数,原型如下

ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

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

1.用户进程发起sendfile系统调用,上下文从用户态切换至内核态
2.DMA控制器将数据从硬盘拷贝到内核缓冲区
3.CPU将读缓冲区中的数据拷贝到socket缓冲区
4.DMA控制器异步把数据从socket缓冲器拷贝到网卡
5.上下文从内核态切换至用户态,sendfile函数返回

使用sendfile拷贝,CPU上下文切换2次,拷贝3次 (1次CPU拷贝,2次DMA拷贝),由此可见减少了两次CPU切换,一次CPU拷贝。

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

1.用户进程发起sendfile系统调用,上下文从用户态切换至内核态
2.DMA控制器将数据从磁盘拷贝到内核缓冲器
3.CPU把内核缓冲区中的文件描述符信息(包括内核缓冲区的内存地址和偏移量)直接发送到socket缓冲区
4.DMA控制器根据文件描述符信息直接把数据从内核缓冲区拷贝到网卡
5.上下文切换至用户态,sendfile返回

sendfile +DMA scatter/gather,只产生2次CPU切换,2次DMA拷贝,这才是真正意义的零拷贝。

拷贝速度对比

Java中拷贝文件的方式

使用文件大小:2657KB

使用FileInputStream、FileOutputStream传统拷贝

private static void fileCopy(){
            FileInputStream fileInputStream = null;
            FileOutputStream fileOutputStream = null;

            try {
                Long t1 = System.currentTimeMillis();
                fileInputStream = new FileInputStream(new File("C:\\Users\\XXX\\IdeaProjects\\text.txt"));
                fileOutputStream = new FileOutputStream(new File("C:\\Users\\XXX\\IdeaProjects\\FileOutputStream.txt"));
                int readData = 0;
                byte[] buf = new byte[1024*1024*10];
                while (-1 != (readData = fileInputStream.read(buf))) {
                    fileOutputStream.write(readData);
                }
                Long t2 = System.currentTimeMillis();
                System.out.println(t2-t1);
            } catch (Exception e) {
                e.printStackTrace();
            }  finally {
                try {
                    fileInputStream.close();

                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    fileOutputStream.close();

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

耗时:11

MMAP(FileChannle的read、write)拷贝

private static void mmapCopy() {
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        FileChannel readChannel = null;
        FileChannel wirteChannel = null;

        try {
            Long t1 = System.currentTimeMillis();
            fileInputStream = new FileInputStream(new File("C:\\Users\\xxx\\IdeaProjects\\text.txt"));
            fileOutputStream = new FileOutputStream(new File("C:\\Users\\xxx\\IdeaProjects\\MMAP.txt"));
            readChannel = fileInputStream.getChannel();
            wirteChannel = fileOutputStream.getChannel();
            int n = 0;

            ByteBuffer buf = ByteBuffer.allocate(1024*1024*10);
            while (readChannel.read(buf) != -1) {
                buf.flip();
                wirteChannel.write(buf);
                buf.clear();
                wirteChannel.force(true);
            }

            Long t2 = System.currentTimeMillis();
            System.out.println(t2 - t1);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                readChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                wirteChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fileInputStream.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fileOutputStream.close();

            } catch (IOException e) {
                e.printStackTrace();
            }

        }

    }

 耗时:27

sendfile拷贝(FileChannle的transferTo)

private static void senfileCopy() {
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        FileChannel readChannel = null;
        FileChannel wirteChannel = null;

        try {

            fileInputStream = new FileInputStream(new File("C:\\Users\\XXX\\IdeaProjects\\text.txt"));
            fileOutputStream = new FileOutputStream(new File("C:\\Users\\XXX\\IdeaProjects\\SendFile.txt"));
            readChannel = fileInputStream.getChannel();
            wirteChannel = fileOutputStream.getChannel();

            Long t1 = System.currentTimeMillis();
            long len = readChannel.size();
            long position = readChannel.position();
            readChannel.transferTo(position,len,wirteChannel);
            Long t2 = System.currentTimeMillis();
            System.out.println(t2 - t1);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                readChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                wirteChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fileInputStream.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fileOutputStream.close();

            } catch (IOException e) {
                e.printStackTrace();
            }

        }

    }

 耗时:3

在小数据量下传统方式和mmap方式没有太大的区别,但是sendFile方式差距还是比较明显的。

传统拷贝mmap+writesendfilesendfile+scatter、gather
CPU切换次数4422
CPU拷贝次数2110
DMA拷贝次数2222
原因存在用户缓冲区和内核缓冲区,使用DMA技术,CPU需要将数据从内核缓冲区到用户缓冲区来回读取增加虚拟内存地址映射,CPU直接通过映射招到内核态的数据copy到Socket缓冲区使用sendfile减少CPU的切换次数使用sendfile减少CPU的切换次数,且CPU不再将数据复制到Socket缓冲区,而是把数据的位置和长度给到Socket缓冲区,由DMA根据位置和长度直接从内核缓冲区复制到磁盘
系统调用read/writemmap/writesenfilesendfile+scatter/gather

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

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

相关文章

Error: [mobx-miniprogram] no store specified (小程序全局数据共享bug)

话不多说,上bug!!! 这个错误提示引入的store文件没有被指定,但是看了一下以为是后面的路径没引对,就重新引入还是不行,页面效果渲染不出来,然后查文档也没遇到类似的问题&#xff0…

【JSP】JSTL汇总——源码解析

JSTL什么是JSTL使用JSTL的步骤JSTL标签的原理分析标签源码看核心标签库中的forEach标签主标签库常用标签forEach标签begin、end、step属性stuStatus属性if标签test属性var和scopechoose和when标签什么是JSTL JSTL全称为 Java Standard Tag Library(Java标准标签库&…

[附源码]计算机毕业设计springboot区域医疗服务监管可视化系统

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

C++读写Excel有许多开源库

1、OpenXLSX GitHub - troldal/OpenXLSX: A C library for reading, writing, creating and modifying Microsoft Excel (.xlsx) files. 依赖于第三库: PugiXMLZippy (C wrapper around miniz)Boost.Nowide (for opening files with non-ASCII names on Windows)…

(二)初识Vue

文章目录Vue环境搭建第一步:Vue下载第二步:安装开发者调试工具第一个Vue程序第一步:引入Vue.js第二步:关闭生产提示第三步:准备容器第四步:创建Vue实例第一个Vue程序的小细节细节一:如果使用的是…

Vue 2.0/3.0

vue核心 vue官网 1、 英文官网: https://vuejs.org/ 2、中文官网: https://cn.vuejs.org/ 模板语法 插值语法(双大括号表达式)指令语法(以 v-开头) Vue模板语法有2大类: 1.插值语法: 功能:用…

01【SpringMVC快速入门】

文章目录01【SpringMVC快速入门】一、SpringMVC快速入门1.1 SpringMVC概述1.1.1 SpringMVC是什么1.2 SpringMVC环境搭建1.2.1 创建web项目1.2.2 Maven依赖:1.2.3 配置web.xml1.2.4 SpringMVC配置1.2.5 编写Controller1.2.6 编写视图页面01【SpringMVC快速入门】 一…

高等数学(第七版)同济大学 习题10-4 (前7题)个人解答

高等数学(第七版)同济大学 习题10-4(前7题) 函数作图软件:Mathematica 1.求球面x2y2z2a2含在圆柱面x2y2ax内部的那部分面积.\begin{aligned}&1. \ 求球面x^2y^2z^2a^2含在圆柱面x^2y^2ax内部的那部分面积.&\e…

设置Oracle表空间只读

如果对表存储的规划较好&#xff0c;将业务相关的表都放在几个表空间里&#xff0c;可以通过设置表空间只读的方式&#xff0c;让这些表只读&#xff1a; alter tablespace <tablespace name> read only; 解除只读&#xff1a; alter tablespace <tablespace name>…

2023年软考备考,软件设计师知识点速记,速看

2023上半年软考中级软件设计师知识点速记分享给大家&#xff0c;快来一起打卡学习吧&#xff01; 1、码制的表示 2、浮点数的表示 &#xff08;1&#xff09;浮点数格式 阶码决定范围&#xff0c;阶码越长&#xff0c;范围越大&#xff1b; 尾数决定精度&#xff0c;尾数越…

Android编写一个视频监控App

Android编写一个视频监控App 很久没写app了&#xff0c;小项目需要写一个rtmp拉流的视频监控app&#xff0c;简单记录一下。 参考&#xff1a;Android实现rtmp推拉流摄像头&#xff08;三&#xff09;_空空7的博客-CSDN博客_android rtmp拉流 相关库 引用外部库首先添加这个…

C_C++文件,字符串和控制台格式化处理总结

在实际业务开发中经常会用到文件&#xff0c;字符串和控制台格式化操作&#xff0c;格式化操作无非就是将数据转成指定格式存储在文件或者字符串&#xff0c;或者显示在控制台上&#xff0c;或者反过来。本篇结合实际工作将C/C语言中常用的文件&#xff0c;字符串和控制台常用格…

[附源码]Python计算机毕业设计Django高血压分析平台

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

数据抓取-bs4、XPath、pyquery详细代码演示

数据抓取-bs4、XPath、pyquery 一般抓取某个网站或者某个应用的内容&#xff0c;内容分为两个部分 非结构化的文本&#xff1a;HTML文本 结构化的文本&#xff1a;JSON、XML 非结构化的数据常见的解析方式有&#xff1a;XPath、CSS选择器、正则表达式 XPath语言 XPath是X…

golang知识点整理

目录 1、goroutine GMP模型 2、goroutine阻塞的处理 3、goroutine内存泄漏 4、map原理、扩容 5、go内存管理 6、go的gc 1、goroutine GMP模型 1. G代表一个goroutine对象&#xff0c;每次go调用的时候&#xff0c;都会创建一个G对象 2. M代表一个线程&#xff0c;每次创建…

JavaScript和Node.js的关系

JavaScript和Node.js的关系 JavaScript是一门编程语言&#xff08;脚本语言&#xff09;&#xff0c;JavaScript以前是在浏览器里执行的&#xff0c;需要浏览器里的JavaScript引擎&#xff0c;Firefox有叫做Spidermonkey的引擎&#xff0c;Safari有JavaScriptCore的引擎&#x…

第2章物理层——2.数据通信基础知识

一.数据通信系统模型 一个通信系统可以划分为三大部分: 源系统&#xff08;发送端&#xff09;&#xff0c;传输系统&#xff08;传输网络&#xff09;&#xff0c;目的系统&#xff08;接收端&#xff09; [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传…

GIS工具maptalks开发手册(二)03-02——示例之json格式添加绘制工具、渲染点、文字和多个面

GIS工具maptalks开发手册(二)03-02——示例之json格式添加绘制工具、渲染点、文字和多个面 layer参数——https://maptalks.org/maptalks.js/api/0.x/Layer.html 1、json格式渲染点和面 效果-json格式渲染点和面 代码 index.html <!DOCTYPE html> <html> <…

spring boot使用自定义过滤器实现接口认证

spring boot使用自定义过滤器实现接口认证自定义过滤器创建FilterConfig类加密 解密 验证CipherFilter其他工具类AES 128 加密工具bean未加载前获取bean接口效果swagger访问Apipost 错误请求Apipost 正确请求自定义过滤器 创建MyFilter 类 去实现Filter接口 根据业务逻辑&…

(Git) git使用入门学习

文章目录打开基本操作拉代码常用指令设置用户查看历史版本分支管理配置公钥基于VS CodeEND打开 Git Bash Here 即打开命令行的形式 基本操作 拉代码 # git clone 地址 $ git clone https://gitee.com/heaven-sent-lotus/test.git常用指令 # 查看状态 git status# 添加到工作区…