零拷贝机制在文件传输中的使用手法

文章目录

  • 文件传输(读取与发送)中的拷贝与上下文切换
  • 零拷贝技术
    • sendfile
    • sendfile + SG-DMA
    • mmap + write
    • splice
    • Direct I/O
  • 经典应用

文件传输(读取与发送)中的拷贝与上下文切换

如果服务端要提供文件传输的功能,最简单的方式是:
1、将磁盘上的文件读取出来
2、通过网络协议将内容发送给客户端
传统IO的工作方式是,数据读取和写入从用户空间到内核空间来回赋值,内核空间数据通过IO接口从磁盘读取/写入。
就如同下面这两个api的使用:

File.read(file, buf, len);
Socket.send(socket, buf, len);

这个场景下会发生4次数据拷贝+4次上下文切换
read系统调用,从用户态到内核态 切换 ,CPU从磁盘 拷贝 数据到内核pagecache。
read返回,从内核态 切换 到用户态,CPU从pagecache 拷贝 数据到用户缓冲区。
send,可以看作write。
write系统调用,从用户态到内核态切换,CPU从用户缓冲区拷贝数据到内核socket缓冲区
然后CPU从内核socket缓冲区拷贝数据到网卡上
最后write返回,从内核态 切换 到用户态。
当然可以使用DMA技术,替代CPU在IO外设与内核缓冲区之间的拷贝。因为DMA仅仅只能用于设备之间交换数据时的数据拷贝,内存之间的数据拷贝用不了DMA。
这样优化下来会发生2次CPU数据拷贝+2次DMA数据拷贝+4次上下文切换,接下来的讲解都是基于这个成本来的。
想要提高性能就需要减少上下文切换和CPU拷贝的次数。

零拷贝技术

零拷贝是一种高效的数据传输机制,在追求低延迟的传输场景中经常使用,具体思想是计算机执行操作时,CPU不需要将数据从某处内存复制到另外一个特定区域。
现存的比较常用的零拷贝方法有下面几个:

  • sendfile
  • mmap + write
  • splice
  • Direct I/O
    不同的技术使用的场景也是不同的,使用时请结合业务逻辑。

sendfile

应用场景:用户从磁盘读取文件数据后不需要经过CPU计算/处理就直接通过网络传输出去
典型应用:MQ
Linux版本:2.1

ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
// out_fd:目的端文件描述符
// in_fd:源端文件描述符
// offset:源端偏移量
// count:数据长度
// 返回值:实际复制数据的长度

我们只需要传递文件描述符就可以代替数据的拷贝了,直接替代read+write操作。sendfile一次系统调用就相当于之前的两次系统调用。这是因为page cache和socket buffer均在内核空间,sendfile直接把内核缓冲区数据拷贝到socket缓冲区上了,直接省略掉用户态。
成本:1次系统调用,2次上下文切换,1次CPU数据拷贝,2次DMA数据拷贝

sendfile + SG-DMA

Linux版本:2.4
如果网卡支持SG-DMA(The Scatter-Gather Direct Memory Access)技术,可以直接将内核态缓冲区数据直接SG-DMA到网卡上,省略了内核态缓冲区->socket缓冲区->网卡的步骤。
成本:1次系统调用,2次上下文切换,1次DMA数据拷贝,1次SG-DMA数据拷贝
这就是真正的zero-copy,完全没有通过内存层面去拷贝数据,全程使用DMA传输。
局限性:当然sendfile也是有局限性的,它直接隔离了应用程序对数据操作,如果需要从数据中提取统计信息或者进行加解密,sendfile根本使用不了。

mmap + write

mmap:memory map,一种内存映射文件的方法。即将一个文件或者其他对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址直接对映。这样进程就可以采用指针的方式直接读写操作这一块内存,系统自动回写脏页到对应的文件磁盘上。这样对文件操作就不需要调用read+write了。并且内核空间对这段区域的修改也直接反映在了用户空间,从而实现不同进程间的文件共享。
在这里插入图片描述
mmap技术特点如下:
1、用户空间的mmap file使用虚拟内存,实际上不占有物理内存,只有内核空间的kernel buffer cache才占据实际物理内存
2、mmap需要配合write
3、mmap仅仅避免内核空间到用户空间的CPU数据包被,但是内核空间内部还是需要CPU负责数据拷贝
使用mmap流程如下:
1、用户调用mmap,从用户态切换到内核态,将内核缓冲区映射到用户缓存区
2、DMA控制器将数据从磁盘拷贝到内核缓冲区
3、mmap返回,从内核态切换到用户态
4、用户进程调用write,尝试把文件数据写到内核socket buffer中,从用户态切换到内核态
5、CPU将内核缓冲区数据拷贝到socket buffer
6、DMA控制器将数据从socket buffer拷贝到网卡
7、write返回
成本:2次系统调用、4次上下文切换、1次CPU数据拷贝、2次DMA数据拷贝
应用场景
1、多个线程以只读方式同时访问一个文件,mmap机制下的多线程共享同一个物理内存空间,节约了内存。
例子:多个进程可以依赖于同一个动态链接库,利用mmap可以实现内存仅仅加载一份动态链接库,多个进程共享此库
2、mmap可用于进程间通信,对于同一个文件对应的mmap分配的物理内存天然多线程共享,可以依赖于操作系统的同步原语
3、mmap比sendfile多了一次CPU参与的内存拷贝,但是用户空间与内核空间之间不需要数据拷贝,所以效率也很高

splice

Linux版本:2.6.17

#include <fcntl.h>
ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);

splice用于在两个文件描述符之间移动数据, 也是零拷贝。
fd_in参数是待输入描述符。如果它是一个管道文件描述符,则off_in必须设置为NULL;否则off_in表示从输入数据流的何处开始读取,此时若为NULL,则从输入数据流的当前偏移位置读入。

fd_out/off_out与上述相同,不过是用于输出。

len参数指定移动数据的长度。

flags参数则控制数据如何移动:

SPLICE_F_NONBLOCK:splice 操作不会被阻塞。然而,如果文件描述符没有被设置为不可被阻塞方式的 I/O ,那么调用 splice 有可能仍然被阻塞。
SPLICE_F_MORE:告知操作系统内核下一个 splice 系统调用将会有更多的数据传来。
SPLICE_F_MOVE:如果输出是文件,这个值则会使得操作系统内核尝试从输入管道缓冲区直接将数据读入到输出地址空间,这个数据传输过程没有任何数据拷贝操作发生。
2. 使用splice时, fd_in和fd_out中必须至少有一个是管道文件描述符。

调用成功时返回移动的字节数量;它可能返回0,表示没有数据需要移动,这通常发生在从管道中读数据时而该管道没有被写入的时候。

失败时返回-1,并设置errno

splice系统调用直接在内核空间的read buffer 和socket buffer之间建立了管道,避免了用户缓冲区和socket buffer之间的CPU拷贝
成本:1次splice系统调用、1次pipe调用、2次上下文切换、2次DMA数据拷贝
局限性
1、用户程序不能对数据进行操作,与sendfile类似
2、Linux管道缓冲机制,可以用于任意两个文件描述符中传输数据,但是其中一个必须是管道设备

Direct I/O

缓存文件I/O:用户空间要读取一个文件并不是直接与磁盘进行交互看,而是中间夹了一层缓存,即page cache
直接文件I/O:用户空间读取文件直接与磁盘交互,数据直接存储在用户空间中,没有中间page cache曾,绕过了内核。
部分操作系统中,在直接文件I/O模式下,write虽然能够保证文件数据落盘,但是文件元数据不一定落盘,所以还需要执行一次fsync操作。
局限性
1、设备之间数据传输通过DMA,所以用户空间的数据缓冲区内存页必须进行页锁定,这是为了防止其物理页地址被交换到磁盘或者被移动到新的地址导致DMA去拷贝数据时在指定地址找不到内存页从而引发缺页异常,而页锁定的开销也不小,所以应用程序必须分配和注册一个持久的内存池,用户数据缓冲。(应用程序手动做缓存池)
2、如果在应用程序的缓存中没有找到,那么就直接从磁盘加载,十分缓慢
3、应用层引入缓存管理以及底层硬件管理(页锁定),很麻烦

经典应用

在之前的笔记中有谈到kafka高性能的原因之一就是使用了zero-copy:消息队列重要机制讲解以及MQ设计思路(kafka、rabbitmq、rocketmq,这里稍微拓展一下:
生产者发消息给kafka,kafka将消息持久化落盘。
消费者从kafka拉取消息,kafka从磁盘读取一批数据,通过网卡发送。
接收消息持久化的时候使用到了mmap机制,对接收的数据持久化。发送消息的时候使用sendfile从持久化介质中读取数据然后对外发送。
sendfile避免了内核空间到用户空间的CPU数据拷贝,同时sendfile基于page cache实现,如果有多个消费者同时消费一个topic消息,消息会在page cache上缓存,就只需要一次磁盘IO了。
所以我们应该熟悉掌握sendfile 和 mmap

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

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

相关文章

POJ 3233 Matrix Power Series 矩阵快速幂 + 二分

题意&#xff1a;求矩阵的次方和 解题思路&#xff1a;最容易想到方法就是两次二分因为 我们可以把一段 A^1 A^2 .......A^K 变成 A^1 ..A^(K/2) ( A^1 ..A^(K/2))*(A^(k/2)) 当k 为奇数的时候 或者 A^1 ..A^(K/2) ( A^1 ..A^(K/2))*(A^(k/2)) A^K 当K 为偶数的时候…

时间序列进行分析的一些手法以及代码实现(移动平均、指数平滑、SARIMA模型、时间序列的(非)线性模型)

文章目录1、移动平均moving average方法weighted average方法2、指数平滑单指数平滑 exponential_smoothing双指数平滑三指数平滑 Triple exponential smoothing3、平稳性以及时间序列建模SARIMA模型4、时间序列的&#xff08;非&#xff09;线性模型时间序列的滞后值使用线性回…

三大平衡树(Treap + Splay + SBT)总结+模板

Treap树 核心是 利用随机数的二叉排序树的各种操作复杂度平均为O(lgn) Treap模板&#xff1a; #include <cstdio> #include <cstring> #include <ctime> #include <iostream> #include <algorithm> #include <cstdlib> #include <cmath…

mysqld进程 ut_delay 占用率过高

采用性能分析工具perf top -p mysqld进程 在测试mysql数据库时&#xff0c;用perf top如果看到热点函数是ut_delay或者_raw_spin_lock的话&#xff0c;说明锁争用比较严重。 ut_delay这是innodb的一个自旋琐。也就是说&#xff0c;在这里由于锁等待&#xff0c;innodb不停地在…

滑动窗口在重构数据集的作用

step1&#xff1a;使用滑动窗口重构数据集 给定时间序列数据集的数字序列&#xff0c;我们可以将数据重构为看起来像监督学习问题。 我们可以通过使用以前的时间步作为输入变量并使用下一个时间步作为输出变量来做到这一点。 通过观察重构后的数据集与原本的时间序列&…

sliverlight - Unhandled Error in Silverlight Application错误

使用firebug控制台输出错误&#xff1a; Unhandled Error in Silverlight Application 查询“GetFlow_Process”的 Load 操作失败。远程服务器返回了错误: NotFound。 位于 System.ServiceModel.DomainServices.Client.OperationBase.Complete(Exception error) 位于 System.S…

前向验证对于模型的更新作用

首先&#xff0c;让我们看一个小的单变量时间序列数据&#xff0c;我们将用作上下文来理解这三种回测方法&#xff1a;太阳黑子数据集。该数据集描述了刚刚超过 230 年&#xff08;1749-1983 年&#xff09;观察到的太阳黑子数量的每月计数。 数据集显示了季节之间差异很大的…

PHP-面向对象(八)

1、多态的介绍与优势 多态性是继抽象和继承后&#xff0c;面向对象语言的第三个特征。从字面上理解&#xff0c;多态的意思是“多种形态”&#xff0c;简单来说&#xff0c;多态是具有表现多种形态的能力的特征&#xff0c;在OO中是指“语言具有根据对象的类型以不同方式处理。…

双指数平滑中参数对于预测模型的影响

先看看α 在β一致的情况下&#xff0c;α越小&#xff0c;模型越滞后。 再看看β 在α一致的情况下&#xff0c;β越大&#xff0c;模型对于趋势的预测更敏锐。

分页查询

分页查询算是比较常用的一个查询了在DAO层主要是查两个数据第一个总条数第二个要查询起始记录数到查询的条数当第一次点击查询时候(非下一页时Page类里面预设的就是 index就是0 pageSize是预设值当点击下一页的时候 index 和 pageSize带的就是页面上面给的值了分页的页面一般的…

me23n去价格

SELECT knumv kposn AS ebelp kschl kbetr kpein kwert INTO CORRESPONDING FIELDS OF TABLE gt_konv FROM konv FOR ALL ENTRIES IN gt_ekpo WHERE knumv gt_ekpo-knumv AND kinak EQ AND kschl IN (PB00,PBXX,P101).转载于:…

使用Bootstrap-table创建表单,并且与flask后台进行数据交互

文章目录引用css和js使用htmljavascriptflaskmysql参考引用css和js Bootstrap-table为这些文件提供了 CDN 的支持&#xff0c;所以不需要下载.js .css文件就可以直接用了&#xff0c;十分方便 <!-- Latest compiled and minified CSS --> <link rel"stylesheet…

使用Xcode和Instruments调试解决iOS内存泄露

虽然iOS 5.0版本之后加入了ARC机制&#xff0c;但由于相互引用关系比较复杂时&#xff0c;内存泄露还是可能存在。所以了解原理很重要。 这里讲述在没有ARC的情况下&#xff0c;如何使用Instruments来查找程序中的内存泄露&#xff0c;以及NSZombieEnabled设置的使用。 本文假设…

五大主流浏览器 HTML5 和 CSS3 兼容性比较

转眼又已过去了一年&#xff0c;在这一年里&#xff0c;Firefox 和 Chrome 在拼升级&#xff0c;版本号不断飙升&#xff1b;IE10 随着 Windows 8 在去年10月底正式发布&#xff0c;在 JavaScript 性能和对 HTML5 和 CSS3 的支持方面让人眼前一亮。这篇文章给大家带来《五大主流…

精通 VC++ 实效编程280例 - 02 菜单和光标

菜单和关闭时重要的 Windows 资源之一。SDK 中&#xff0c;用 HCURSOR 和 HMENU 分别表示菜单和光标的句柄。MFC 中&#xff0c;CMenu 类封装了菜单的功能。 23 动态添加和删除菜单项 添加菜单项可以调用 CMenu::AppendMenu 或 CMenu::InserMenu 函数&#xff0c;删除菜单项可以…

我的osu游戏程序设计(oo)

osu是一款社区元素为主旨的音乐游戏,由澳大利亚人Dean Herbert (peppy)独立制作并运行. 游戏的方法简单,就是 1. 圈圈(Circle)&#xff1a;圈圈(Circle) 50。没打中显示X,并减少生命值。圈中序号的最后一个的300、100会显示为激300、喝100。2.滑条(Slider) : 在开始端点击按住不…

Android programming on Mac 之安装Eclipse

1.安装包在此链接下载&#xff1a; http://developer.android.com/sdk/index.html google GoAgent翻墙不好用&#xff0c;更新了host文件也不行&#xff0c;整了半天&#xff0c;还是一怒之下续签了vpn账号。早知如此&#xff0c;何必折腾。~~~~(>_<)~~~~ 更新文件时…

c++关于虚表的一些笔记

文章目录1、虚函数表指针2、多态构成的条件3、重载、重写、重定义 三者区别4、继承与虚函数5、单继承中的虚函数表无虚函数覆盖有虚函数覆盖6、单继承中的虚函数表无虚函数覆盖有虚函数覆盖参考看《深度探索c对象模型》的时候对虚表有了点疑惑&#xff0c;正好网上有些文章解除…

C语言技巧:把单一元素的数组放在末尾,struct可以拥有可变大小的数组

《C 对象模型》第19页有这样一句话 C程序员的巧计有时候却成为c程序员的陷阱。例如把单一元素的数组放在一个struct的末尾&#xff0c;于是每个struct objects可以拥有可变数组的数组&#xff1a; struct mumble {/* stuff */char pc[1]; };//从文件或标准输入装置中取得一个…

探讨C++ 变量生命周期、栈分配方式、类内存布局、Debug和Release程序的区别(二)...

看此文&#xff0c;务必需要先了解本文讨论的背景&#xff0c;不多说&#xff0c;给出链接&#xff1a; 探讨C 变量生命周期、栈分配方式、类内存布局、Debug和Release程序的区别&#xff08;一&#xff09; 本文会以此问题作为讨论的实例&#xff0c;来具体讨论以下四个问题&a…