复习Kafka
的时候,涉及到零拷贝技术,特借此机会对零拷贝技术做一个系统的复习。做一个学习笔记。
IO模型的复习
首先来说下零拷贝的含义是什么:它是一种IO
操作优化技术。可以快速高效地将数据从文件系统移动到网络接口,而不需要将其从内核空间复制到用户空间。
备注:目前只有在使用NIO
以及epoll
传输的时候可以用该特性。
用户进行IO
操作,其实也就是应用程序访问系统资源,即通过系统调用 或者中断(外中断、内中断)从而使得 CPU
从用户态转向内核态。
系统调用其实就是一些函数,用于对文件和设备进行访问和控制。最常见的有两种:
read
:从文件中读取内容。write
:往文件中写入内容。在复习IO
模型相关的知识的时候,就遇到这么两个概念:
这个到底有什么区别呢?我们知道,我们的应用程序从磁盘上读取数据的时候,一般都是分成两步:
那么这个过程中就会涉及到两次数据读操作:
而访问磁盘的速度要远远小于访问内存的速度,那么整个读取数据的操作中耗费时长最长的阶段自然在磁盘读取上。因此出现了内核缓冲区以及对应的用户缓冲区。
内核缓冲区,其实可以从两个方向去理解:
Buffer
。Cache
。它的作用如下:
read
系统调用的时候,内核会读更多磁盘上的数据,以备程序后续使用。(假设我的read
请求可能只需要读100KB
的数据,那么此时内核会读200KB
,就是这个意思。)write
系统调用时,内核并不会直接把数据写入到磁盘文件中,而是写入到缓冲区中。当缓冲区中的数据积累到一定程度,才将数据真正地刷新到磁盘中。用户缓冲区的作用和内核缓冲区一样,都是数据的预读以及延时回写。不过两者出现的目的还是不一样:
DMA
的全称:Direct Memory Access
,直接存储器访问。DMA
传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。
为什么要有DMA
技术?我们来看下IO
操作的前后流程对比:
总结就是:DMA
帮助CPU
将数据从磁盘拷贝至内核缓冲区中,让CPU
解放双手。
虚拟内存:即拿出一部分硬盘空间来充当内存使用,当内存占用完时,电脑就会自动调用硬盘来充当内存,以缓解内存的紧张。
一般虚拟内存都用来替代一部分物理内存的,有这么几个好处:
我们用一个最基本的文件读取操作来看下流程,无非分为两个步骤:
备注:每次系统调用都得先从用户态切换到内核态,等内核完成任务后,再从内核态切换回用户态。因此相当于2次上下文切换。
问题就是:再这样的传统IO
传输模型中,用户为了获取服务器上的某个数据,期间竟然有4次数据的搬运过程,而过多的数据拷贝会消耗CPU资源,在高并发的情况下更是大大降低了系统的性能。
那么。“如何减少用户态和内核态之间的上下文切换以及内存拷贝的次数” 成了提高文件传输性能的一个关键点。
还记得上文提到的虚拟内存的概念吗?零拷贝技术中就用到了虚拟内存可以多对一的一个特性:将内核空间和用户空间的虚拟地址映射到同一个物理地址,这样在 I/O 操作时就不需要来回复制了。
零拷贝实现的方式有两种:
mmap + write
。sendfile
。mmap()
函数也是属于系统函数的一种,在 2.1 节当中,我们知道read()
函数会把内核缓冲区的数据拷贝到用户的缓冲区里。 而mmap()
函数就可以减少这一步开销,因为它会直接把内核缓冲区里的数据映射到用户空间。
read + write
。现在:mmap + write
。sendfile()
是一个专门发送文件的系统调用函数。
#include
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
out_fd
:目的端的文件描述符。in_fd
:源端文件描述符。offset
:源端数据偏移量。count
:复制数据的长度。最终的返回是实际复制数据的长度。该函数的作用主要有两点:
read(
)和write()
函数。即可以减少一次系统调用。SG-DMA
控制器减少数据拷贝的次数。这个SG
的前缀是什么意思呢?
SG:scatter-gather
。其原理就是在内核空间Read Buffer
和Socket Buffer
不做数据复制,而是将Read Buffer
的内存地址、偏移量记录到相应的Socket Buffer
中,这样就不需要复制。其本质和虚拟内存的解决方法思路一致,就是内存地址的记录。
流程如下:
DMA
将磁盘上的数据拷贝到内核缓冲区里。socket
缓冲区,SG-DMA
控制器直接将内核缓存中的数据拷贝到网卡的缓冲区里, 因此不需要将内核缓冲区的数据拷贝到socket
缓冲区中。如图:
这就是零拷贝技术。即没有再内存层面去拷贝数据,整个过程中CPU
都没有参与数据的拷贝,都是交给DMA
来完成。
相对于传统的文件传输方式,零拷贝的优势:
CPU
参与,因此CPU
可以在此期间做其他事情。注意:零拷贝并不是说数据传输过程中拷贝的次数为0,而是指不存在内存层面的数据拷贝(一共2次)。
最后,Kafka
就利用了零拷贝技术,因此Kafka
的IO
处理是非常快的。