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

文章目录

  • 文件传输(读取与发送)中的拷贝与上下文切换
  • 零拷贝技术
    • 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,一经查实,立即删除!

相关文章

Effective Modern C++翻译(3)-条款2:明白auto类型推导

条款2 明白auto类型推导 如果你已经读完了条款1中有关模板类型推导的内容&#xff0c;那么你几乎已经知道了所有关于auto类型推导的事情&#xff0c;因为除了一个古怪的例外&#xff0c;auto的类型推导规则和模板的类型推导规则是一样的&#xff0c;但是为什么会这样呢&#xf…

信息论与编码复习

若信源有m种消息&#xff0c;且每个消息是以相等可能产生的&#xff0c;则该信源的信息量可表示为Ilogm。 信息率是通过接收到的信息可获得的发送信息的信息量,即互信息。单位:bit/符号。 信息速率是单位时间内传输的信息量。单位:bit/s 码字CodeWord。由若干个码元组成&#x…

拖拽碰撞效果最终版

拖拽碰撞效果最终版&#xff0c;没准还有bug&#xff0c;不过现在在各个浏览器下效果是对的&#xff0c;代码需要精简一些&#xff0c;以后有时间了在弄吧&#xff0c;现在先不理了&#xff0c;感冒了&#xff0c;没有心情整理 <!DOCTYPE HTML> <html lang"en-US…

Python 如何利用函数修改函数外list?

#在函数内修改列表的时候&#xff0c;在列表后面加上[:]&#xff0c;无论几维列表均可。 def foo(listA):listA[:] [1,2,3] def foo2(listB):listB [1,2,3] listA [4,5,6] listB [4,5,6] foo(listA) foo2(listB) print listA #result: [1,2,3] print listB #result: [4,5,6…

图片压缩android bitmap compress(图片压缩)

本文纯属个人见解&#xff0c;是对前面学习的总结&#xff0c;如有描述不正确的地方还请高手指正~ 有些场景中&#xff0c;须要照相并且上传到服务&#xff0c;但是由于图片的巨细太大&#xff0c;那么就 上传就 会很慢(在有些网络情况下)&#xff0c;而且很耗流量&#xff0c;…

linux进程间通信快速入门【一】:管道编程

介绍 管道本质上就是一个文件&#xff0c;前面的进程以写方式打开文件&#xff0c;后面的进程以读方式打开。这样前面写完后面读&#xff0c;于是就实现了通信。虽然实现形态上是文件&#xff0c;但是管道本身并不占用磁盘或者其他外部存储的空间。在Linux的实现上&#xff0c;…

返回长度hdu 1518 square

查了好多资料&#xff0c;发现还是不全&#xff0c;干脆自己整理吧&#xff0c;至少保证在我的做法正确的&#xff0c;以免误导读者&#xff0c;也是给自己做个记载吧&#xff01; 题目的意思是比较明显的&#xff0c;就是当初给你m根木棒&#xff0c;当初让你判断利用这些木棒…

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;线性模型时间序列的滞后值使用线性回…

政权组织形式

神马国家结构、政权组织形式的 现在我比较明确的是英国的政权组织形式是式君主立宪制、美国是总统制、中国是人民代表大会制。 目前,世界各国采用的国家结构可分为单一制和复合制两大类。其中&#xff0c;复合制国家结构形式主要包括联邦制和邦联制两种类型。英国、法国、意大利…

三大平衡树(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不停地在…

TClientDataSet使用要点

TClientDataSet控件继承自TDataSet&#xff0c;其数据存储文件格式扩展名为 .cds&#xff0c;是基于文件型数据存储和操作的控件。该控件封装了对数据进行操作处理的接口和功能&#xff0c;而本身并不依赖上述几种数据库驱动程序&#xff0c;基本上能满足单机"瘦"数据…

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

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;观察到的太阳黑子数量的每月计数。 数据集显示了季节之间差异很大的…

2014年9月21日_随笔,jdic,ETL,groovy,Nutz好多东西想学

&#xff08;1&#xff09;老妈十一要回老家&#xff0c;才突然发现买票好难啊。有亲朋很重要 &#xff08;2&#xff09;这周我做了什么。jdic,ETL,groovy, Nutz好多东西想学。 Nutz开发成员专访、Nutz优酷视频(演讲)、Nutz 入门教程、 &#xff08;3&#xff09;想改变&#…

PHP-面向对象(八)

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

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

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

SQL 性能不佳的几个原因

SQL 性能不佳的几个原因 •不准确的统计数据•差劲的索引•差劲的查询设计 •差劲的执行计划&#xff0c;通常是由不正确的参数引起的•过度阻塞和死锁 •非基于集合的操作•不良数据库设计 •过度碎片 •不能重复使用执行计划 •查询频繁重编译 •不当使用游标 •数据库日志的…