java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
ahcoder 2025-03-24 13:27 78 浏览
零拷贝技术 Zero-Copy 是指计算机执行操作时,可以直接从源(如文件或网络套接字)将数据传输到目标缓冲区, 而不需要 CPU 先将数据从某处内存复制到另一个特定区域,从而减少上下文切换以及 CPU 的拷贝时间。
1 I/O 中断原理
在 DMA 技术出现之前,应用程序与磁盘之间的 I/O 操作都是通过 CPU 的中断完成的。
- 用户进程向 CPU 发起 read 系统调用读取数据,由用户态切换为内核态,然后一直阻塞等待数据的返回。
- CPU 在接收到指令以后对磁盘发起 I/O 请求,将磁盘数据先放入磁盘控制器缓冲区。
- 数据准备完成以后,磁盘向 CPU 发起 I/O 中断。
- CPU 收到 I/O 中断以后将磁盘缓冲区中的数据拷贝到内核缓冲区,然后再从内核缓冲区拷贝到用户缓冲区。
- 用户进程由内核态切换回用户态,解除阻塞状态,然后等待 CPU 的下一个执行时间钟。
可以看到,整个数据的传输过程,都要需要 CPU 亲自参与搬运数据的过程,而且这个过程,CPU 是不能做其他事情的。
假如通过千兆网卡或者磁盘传输大量数据时,使用 CPU 来拷贝的话,对 CPU 资源是种极大的消耗。
2 DMA 方式
DMA 的全称是直接内存访问(Direct Memory Access),是一种硬件设备绕开 CPU 独立直接访问内存的机制。
目前支持 DMA 的硬件包括:网卡、声卡、显卡、磁盘控制器等。
基于 DMA 访问方式,系统主内存于硬盘或网卡之间的数据传输可以绕开 CPU 的全程调度,数据搬运的工作交给 DMA 控制器,在传输过程中,CPU 可以继续处理其他的工作,提升系统的资源利用率。
- 用户进程向 CPU 发起 read 系统调用读取数据,用户态切换为内核态,然后一直阻塞等待数据的返回。
- CPU 在接收到指令以后对 DMA 磁盘控制器发起调度指令。
- DMA 磁盘控制器对磁盘发起 I/O 请求,将磁盘数据先拷贝到磁盘控制器缓冲区,CPU 全程不参与此过程。
- 数据读取完成后,DMA 磁盘控制器会接受到磁盘的通知,将数据从磁盘控制器缓冲区拷贝到内核缓冲区。
- DMA 磁盘控制器向 CPU 发出数据读完的信号,由 CPU 负责将数据从内核缓冲区拷贝到用户缓冲区。
- 用户进程由内核态切换回用户态,解除阻塞状态,然后等待 CPU 的下一个执行时间钟。
3 传统 I/O 方式
为了理解零拷贝技术的思路,首先了解一下传统 I/O 方式存在的问题。
Linux 系统中,传统的访问方式是通过 write() 和 read() 两个系统调用实现的,通过 read() 函数读取文件到到缓存区中,然后通过 write() 方法把缓存中的数据输出到网络端口 。
下图分别对应传统 I/O 操作的数据读写流程,整个过程涉及 4 次上下文切换、 2 次 CPU 拷贝、2 次 DMA 拷贝总共 4 次拷贝。
- 4 次上下文切换
因为发生了两次系统调用,一次是 read() ,一次是 write(),用户程序向内核发起系统调用时,CPU 将用户进程从用户态切换到内核态;当系统调用返回时,CPU 将用户进程从内核态切换回用户态。
- 4 次 数据拷贝
- 第一次 DMA 拷贝,将磁盘上的数据拷贝到操作系统内核的缓冲区 ;
- 第二次 CPU 拷贝,将内核缓冲区的数据拷贝到用户的缓冲区里;
- 第三次 CPU 拷贝,将用户缓冲区的数据,拷贝到内核 Socket 缓冲区;
- 第四次 DMA 拷贝,将内核 Socket 缓冲区的数据拷贝到网卡的缓冲区。
综上,想要提升文件传输的性能,因此我们需要减少「上下文切换」和「数据拷贝」的次数。
3 零拷贝方式
零拷贝技术实现的方式通常有 2 种:mmap + write 、sendfile、sendfile + DMA scatter-gather 。
3.1 mmap + write
mmap 是 Linux 提供的一种内存映射文件的机制,它实现了将内核中读缓冲区地址与用户空间缓冲区地址进行映射,从而实现内核缓冲区与用户缓冲区的共享。
基于 mmap + write 系统调用的零拷贝方式,整个拷贝过程会发生 4 次上下文切换,1 次 CPU 拷贝和 2 次 DMA 拷贝。
用户程序读写数据的流程如下:
- 用户进程通过 mmap() 函数向内核发起系统调用,上下文从用户态切换为内核态。
- 将用户进程的内核空间的读缓冲区与用户空间的缓存区进行内存地址映射。
- CPU 利用 DMA 控制器将数据从主存或硬盘拷贝到内核空间的读缓冲区。
- 上下文从内核态切换回用户态,mmap 系统调用执行返回。
- 用户进程通过 write() 函数向内核发起系统调用,上下文从用户态切换为内核态。
- CPU 将读缓冲区中的数据拷贝到的网络缓冲区。
- CPU 利用 DMA 控制器将数据从网络缓冲区(socket buffer)拷贝到网卡进行数据传输。
- 上下文从内核态切换回用户态,write 系统调用执行返回。
mmap 的拷贝虽然减少了 1 次 CPU 拷贝,提升了效率,但也存在一些隐藏的问题。
当 mmap 一个文件时,如果这个文件被另一个进程所截获,那么 write 系统调用会因为访问非法地址被 SIGBUS 信号终止,SIGBUS 默认会杀死进程并产生一个 coredump,服务器可能因此被终止。
3.2 sendfile
sendfile 系统调用在 Linux 内核版本 2.1 中被引入,目的是简化通过网络在两个通道之间进行的数据传输过程。
通过 sendfile 系统调用,数据可以直接在内核空间内部进行 I/O 传输,从而省去了数据在用户空间和内核空间之间的来回拷贝。
sendfile 系统调用的引入,不仅减少了 CPU 拷贝的次数,还减少了上下文切换的次数,它的伪代码如下:
基于 sendfile 系统调用的零拷贝方式,整个拷贝过程会发生 2 次上下文切换,1 次 CPU 拷贝和 2 次 DMA 拷贝。
用户程序读写数据的流程如下:
- 用户进程通过 sendfile() 函数向内核发起系统调用,上下文从用户态切换为内核态。
- CPU 利用 DMA 控制器将数据从主存或硬盘拷贝到内核空间的读缓冲区。
- CPU 将读缓冲区中的数据拷贝到的网络缓冲区。
- CPU 利用 DMA 控制器将数据从网络缓冲区拷贝到网卡进行数据传输。
- 上下文从内核态切换回用户态,sendfile 系统调用执行返回。
相比较于 mmap 内存映射的方式,sendfile 少了 2 次上下文切换,但是仍然有 1 次 CPU 拷贝操作。
3.3 sendfile + DMA gather copy
Linux 2.4 版本的内核对 sendfile 系统调用进行修改,为 DMA 拷贝引入了 gather 操作。
它将内核空间的读缓冲区中对应的数据描述信息(内存地址、地址偏移量)记录到相应的网络缓冲区中,由 DMA 根据内存地址、地址偏移量将数据批量地从读缓冲区拷贝到网卡设备中,这样就省去了内核空间中仅剩的 1 次 CPU 拷贝操作。
在硬件的支持下,sendfile 拷贝方式不再从内核缓冲区的数据拷贝到 socket 缓冲区,取而代之的仅仅是缓冲区文件描述符和数据长度的拷贝,这样 DMA 引擎直接利用 gather 操作将页缓存中数据打包发送到网络中即可,本质就是和虚拟内存映射的思路类似。
基于 sendfile + DMA gather copy 系统调用的零拷贝方式,整个拷贝过程会发生 2 次上下文切换、0 次 CPU 拷贝以及 2 次 DMA 拷贝,用户程序读写数据的流程如下:
- 用户进程通过 sendfile() 函数向内核发起系统调用,上下文从用户态切换为内核态。
- CPU 利用 DMA 控制器将数据从主存或硬盘拷贝到内核空间的读缓冲区。
- CPU 把读缓冲区的文件描述符(file descriptor)和数据长度拷贝到网络缓冲区。
- 基于已拷贝的文件描述符和数据长度,CPU 利用 DMA 控制器的 gather/scatter 操作直接批量地将数据从内核的读缓冲区拷贝到网卡进行数据传输。
- 上下文从内核态切换回用户态,sendfile 系统调用执行返回。
5 写到最后
无论是传统 I/O 拷贝方式还是引入零拷贝的方式,2 次 DMA 拷贝是都少不了的,因为两次 DMA 都是依赖硬件完成的。
拷贝方式 | CPU拷贝 | DMA拷贝 | 系统调用 | 上下文切换 |
传统方式(read + write) | 2 | 2 | read / write | 4 |
内存映射(mmap + write) | 1 | 2 | mmap / write | 4 |
sendfile | 1 | 2 | sendfile | 2 |
sendfile + DMA gather copy | 0 | 2 | sendfile | 2 |
RocketMQ 选择了 mmap + write 这种零拷贝方式,适用于业务级消息这种小块文件的数据持久化和传输;
而 Kafka 采用的是 sendfile 这种零拷贝方式,适用于系统日志消息这种高吞吐量的大块文件的数据持久化和传输。
但是值得注意的一点是,Kafka 的索引文件使用的是 mmap + write 方式,数据文件使用的是 sendfile 方式。
相关推荐
- Java程序员必备的Linux命令速查表
-
Java程序员必备的Linux命令速查表在Java开发的世界里,Linux就像一位默默支持的幕后英雄。作为一名Java开发者,掌握一些基本的Linux命令,不仅能提高工作效率,还能让你在团队中显得格外...
- Linux 命令速查手册:这 30 个高频指令,拯救 90% 的运维小白!
-
在Linux系统的世界里,命令行是强大的武器。对于运维小白而言,掌握一些高频使用的Linux命令,能极大提升工作效率,轻松应对各种系统管理任务。今天,就为大家奉上精心整理的30个Linu...
- linux磁盘管理相关命令(linux磁盘管理常用命令)
-
磁盘的使用情况会直接影响系统的性能,因此我们经常会用到以下命令,主要围绕:fdisk:磁盘分区df:文件系统的磁盘空间占用情况du:文件目录的磁盘空间占用情况查看磁盘关系lsblk查看磁盘分区情况fd...
- 第四章 Linux常用shell命令-4.5.磁盘管理
-
主要介绍一下跟磁盘管理相关命令,有比较多的内容摘抄自网络,如有侵权,请及时联系我删除:显示目前在Linux系统上的文件系统磁盘使用情况统计:df创建和维护分区表的程序:fdisk将磁盘分区或镜像挂...
- Linux新手必备:20个高效命令轻松掌握!
-
Linux基本命令使用指南在现代计算机操作系统中,Linux因其开放性、灵活性和强大的功能,广泛应用于服务器和开发环境中。作为技术人员,掌握Linux的基本命令是非常重要的。在本文中,我们将重点介绍2...
- 每日必学Linux命令:ls命令(linux命令详解之ls命令)
-
ls命令是linux下最常用的命令。ls命令就是list的缩写缺省下ls用来打印出当前目录的清单如果ls指定其他目录那么就会显示指定目录里的文件及文件夹清单。通过ls命令不仅可以查看linux文件...
- Linux系统dev和proc目录详解(linux dev/sr0)
-
简介:Linux系统里的/dev和/proc目录那可是相当重要的系统文件。在Linux系统中,/dev目录专门用来存放设备文件。不光有设备文件,系统里还有好多特殊功能也是通过设备的形式...
- Linux切换目录之cd命令(linux切换指定目录)
-
1.基本概念1.1命令作用当我们在Linux系统上工作时,做得相当多的一项任务就是在不同的目录之间进行切换,这时就需要用到cd命令了。cd是"changedirectory"的首...
- Linux切换目录(cd命令)(linux如何切换到目录)
-
cd命令,是ChangeDirectory的缩写,用来切换工作目录。Linux命令按照来源方式,可分为两种,分别是Shell内置命令和外部命令。所谓Shell内置命令,就是Shel...
- MongoDB数据库的快速部署和启动(mongodb的使用教程)
-
一、Mongodb介绍常见数据库介绍关系数据库RDBMS设计表结构,通过SQL语句进行操作。连表关系常见的关系型数据库:mysqloracle(商业)DB2(IBM)sqlserver(微软...
- 5分钟学会网络服务搭建,飞凌i.MX9352 + Linux 6.1实战示例
-
在“万物互联”的技术浪潮下,网络服务已成为连接物理世界与数字世界的核心纽带,它不仅赋予了终端设备“开口说话”的能力,更构建了智能设备的开发范式。本文就将以飞凌嵌入式OK-MX9352-C开发板(搭载了...
- centos安装geoserver并配置开机启动
-
前提条件:服务器已经安装了java环境一、下载下载地址:http://geoserver.org/release/maintain/下载后文件名为:geoserver-2.19.3-bin.zip二、...
- 开机启动流程(开机流程图)
-
grubandbootCentos5,6的开机启动流程grubCentos7的开机启动流程Centos5,6的开机启动流程initrd/initramfs一般存储在/boot目录下,以.img...
- Linux cron服务概述(crontab服务)
-
cron是Linux/Unix系统中一个非常重要的后台服务(守护进程),用于在预定的时间间隔自动执行命令或脚本。它使得自动化重复性任务成为可能,例如日志清理、数据备份、系统维护等。1.cron...
- CentOS 8利用rc.local进行开机自启动的配置
-
CentOS8利用rc.local进行开机自启动的配置CentOS8linux系统是不建议使用rc.local进行开机自启动的,建议创建systemdservice。我们为了方便以后多一个配置...
- 一周热门
- 最近发表
- 标签列表
-
- linux 远程 (37)
- u盘 linux (32)
- linux 登录 (34)
- linux 路径 (33)
- linux 文件命令 (35)
- linux 是什么 (35)
- linux 界面 (34)
- 查看文件 linux (35)
- linux 语言 (33)
- linux代码 (32)
- linux 查看命令 (33)
- 关闭linux (34)
- root linux (33)
- 删除文件 linux (35)
- linux 主机 (34)
- linux与 (33)
- linux 函数 (35)
- linux .ssh (35)
- cpu linux (35)
- 查看linux 系统 (32)
- linux 防火墙 (33)
- linux 手机 (32)
- linux 镜像 (34)
- linux ip地址 (34)
- linux 用户查看 (33)