今天搞懂Linux零拷贝
gudong366 2025-03-23 18:21 8 浏览
Linux零拷贝
很多人都听过零拷贝zero-copy的大名,知道在很多牛掰的中间件(比如rocketmq)中为了提高效率,都是用了这项技术。
但是,zero-copy到底是怎么回事?
今天我想把他彻底搞明白。
什么是zero-copy
为了搞明白一个技术方案,首先我们应该搞明白这个技术是为了解决什么问题。先理解问题,才能理解答案。
我们先看一个简单的场景,服务器需要将磁盘文件中的数据通过网络发送给客户端。在这个场景下都发生了什么?
你可能觉得这只是两个系统间的调用,数据从一个服务器copy到另一台服务器。但事实上,在操作系统层面发生的事情要复杂得多。
在传统模式下,至少会copy四次数据,下面会细讲。所以,为了减少copy次数,提高效率,诞生了zero-copy方案。
zero-copy并不是真的一次都不copy,而是站在内核空间的角度讲,没有进行内核空间和用户空间之间的copy。
看不懂这几个名词没关系,下面会细讲。
前置知识
Linux系统的“用户空间”和“内核空间”
首先要知道,Linux操作系统分为【用户态user context】和【内核态 kernel context】,
从Linux系统上看,除了引导系统的BIN区,整个内存空间主要被分成两个部分:内核空间(Kernel space)、用户空间(User space)。“用户空间”和“内核空间”的空间、操作权限以及作用都是不一样的。文件操作、网络操作需要涉及这两种上下文的切换,免不了进行数据复制。
内核空间是Linux自身使用的内存空间,主要提供给程序调度、内存分配、连接硬件资源等程序逻辑使用;
用户空间则是提供给各个进程的主要空间。用户空间不具有访问内核空间资源的权限,因此如果应用程序需要使用到内核空间的资源,则需要通过系统调用来完成:从用户空间切换到内核空间,然后在完成相关操作后再从内核空间切换回用户空间
DMA
DMA(Direct Memory Access) ———— 直接内存访问 :DMA是允许外设组件将I/O数据直接传送到主存储器中并且传输不需要CPU的参与,以此将CPU解放出来去完成其他的事情。 而用户空间与内核空间之间的数据传输并没有类似DMA这种可以不需要CPU参与的传输工具,因此用户空间与内核空间之间的数据传输是需要CPU全程参与的。所有也就有了通过零拷贝技术来减少和避免不必要的CPU数据拷贝过程。
传统做法
在普通的做法中,这个简单的数据复制过程,至少经过了4次的数据复制。
下面这张图,上半部分是上下文的切换,下半部分是数据复制操作
已java为例讲解
第一步:首先jvm向操作系统发出read()系统调用,此时会要将上下文从用户态切换到内核态。将数据从磁盘copy到内核地址空间buffer中,这一步copy是由DMA(直接内存访问)引擎执行的,这是第一次copy
第二步:linux的read操作结束退出,此时会将上下文切换回用户态,同时将数据从内核缓冲区拷贝到用户空间缓冲区。这里用的是CPU。这是第二次copy
之后需要将数据写给网卡进行发送
第三步:jvm向OS发起write()系统调用。系统上下文由用户态切换为内核态。系统的write操作将数据从用户空间buffer复制到内核空间的socket buffer。现在数据又回到了内核buffer,当然这和第一步的内核buffer不是一个空间。这是第三次copy。用的是cpu。
第四步:write操作返回,系统切换回用户态。同时,系统通过DMA将socket buffer中的数据copy到底层硬件(网卡)缓冲区。(这是一个独立且异步的操作,和write操作无关)
下面这个图更清晰些
可以看到,数据在内核空间和用户空间之间,被来来回回的复制。同时伴随着系统上下文的切换。我们知道,上下文切换是CPU密集型的工作,数据拷贝是I/O密集型的工作。如果一次简单的传输就要像上面这样复杂的话,效率是相当低下的。零拷贝机制的终极目标,就是消除冗余的上下文切换和数据拷贝,提高效率。
sendfile
为了做到这点,linux内核从2.1版本开始,提供了名为sendfile()的系统调用。不仅能减少数据copy,而且能减少系统上下文切换。
看图,从图的上半部分看出,sendfile方式,只有2次上下文的切换。
在数据copy方面。
第一步,从硬盘copy进内核空间,DMA。
第二步,内核buffer到socket buffer的copy。用的cpu。注意这里的copy只在内核空间,不涉及用户空间。
第三步,从socket buffer 到网卡,DMA
ok,目前我们省了1次上下文切换,省了内核空间和用户空间的cpu copy
但是,我们看到在内核空间中,还有一次cpu copy。直觉告诉我们,这次copy还是没有什么用。
为什么会有这一步呢?
这是因为在一般的Block DMA方式中,源物理地址和目标物理地址都得是连续的,所以一次只能传输物理上连续的一块数据,每传输一个块发起一次中断,直到传输完成,所以必须要在两个缓冲区之间拷贝数据。
那么能否省去这一步呢?
对Scatter/Gather的支持的IO
Scatter/Gather技术的意思是分散然后收集。
从kernel 2.4版本开始,socket buffer的描述符做了优化。描述符中包含有数据的起始地址和长度。传输时只需要遍历链表,按序传输数据,全部完成后发起一次中断即可,效率比Block DMA要高。也就是说,硬件可以通过Scatter/Gather DMA直接从内核缓冲区中取得全部数据,不需要再从内核缓冲区向Socket缓冲区拷贝数据。
这样,又省区了cpu拷贝。
到现在为止,从用户空间角度看,是0次copy。从CPU的角度看,也是0次copy。
所以zero-copy完成。
那么,现在是不是完美了呢?其实是有一个问题的。
现在数据一直在内核空间,用户空间没有数据,那么我们的程序就不能对数据进行任何修改操作。如果我们有修改的需求,sendfile实现是不行的。
进而,引出了下面这个。
mmap
mmap是内存映射。他要比sendfile效率差,但是比传统方式又高效的一种方案。
mmap的特点是,不会将数据从内核空间copy到用户空间。而是在用户空间共享内核空间的数据。从而可以对数据进行修改。
从效率上来说,mmap也是有4次上下文切换,和三次的数据copy。相比传统方式是省了一次cpu copy。
java的NIO
java的NIO中,常用的FileChannel。有个map方法,FileChannel的map方法会返回一个MappedByteBuffer。MappedByteBuffer是一个直接字节缓冲器,该缓冲器的内存是一个文件的内存映射区域。map方法底层是通过mmap实现的,因此将文件内存从磁盘读取到内核缓冲区后,用户空间和内核空间共享该缓冲区。
相关推荐
- linux sed系列 第四篇:sed工业实战——日志处理与数据清洗
-
“掌握了sed的编程能力后,我们如同装备精良的工匠,终于可以踏入真实的工业战场。本篇将聚焦sed在日志分析、数据合规化、多文件批处理等场景中的应用,看它如何在海量数据中游刃有余,展现文本处理的...
- Linux下sed的简单使用(linux中sed是什么意思)
-
1、sed简介stremeditor流编辑器,它是一项Linux指令,功能同awk类似,差别在于,sed简单,对列处理的功能要差一些,awk的功能复杂,对列处理的功能比较强大,sed编辑器是一行一...
- linux基础命令之date命令(linux中的date)
-
date命令主要用于显示或者设置系统时间语法格式:date参数对象使用date命令时,最好先使用date--help命令查看支持哪些参数,有些小型Linux系统下的date命令,只支持一些基本参...
- Ubuntu linux 常用命令(ubuntu常用的50个命令)
-
使用dpkg命令来安装.deb包。sudodpkg-i~/example.deb如果在安装过程中遇到依赖问题,可以使用以下命令来修复:sudoapt-getinstall-f将flut...
- Linux基础命令-sed命令(linux教程:sed命令的用法)
-
Sed全名streameditor流编辑器,它是一个强大的文本处理工具,它可以从文件中接受输入,也可以接受来自标准输入流的输入,它擅长取行。Sed的用途非常广泛,包括:1)文本替换2)选择性的输...
- linux sed系列 第二篇:sed进阶技巧——地址定位与正则表达式
-
“上一篇我们掌握了sed的基础替换,如同获得了第一把钥匙。现在,让我们更进一步,学习如何精准锁定目标行,如同拥有了导航地图,让每一次操作都直击要害!”地址定位的四种维度sed的强大,很大程度上源...
- 火狐Firefox浏览器140发布:手动Unload标签页、优化翻译体验等
-
IT之家6月24日消息,Mozilla在发布版本139不到一个月后,推出了最新的开源网页浏览器Firefox140。新版本增加了手动Unload标签页的功能,优化了垂直标签页的调...
- Linux 基本正则表达式及扩展正则表达式功能举例
-
在Linux中,正则表达式(RegularExpression)是一种强大的模式匹配工具,用于在文本中查找、匹配和处理特定模式的字符串。Linux支持两种类型的正则表达式:基本正则表达式(Basic...
- linux下find命令的经典26个使用示例
-
简介find命令是基于unix的操作系统中常用的工具之一。顾名思义,它在目录层次结构中查找文件和目录。用户可以传递不同的参数,并根据文件的名称、扩展名、类型、大小、权限、修改时间、所有者、组等搜索文件...
- linux运维中特殊符号的应用与实践
-
路径位置类的特殊符号(1)、波浪线(~)在linux系统的命令行中,~表示用户的家目录,超级用户为/root,普通用户为/home。假设我当前目录在usr/local下[root@xrylocal]...
- 开源框架log4cpp实战(开源gui框架)
-
1.Log4cpp使用Log4cpp中主要包含Category(种类),Appender(附加器),Layout(布局),Priorty(优先级),NDC(嵌套的诊断上下文)。Category、App...
- Linux find命令详解(linux find -l)
-
一、命令介绍Linuxfind命令是类unix操作系统中最重要和最常用的命令行实用程序之一。find命令用于根据指定的条件搜索和定位与参数匹配的文件和目录列表。find命令提供了广泛的选项,允许用户...
- Linux运维:单引号与双引号的使用(linux 单引号和双引号)
-
1、单引号的使用单引号可以将它中间的所有任意字符还原为字面意义,实现屏蔽Shell元字符的功能。注意不可以在两个单引号中间单独插入一个单引号,单引号必须成对出现。示例1:定义一个变量,并输出变量的...
- Linux技巧:find 命令用法详细说明,看完会有收获
-
在Linux命令中,find是比较复杂难用的命令。使用该命令搜索文件时,常常发现自己找了一些例子能用,但稍微改一下条件,就搜不到想要的结果。下面会以一些实例来说明使用find命令的关键要点和...
- Linux Shell中单引号、双引号、反引号的解释
-
1、单引号('')单引号所见即所得,直接显示单引号里的内容。即单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的。比如下面的例子,单引号所见即所得。2、双引号("...
- 一周热门
- 最近发表
-
- linux sed系列 第四篇:sed工业实战——日志处理与数据清洗
- Linux下sed的简单使用(linux中sed是什么意思)
- linux基础命令之date命令(linux中的date)
- Ubuntu linux 常用命令(ubuntu常用的50个命令)
- Linux基础命令-sed命令(linux教程:sed命令的用法)
- linux sed系列 第二篇:sed进阶技巧——地址定位与正则表达式
- 火狐Firefox浏览器140发布:手动Unload标签页、优化翻译体验等
- Linux 基本正则表达式及扩展正则表达式功能举例
- linux下find命令的经典26个使用示例
- linux运维中特殊符号的应用与实践
- 标签列表
-
- linux一键安装 (31)
- linux运行java (33)
- ln linux (27)
- linux 磁盘管理 (31)
- linux 内核升级 (30)
- linux 运行python (28)
- linux 备份文件 (30)
- linux 网络测试 (30)
- linux 网关配置 (31)
- linux jre (32)
- linux 杀毒软件 (32)
- linux语法 (33)
- linux博客 (33)
- linux 压缩目录 (37)
- linux 查看任务 (32)
- 制作linux启动u盘 (35)
- linux 查看存储 (29)
- linux乌班图 (31)
- linux挂载镜像 (31)
- linux 软件源 (28)
- linux题目 (30)
- linux 定时脚本 (30)
- linux 网站搭建 (28)
- linux 远程控制 (34)
- linux bind (31)