首页 > 其他分享 >零拷贝

零拷贝

时间:2023-08-31 11:33:24浏览次数:112  
标签:DMA Cache 内核 拷贝 CPU 内存

小林coding《什么是零拷贝》笔记

参考:

The Linux Kernel Linux :Concepts overview

兰新宇 :  Linux中的mmap映射 [一]

ALEX XU  徐旭 :Why is Kafka fast?

 

每个存储器只和相邻的一层存储器设备打交道,并且存储设备为了追求更快的速度,所需的材料成本必然也是更高,也正因为成本太高,所以 CPU 内部的寄存器、L1\L2\L3 Cache 只好用较小的容量,相反内存、硬盘则可用更大的容量,这就我们今天所说的存储器层次结构。

速度越快的存储器,造价成本往往也越高,存储容量也就越小。下面这张表格是不同层级的存储器之间的成本对比图:

 

 

Page Cache

物理内存是易失性的,将数据存入内存的常见情况是从文件中读取数据。每当读取文件时,数据都会放入物理内存 Page Cache 中,以避免后续读取时昂贵的磁盘访问。类似地,当写入文件时,数据被放置在页面缓存中并最终进入磁盘等存储设备。写入的页面被标记为脏页,当 Linux 决定将放置 Page Cache 的内存用于其他目的时,它会确保将脏页更新到磁盘。

这块 放置 Page Cache 的物理内存是由 linux 内核虚拟内存管理的,用户进程需要切换到内核态才能访问。

 

为什么要有 DMA 技术?

在没有 DMA 技术前,I/O 的过程是这样的:

整个数据的传输过程,都要需要 CPU 亲自参与搬运数据的过程,而且这个过程,CPU 是不能做其他事情的。

用 DMA 控制器进行数据传输

什么是 DMA 技术?简单理解就是,在进行 I/O 设备和内存的数据传输的时候,数据搬运的工作全部交给 DMA 控制器,而 CPU 不再参与任何与数据搬运相关的事情,这样 CPU 就可以去处理别的事务。

 

 

传统的文件传输

场景:将磁盘上的文件读取出来,然后通过网络协议发送给客户端。

  • 上图 read 时的缓存区就是内核态管理Page Cache,从磁盘读取的文件会放在那里。需要由系统调用 read() 切换到内核态来拷贝到用户缓冲区读取。
  • 由 TCP 发送数据的 socket 缓冲区也是内核管理的(Socket 也是一种文件)。需要由系统调用 write() 切换到内核态来写入。

发生了 4 次用户态与内核态的上下文切换因为发生了两次系统调用,一次是 read() ,一次是 write()每次系统调用都得先从用户态切换到内核态,等内核完成任务后,再从内核态切换回用户态。

还发生了 4 次数据拷贝,其中两次是 DMA 的拷贝,另外两次则是通过 CPU 拷贝的。

所以,要想提高文件传输的性能,就需要减少「用户态与内核态的上下文切换」和「内存拷贝」的次数。

 

mmap + write

 mmap() 系统调用函数可以把内核管理的 Page Cache 文件缓存「映射」到用户空间,Page Cache 就可以由用户进程直接读写了。这样,操作系统内核与用户空间就不需要再进行任何的数据拷贝操作。

于是 使用 mmap() 来代替 read()减少了一次数据拷贝的过程

但是 mmap() 是懒加载,实际访问时会触发大量缺页中断建立实际的物理内存映射。另一方面,随着硬件性能的发展,内存拷贝消耗的时间已经大大降低了。所以啊,很多情况下,mmap()的性能反倒是比不过read()和write()的。

 

sendfile

Linux 内核版本 2.1 中,提供了一个专门发送文件的系统调用函数 sendfile(),函数形式如下:

#include <sys/socket.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

它的前两个参数分别是目的端 和 源端 的文件描述符,后面两个参数是源端的偏移量和复制数据的长度,返回值是实际复制数据的长度。

这就是所谓的零拷贝(Zero-copy)技术,数据【直接在内核态】由【源文件 - Page Cahe 缓冲区】 发到【目标文件 - Socket 缓冲区】 ,也就是说全程没有通过 CPU 来把内存数据从内核态搬运到用户空间,所有的数据都是通过 DMA 来进行传输的。。

零拷贝技术的文件传输方式相比传统文件传输的方式,减少了 2 次上下文切换和数据拷贝次数,只需要 2 次上下文切换和数据拷贝次数,就可以完成文件的传输,而且 2 次的数据拷贝过程,都不需要通过 CPU,2 次都是由 DMA 来搬运。

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

 

使用零拷贝技术的项目

Kafka

追溯 Kafka 文件传输的代码,你会发现,最终它调用了 Java NIO 库里的 transferTo 方法:

@Overridepublic 
long transferFrom(FileChannel fileChannel, long position, long count) throws IOException { 
    return fileChannel.transferTo(position, count, socketChannel);
}

如果 Linux 系统支持 sendfile() 系统调用,那么 transferTo() 实际上最后就会使用到 sendfile() 系统调用函数。

生产者消息写入 PageCache 后(即 CommitLog 文件),直接在内核态 sendFile 发到 Socket 缓冲区发送给消费者。

RocketMQ

rocketMQ 没有采用零拷贝技术,而是用了 mmap + write

标签:DMA,Cache,内核,拷贝,CPU,内存
From: https://www.cnblogs.com/suBlog/p/17667930.html

相关文章

  • c++ 删除 类的拷贝和赋值函数
      #pragmaonce#include"include/cef_app.h"classHttpSchemeFactory:publicCefSchemeHandlerFactory{public:HttpSchemeFactory()=default;//删除拷贝函数HttpSchemeFactory(constHttpSchemeFactory&)=delete;//删除赋值函数H......
  • 面向对象(构造、析构、拷贝、赋值、友元)
    文章目录一、面向对象的基本概念1、面试题--->面向对象的基本原则?2、面试题--->空类会创造哪些成员函数?二、类和结构体1、面试题--->类和结构体的区别?三、成员变量及其初始化四、构造函数和析构函数1、构造函数和析构函数的调用顺序2、面试题--->析构函数为什么要定义为虚函数?3、......
  • JS手写代码实现深拷贝
    /***深拷贝*/constobj1={age:20,name:'xxx',address:{city:'beijing'},arr:['a','b','c']}constobj2=obj1obj2.address.city='shanghai'console.log(o......
  • 求求你不要在使用BeanUtils进行拷贝了
    哈喽,大家好,我是指北君。最近接手一个项目发现有些接口只是做了一些简单的单表查询业务,但是却耗时八百多毫秒,明显不太正常,经排查发现时间都消耗在Apache的BeanUtils中对属性的拷贝上,整个流程使用了四次拷贝方法,使得整个方法耗时急剧增加。指北君在这里求求大家不要再使用BeanUtils......
  • java高频面试题(反射、对象拷贝)
    java高频面试题(反射、对象拷贝)什么是反射?反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力Java反射:在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法Java反射机制主要提供了以下功能:在运行时判断任意一个......
  • 浅拷贝、深拷贝
    理解1、浅拷贝:是对目标的简单复制。只能复制目标对象的值引用,并没有重新开辟新的存储空间,这导致只要其中一方值被修改,另一个也跟着被修改了值。2、深拷贝:内存地址是自主分配的,两个数据指向了不同的地址,数据元素发生改变时不会相互影响。不过,拷贝一般只针对Array和Object先来......
  • .bat执行拷贝和写入文件
    echooff::设置utf-8编码chcp65001::先删除web_out目录下的所有文件及子目录cd../web_outdel/Q/F/S*&rd/S/Q.::进入源代码路径中cd../source_code::执行打包命令callnpmrunbuild::进入dist文件中cddist::执行拷贝命令robocopy...\..\web_ou......
  • C++拷贝构造、赋值函数
    拷贝构造拷贝构造就是一种特殊版本的构造函数,格式:类名(const类名&that){    //执行给每个成员变量进行赋值  }什么时候会调用拷贝构造:当使用旧对象(已new的)给新对象(新new的)初始化时,会自动调用拷贝构造    Testt1;//调用无参构造Testt2=t1......
  • Java的深拷贝与浅拷贝的区别
    1、二者的区别浅拷贝:在拷贝一个对象时,对对象的基本数据类型的成员变量进行拷贝,但对引用类型的成员变量只进行引用的传递,并没有创建一个新的对象,当对引用类型的内容修改会影响被拷贝的对象。深拷贝:在拷贝一个对象时,除了对基本数据类型的成员变量进行拷贝,对引用类型的成员变量进行......
  • C#深拷贝
    最近在做winform项目,遇到页面上有同一个数据源的两个ComboBox,此时操作一个ComboBox,会影响到另一个ComboBox,究其原因,是因为数据源是引用类型,值栈引堆,虽然是不同的ComboBox,但是指针指向同一个内存地址,所以操作一个变量会影响到另一个,为解决这个问题,考虑使用深拷贝的方式。下边是我......