目录
一.背景
二.零拷贝定义
三.传统I/O执行流程
四.零拷贝相关
1.内核空间与用户空间
为什么有空间划分?
责任划分
2.用户态与内核态定义
3.上下文切换
1.什么是CPU上下文
2.什么是CPU上下文切换
4.虚拟内存
5.DMA
1.定义
2.DMA帮做的事情
3.为什么需要DMA
五.实现零拷贝的方式
mmap + write
sendfile
sendfile+DMA scatter/gather 实现的零拷贝
三种“零拷贝”方式对比
六.java提供的零拷贝技术
java NIO对mmap的支持
java NIO对sendfile的支持
一.背景
零拷贝算是一个老生常谈的问题啦,很多顶级框架都用到了零拷贝来提升性能,比如我们经常接触到的Kafka 、RocketMQ、Netty 。
二.零拷贝定义
- “拷贝”:就是指数据从一个存储区域转移到另一个存储区域.
- “零”: 表示次数为 0,它表示拷贝数据的次数为 0。
计算机执行 I/O 操作时,CPU 不需要将数据从一个存储区域复制到另一个存储区域,从而可以减少上下文切换以及 CPU 的拷贝时间。它是一种 I/0 操作优化技术。
零拷贝并不是没有拷贝数据,而是减少用户态/内核态的切换次数以及 CPU 拷贝的次数。
三.传统I/O执行流程
以【文件下载】功能为例,前端发起请求后,服务端的任务就是:将主机磁盘中的文件通过已连接的Socket发出。
关键代码如下:
while((n = read(diskfd,buf,BUF SIZE)) > 0)
write(sockfd,buf ,n);
传统的IO流程,包括read和write两个过程。
read:把数据从【磁盘】--读取到-->【内核缓冲区】--读取到-->【用户缓冲区】
write:把数据写入【socket缓冲区】--写入到-->【网卡设备】
流程图如下:
- 用户应用进程调用 read 函数,向操作系统发起 I/O 调用,上下文从用户态转为内核态(切换 1)
- DMA 控制器把数据从磁盘中,读取到内CPU 把内核缓冲区
- CPU 把内核缓冲区数据,拷贝到用户应用缓冲区,上下文从内核态转为用户态 (切换 2),read 函数返回
- 用户应用进程通过 write 函数,发起 1O 调用,上下文从用户态转为内核态 (切换 3)
- CPU 将应用缓冲区中的数据,拷贝到socket缓冲区
- DMA 控制器把数据从 socket 缓冲区拷贝到网卡设备,上下文从内核态切换回用户态 (切换4),write 函数返回
从流程图中可以看出,传统的IO读写流程,包括了4次上下文切换(4次用户态和内核态的切换),4次数据拷贝(两次CPU拷贝及两次DMA拷贝)
四.零拷贝相关
1.内核空间与用户空间
为什么有空间划分?
应用程序其实是需要经过操作系统,才能做一系列特殊操作如:磁盘文件读写、内存读写等等。这些都是有一定风险的操作,不能由应用程序乱来,只能交给底层操作系统来。
因此操作系统为每个进程都分配了内存空间,一部分是内存空间,一部分是用户空间。
内核空间是操作系统内核访问的区域,是受保护的内存空间.
用户空间是用户应用程序访问的内存区域。
以32位操作系统为例,它会为每个进程分配4G(2的32次方)的内存空间。
责任划分
内核空间:主要提供进程调度、内存分配、连接硬件资源等功能
用户空间:提供给各个程序进程的空间,它不具有访问内核空间资源的权限。【若应用程序需要使用到内核空间的资源,则需要通过系统调用来完成】。进程从用户空间切换到内核空间,完成相关操作后,再从内核空间切换回用户空间。
2.用户态与内核态定义
如果进程运行于内核空间,则称为【进程的内核态】
如果进程运行于用户空间,则称为【进程的用户态】
3.上下文切换
1.什么是CPU上下文
是指CPU执行程序时,保存有关程序运行状态的信息集合。这些信息包括程序计数器、寄存器中的值以及堆栈指针等。
当CPU切换到另一个程序时,它会保存当前程序的上下文并加载下一个程序的上下文。这样可以确保程序能够正确地继续执行,并避免数据损坏或丢失。
2.什么是CPU上下文切换
内核(操作系统的核心)在CPU上对进程或者线程进行切换。
进程从用户态到内核态的转变,需要系统调用来完成,系统调用的过程,会发生CPU上下文切换。
上一行“系统调用的过程”,展开来说:
CPU寄存器里原来用户态的指令位置,需要先保存起来。接着,为了执行内核态代码,CPU寄存器需要更新为内核态指令的新位置。最后才是跳转到内核态执行任务。
4.虚拟内存
虚拟地址取代物理地址,使用其可以有2个好处
- 虚拟内存空间可以远远大于物理内存地址
- 多个虚拟内存可以指向同一个物理地址
正是由于多个虚拟内存可以指向同一个物理地址,可以把内核空间和用户空间的虚拟地址映射为同一个物理地址,急可以减少IO拷贝次数。
5.DMA
1.定义
Direct Memory Access即直接内存访问。DMA本质上是主板上一块独立芯片,允许外设设备和内存存储器之间直接进行IO数据传输,其过程不需要CPU参与。
2.DMA帮做的事情
- 用户应用进程调用 read 函数,向操作系统发起IO调用,进入阻塞状态,等待数据返回。
- CPU 收到指令后,对 DMA 控制器发起指令调度
- DMA 收到IO 请求后,将请求发送给磁盘;
- 磁盘将数据放入磁盘控制缓冲区,并通知 DMA
- DMA 将数据从磁盘控制器缓冲区拷贝到内核缓冲区
- DMA 向 CPU 发出数据读完的信号,把工作交换给 CPU,由 CPU 负责将数据从内核缓冲区拷贝到用户缓冲区
- 用户应用进程由内核态切换回用户态,解除阻塞状态
由此可知:DMA是帮CPU转发请求并进行数据拷贝
3.为什么需要DMA
俩字:效率,它帮忙 CPU 做事情,CPU 就可以闲下来去做别的事情,提高了 CPU 的利用效率。
五.实现零拷贝的方式
- mmap+write
- sendfile
- 带有 DMA 收集拷贝功能的 sendfile
mmap + write
具体实现步骤如下:
- 调用mmap函数将文件映射到进程的地址空间中,得到一个指向映射区域的指针,称为mmap指针。这个过程只需要CPU执行一次,将文件内容映射到内存中,不需要进行数据拷贝。
- 将mmap指针传递给write或sendfle等函数,直接将数据从mmap指针指向的内存区域发送到网络或磁盘中,而不需要先将数据从应用程序的缓冲区复制到内核缓冲区,再写入磁盘或网络中,避免了数据的反复拷贝,降低了CPU和内存的压力。
- 写操作完成后,调用munmap程放mmap指针对应的内存区域。这个过程只需要CPU执行-一次,将内存中的数据同步到盘上,并回收已使用的内存区域,不需要进行数据拷贝和额外的内存分配。
因此,使用mmap+write的零拷贝方法可以有效减少数据拷贝的次数,提高数据传输的效率和速度,降低系统资源的消耗。这种零拷贝技术适用于大文件、高并发、低延迟等需求较高的场景。
I/O发生了4次用户空间和内核空间的上下文切换
3次数据拷贝(2次DMA拷贝、1次CPU拷贝)
mmap 是将内核缓冲区的地址和用户缓冲区的地址进行映射,内核缓冲区和应用缓冲区共享,所以节省了一次 CPU 拷贝并且用户进程内存是虚拟的,只是映射到内核的读缓冲区,可以节省一半的内存空间。
sendfile
sendfile 表示在两个文件描述符之间传输数据,它是在操作系统内核中操作的,避免了数据从内核缓冲区和用户缓冲区之间的拷贝操作,因此可以使用它来实现零拷贝。
具体实现步骤如下
- 调用sendfile函数指定源文件描述符和目标文件描述符。目标文件描述符通常指向网络套接字或磁盘文件
- sendfile内部会将源文件描述符和目标文件描述符之间建立一个管道,数据不需要经过应用程序的空间,而是直接从内核中缓存区向目标文件描述符所对应的文件或者socket进行传输,彻底避免了数据在用户态进程地址空间和内核态缓冲区之间的拷贝。
- sendfile函数返回后,数据已经从源文件描述符发送到了目标文件描述符,不再需要其他的操作
因此,使用sendfie函数可以实现零拷贝技术,减少了CPU和内存的开销,提高了系统的性能。sendfle适用于大文件、高并发、低延迟等需求较高的场景,在Web服务器、文件传输等领域得到广泛应用。
I/O共发生了2次上下文切换,3次数据拷贝(一次CPU拷贝,两次DMA拷贝)
sendfile+DMA scatter/gather 实现的零拷贝
0次CPU拷贝
下面是 sendfile+DMA scatter/gather 实现零拷贝的流程:
- 打开源文件和目标文件,获取文件描述符。
- 调用 sendfile 函数,将源文件中的数据写入到内核缓冲区中,并返回写入的字节数
- 使用 DMA scater/gather 技术将内核缓冲区中的数据直接传输到网络适配器的缓冲区中,而不需要经过用户空间缓冲区的拷贝
- 等待数据传输完成后,调用 send 函数将数据发送出去。
- 如果还有剩余数据需要写入,则重复执行步骤 2 到步骤 4,直到所有数据传输完成。
- 关闭文件描述符。
整个过程中,只有在第二步中将数据从磁盘读入到内核缓中区时需要进行数据拷贝,而其他步骤都避免了数据拷贝,从而实现了零接贝,
需要注意的是,要使用 DMA scate/othr 技术,需要具备硬件支持,即网络适配器须支持 DMA SCateotb 能。此外,还需要对 DMA 进行配置和管理,以确保数据传输的正确件和安全性。
sendfiletDMA scatter/gather 实现的零拷贝,I/0 发生了2次用户空间与内核空间的上下文可以切换,以及 2 次数据拷贝。其中 2 次数据拷贝都是DMA 拷贝。这就是真正的 零拷贝,全程都没有通过 CPU 来搬运数据,所有的数据都是通过 DMA 来进行传输的。
三种“零拷贝”方式对比
mmap+write | sendfile | sendfile+DMA scatter/gathe | |
上下文切换次数 | 4 | 2 | 2 |
数据拷贝次数 | 3 = 2(DMA拷贝) +1(CPU拷贝) | 3 = 2(DMA拷贝) +1(CPU拷贝) | 2 = 2(DMA拷贝) +0(CPU拷贝) |
六.java提供的零拷贝技术
java NIO对mmap的支持
文章来源:https://www.toymoban.com/news/detail-433185.html
java NIO对sendfile的支持
文章来源地址https://www.toymoban.com/news/detail-433185.html
收队!
到了这里,关于高性能——零拷贝的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!