零拷贝技术深入分析

一、零拷贝

在前面的文章“深浅拷贝、COW及零拷贝”中对零拷贝进行过分析,但没有举例子,也没有深入进行展开分析。本文将结合实际的例程对零拷贝进行更深入的分析和说明。
在传统的IO操作中,以文件通过网络传输为例 ,一般会经历以下几个数据拷贝的过程:
磁盘缓冲区 ->内核缓冲区->用户缓冲区->内核网络缓冲区->网卡缓冲区
也就是数据要经历从IO到内核空间,再从内核到用户空间再进入内核空间然后才能通过IO发走,至少要有四次的内在拷贝。
而这就引出了零拷贝的概念:尽最大可能减少CPU参与数据拷贝的过程(直到完全不参与拷贝)。它主要有基于内核缓冲优化的零拷贝和DirectIO的零拷贝。
仍然以上面的链路来分析,可不可以直接从硬盘把数据(内核缓冲区)拷贝到网卡缓冲区,可不可以?可不可以不过用户缓冲区直接在内核内交互数据?这都是直接想到的解决问题的方法和手段。而实际上,零拷贝技术也就是按这种指导思想进行开展的。
零拷贝技术的实现有以下几种方法:
1、DirectIO
这个好理解,不通过各种中间环节直接和IO打交道。它主要应用于上层应用本身实现了磁盘的数据缓存,比如常见的数据库系统软件,那么就不需要再使用PageCache进行缓冲。这样就可以减少PageCache(内核缓冲区)的消耗(这可略过了计算中最大的中间商CPU)。而诸如下面的sendfile等,其实都基于PageCache优化的零拷贝。
2、新的函数sendfile(win:TransmitFile)
sendfile是Linux系统提供的系统API,它可以解决用户空间和内核空间的数据拷贝的次数问题;如果其和DMA技术(重点指SG-DMA(The Scatter-Gather Direct Memory Access))共同工作即sendfile+DMA,那么其效率更高,可以直接把数据文件从磁盘拷贝到网络缓冲区 。
sendfile有其一定的局限性,首先是标准不统一,另外一个就是无法在数据操作中间在用户空间对数据进行操作,比如从磁盘加载然后加解密等然后再发送,因为得不到具体的数据 ,这需要引起重视。
3、函数splice
splice技术更进一步,它接近于 sendfile和DMA的进一步效率提高,此函数在内核空间和网络缓冲区间建立管道,避免二者的CPU的拷贝。注意,此函数中的两个文件操作符必须有一个为管道操作符。
4、mmap
mmap方式大家比较熟悉,这里就简单说明一下,其实mmap的零拷贝就是通过内存映射提供一个内核和用户空间直接通信的手段。mmap应用非常多,最典型的是安卓的应用,Framework层的数据通信很多是用mmap为实现的。
5、tee
tee函数用来在两个管道文件描述符间复制数据。它要求两个文件描述符都必须为管道描述符;同时,它在复制过程中保持原数据不动直接复制fd,而splice是移动数据从源fd到目的fd。注意二者的区别和不同。
下面就分别对几类技术实现方式进行举例分析。在分析之前,先对原来的文章“深浅拷贝、COW及零拷贝”中零拷贝的图进行一下完善:

在这里插入图片描述

主要是补齐了未描述清楚的普通DMA部分的流程。

二、sendfile

先看一下定义:

int main(int argc, char* argv[])
{
......int ffd = open(fname, O_RDONLY);//打开文件struct stat st;fstat(ffd, &st);struct sockaddr_in addr;bzero(&addr, sizeof(addr));addr.sin_family = AF_INET;inet_pton(AF_INET, ip, &addr.sin_addr);addr.sin_port = htons(static_cast<uint16_t>(port));int s = socket(PF_INET, SOCK_STREAM, 0);int reuse = 1;//设置端口重用setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));int ret = bind(s, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));ret = listen(s, 3);struct sockaddr_in client;socklen_t client_addrlen = sizeof(client);int cSocket = accept(s, reinterpret_cast<struct sockaddr*>(&client), &client_addrlen);if (cSocket < 0) {printf("accept err: %d\n", errno);}else {sendfile(cSocket, ffd, NULL, static_cast<size_t>(st.st_size));close(cSocket);}......return 0;
}

注意上面的代码省略了相关的安全控制和参数赋值,大家可以自行设置,直接写成固定的就可以,只是一个测试程序么。

三、splice

splice的应用也不复杂,但需要注意其中的一些要求,特别是参数中,在Linux2.6.21以前,splice的flags设置SPLICE_F_MOVE有效,其后就无效了,但SPLICE_F_NONBLOCK 和SPLICE_F_MORE都有效果。看一下例程:

#include <fcntl.h>
#include <unistd.h>
#include <strings.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <libgen.h>
#include <assert.h>
#include <stdlib.h>int main(int argc, char* argv[])
{
......struct sockaddr_in addr;bzero(&addr, sizeof(addr));addr.sin_family = AF_INET;inet_pton(AF_INET, ip, &addr.sin_addr);addr.sin_port = htons(static_cast<uint16_t>(port));int sfd = socket(PF_INET, SOCK_STREAM, 0);int reuse = 1;setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));int r = bind(sockfd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));r = listen(sockfd, 3);struct sockaddr_in cSocket;socklen_t client_addrlen = sizeof(cSocket);int cfd = accept(sfd, reinterpret_cast<sockaddr*>(&cSocket), &client_addrlen);if (cfd < 0) {printf("accept err: %d\n", errno);}else {int pfd[2];ret = pipe(pfd);while (1) {ssize_t res;res = splice(cfd, NULL, pfd[1], NULL, 1024, SPLICE_F_MORE | SPLICE_F_MOVE);if (res == 0) { // 收到EOFbreak;}res = splice(pfd[0], NULL, cfd, NULL, 1024, SPLICE_F_MORE | SPLICE_F_MOVE);}close(cfd);}close(sfd);return 0;
}

相关的具体参数可以看说明文档,还是相当清楚的。

四、tee和mmap

mmap的例子非常多,这里只给一个tee相关的例子:


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <libgen.h>
#include <assert.h>int main(int argc, char* argv[])
{
......int ffd = open(argv[1], O_CREAT | O_TRUNC | O_WRONLY, 0666);int pfdout[2];int r = pipe(pfdout);assert(r != -1);int pfdfile[2];r = pipe(pfdfile);while (1) {ssize_t res = splice(STDIN_FILENO, NULL, pfdout[1], NULL, 1024, SPLICE_F_MORE | SPLICE_F_MOVE);if (res == 0) {break;}res = tee(pfdout[0], pfdfile[1], 1024, SPLICE_F_NONBLOCK);res = splice(pfdfile[0], NULL, ffd, NULL, 1024, SPLICE_F_MORE | SPLICE_F_MOVE);assert(res != -1);// 二次调用,因为第一次调用数据已经移动,所以splice函数阻塞//res = splice(pfdfile[0], NULL, STDOUT_FILENO, NULL, 1024, SPLICE_F_MORE | SPLICE_F_MOVE);}.......return 0;
}

这些都没有什么难度,手册上也都有相关的例程。

五、DMA技术和零拷贝

在上面的分析过程中可以清晰的知道,DMA技术和零拷贝既有千丝万缕的联系,又有所不同:
DMA技术是负责数据的直通,零拷贝重点是CPU不参与数据拷贝,但需要参与数据的管理(比如数据可以使用,开始操作等等),也就是说DMA技术和零拷贝技术中的CPU互相协作,达到数据拷贝的次数最少的目的。
零拷贝其实就是考虑减少从IO到用户层的整个数据流程的拷贝次数从而提高效率,要始终抓住这条主线。DMA主要是拷贝,CPU重点是管理,即把CPU从既管理又复制中简化工作任务,只管理即可。DMA技术和硬件关系很密切,所以在具体的开发使用中,要明确硬件是否支持相关具体的操作。
需要注意的另外一点是,在实际场景中,如果是非常大的数据文件处理,基于PageCache零拷贝技术则有些力不从心了,还是得使用Direct IO的零拷贝技术。

六、使用零拷贝的框架

说一些技术和概念可能理解并不深刻,可以参考一下相关的一些开源框架中使用的零拷贝技术:
1、KAFKA
使用sendfile的零拷贝技术
2、Nginx
提供了sendfile和directio的相关零拷贝技术
3、Mysql
使用了directio的零拷贝技术
4、Netty
使用sendfile的零拷贝技术
5、RocketMQ
使用了mmap write的零拷贝技术

七、总结

其实说得更浅显一些,所谓零拷贝更准确的说不是零次拷贝,是指尽可能的减少拷贝。在DPDK的系列文章中,这种操作被发挥的淋漓尽致。互联网的口号就是“不让中间商赚差价”,这个在现实上可能有一些逻辑上的BUG,但在内存操作上确实是非常用益。
当然,万事万物不是说是绝对的,有的时候,抽象一下,加一层,如果能达到更好的效果,又不影响实际的使用的情况下,岂不更妙?千头万绪又回到始终坚持的原则,应用场景决定应用技术,实践是检验真理的标准。

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

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

相关文章

挑战英伟达,需要另辟蹊径

Groq是近期AI芯片界的一个明星。原因是其号称比英伟达的GPU更快。3月2日&#xff0c;据报道&#xff0c;Groq收购了一家人工智能解决方案公司Definitive Intelligence。这是 Groq 在 2022 年收购高性能计算和人工智能基础设施解决方案公司 Maxeler Technologies 后的第二次收购…

【Kubernetes】k8s中容器之间、pod之间如何进行网络通信?

目录 PodKubernetes 网络模型同一Pod上的容器之间进行通信同一Node上的不同Pod之间进行通信不同Node上的Pod之间进行通信Service参考 Pod 首先来回顾一下Pod&#xff1a; Pod 是用于构建应用程序的最小可部署对象。单个 Pod 代表集群中正在运行的工作负载&#xff0c;并封装一…

【SpringBoot】测试单元使用多线程

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;SpringBoot ⛺️稳重求进&#xff0c;晒太阳 问题产生 今天学习了乐观锁&#xff0c;但在测试单元执行多线程的时候出现了问题&#xff0c;多线程并没有直接结果 在控制台没有任何输出…

CSRF跨站请求伪造(一)

★★免责声明★★ 文章中涉及的程序(方法)可能带有攻击性&#xff0c;仅供安全研究与学习之用&#xff0c;读者将信息做其他用途&#xff0c;由Ta承担全部法律及连带责任&#xff0c;文章作者不承担任何法律及连带责任。 1、CSRF简介 CSRF&#xff0c;全称&#xff1a;Cross-S…

小程序配置服务器域名的操作步骤(入门级)

将详细列出小程序配置服务器域名的操作步骤&#xff1a; 服务器选购推荐&#xff1a;腾讯云轻量服务器 点击以下任一云产品链接&#xff0c;跳转后登录&#xff0c;自动享有所有云产品优惠权益&#xff1a; 经过笔者亲测&#xff0c;强烈推荐腾讯云轻量应用服务器作为游戏服…

计算机网络_2.1 物理层概述

2.1 物理层概述 一、物理层要实现的功能二、物理层接口特性 B站 深入浅出计算机网络 2.1物理层概述 一、物理层要实现的功能 物理层要实现的功能就是在各种传输媒体上传输比特0和1&#xff0c;进而给上面的数据链路层提供透明传输比特流的服务。 数据链路层“看不见”&#xff…

ssm172旅行社管理系统的设计与实现

** &#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;** 一 、设计说明 1.1 研究…

RDD算子介绍

1. RDD算子 RDD算子也叫RDD方法&#xff0c;主要分为两大类&#xff1a;转换和行动。转换&#xff0c;即一个RDD转换为另一个RDD&#xff0c;是功能的转换与补充&#xff0c;比如map&#xff0c;flatMap。行动&#xff0c;则是触发任务的执行&#xff0c;比如collect。所谓算子…

python自动化之项目架构搭建与思路讲解(第二天)

1.自动化测试的概念 自动化测试是指使用自动化工具和脚本来执行测试任务,以验证软件或系统的正确性和稳定性。它可以提高测试的效率和准确性,并节约时间和成本。 2.自动化脚本编写的思路 xmind文档如有需要,可在资源里自行下载 3.项目代码工程创建 lib :基本代码库包 …

像用Excel一样用Python:pandasGUI

文章目录 启动数据导入绘图 启动 众所周知&#xff0c;pandas是Python中著名的数据挖掘模块&#xff0c;以处理表格数据著称&#xff0c;并且具备一定的可视化能力。而pandasGUI则为pandas打造了一个友好的交互窗口&#xff0c;有了这个&#xff0c;就可以像使用Excel一样使用…

基于SpringBoot多模块项目引入其他模块时@Autowired无法注入

基于SpringBoot多模块项目引入其他模块时Autowired无法注入 一、问题描述1、解决方案 一、问题描述 启动Spring Boot项目时报 Could not autowire. No beans of ‘xxxxxxxx’ type found. 没有找到bean的实例&#xff0c;即spring没有实例化对象&#xff0c;也就无法根据配置文…

AcWing 788. 逆序对的数量 解题思路及代码

先贴个题目&#xff1a; 以及原题链接&#xff1a; 788. 逆序对的数量 - AcWing题库https://www.acwing.com/problem/content/790/ 这题也是板子题&#xff0c;就是对归并排序的衍生&#xff0c;我们先分析下如果用归并排序对排序区间进行二分的话&#xff0c;逆序对可能出现的…