大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺
Kafka之所以那么快,其中一个很大的原因就是零拷贝(Zero-copy)技术,零拷贝不是kafka的专利,而是操作系统的升级,又比如Netty,也用到了零拷贝。下面我就画图讲解零拷贝,如果对你有帮助请点个赞支持。
传统IO
kafka的数据是要落入磁盘的,那么必然牵扯到磁盘的IO,传统磁盘IO又叫缓存IO,效率是很低的,那么为什么效率低呢?我们先来粗略讲讲操作系统的知识。
用户空间以及内核空间的概念:
我们知道现在操作系统都是采用虚拟存储器。那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方)。操心系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核,保证内核的安全,操心系统将虚拟空间划分为两部分,一部分为内核空间(Kernel space),一部分为用户空间(User space)。针对Linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空间,而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间。每个进程可以通过系统调用进入内核,因此,Linux内核由系统内的所有进程共享。于是,从具体进程的角度来看,每个进程可以拥有4G字节的虚拟空间。
传统的文件读写或者网络传输,通常需要将数据从内核态转换为用户态。应用程序读取用户态内存数据,写入文件 / Socket之前,需要从用户态转换为内核态之后才可以写入文件或者网卡当中。我们可以称之为read/write模式,此模式的步骤为:
- 首先,调用read时,磁盘文件拷贝到了内核态;
- 之后,CPU控制将内核态数据copy到用户态下;
- 调用write时,先将用户态下的内容copy到内核态下的socket的buffer中;
- 最后将内核态下的socket buffer的数据copy到网卡设备中传送;
DMA
DMA(Direct Memory Access,直接存储器访问) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载 。通俗来讲,就是DMA 传输将数据从一个地址空间复制到另外一个地址空间,当CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成,也就是两个硬件之间完成的,而没有CPU的参与,那么CPU就可以释放出来做别的事情,这样极大地提高了效率。我们常见的硬件设备如网卡、磁盘设备、显卡、声卡之类的都支持DMA。
所以上面所说的read/write模式大概如图所示:
传统IO有两个很大的缺点导致很慢:
- 我们可以清楚的看到共产生了4次copy,从磁盘文件到Kernal的相互读写是支持DMA copy的,但即使是这样,从Kernal到User没有硬件的支持所以不支持DMA,还有两次CPU copy。
- Kafka只是把文件存放到磁盘之后通过网络发出去,中间并不需要修改什么数据,那read和write的两次CPU copy的操作完全是多余的。
零拷贝
mmap
mmap是零拷贝的一种,主要就是去掉read write这两次CPU copy以提升性能,调用mmap()来代替read调用:
buf = mmap(diskfd, len);
write(sockfd, buf, len);
此模式步骤为:
- 用户程序调用 mmap(),磁盘上的数据会通过 DMA被拷贝的内核缓冲区;
- 接着操作系统会把这段内核缓冲区与用户程序共享,这样就不需要把内核缓冲区的内容往用户空间拷贝;
- 用户程序再调用 write(),操作系统直接将内核缓冲区的内容拷贝到 socket缓冲区中;
- 最后, socket缓冲区再把数据发到网卡去。
这显然是一个伟大的进步,把上下文的切换次数从4次减少到2次,同时也把数据copy的次数从4次降低到了3次。
sendfile
Linux2.1内核开始引入了sendfile函数,用于将文件通过socket传送。开始时跟mmap没什么区别,但是Linux2.4做出了重大优化,将零拷贝推到顶峰。
优化后的处理过程如下:
- 将文件拷贝到kernel buffer中;
- 向socket buffer中追加当前要发生的数据在kernel buffer中的位置和偏移量;
- 根据socket buffer中的位置和偏移量直接将kernel buffer的数据copy到网卡设备中;
如图:
经过上述过程,数据只经过了2次copy就从磁盘传送出去了。这个才是真正的Zero-Copy(这里的零拷贝是针对kernel来讲的,数据在kernel模式下是Zero-Copy)。
正是Linux2.4的内核做了改进,Java中的TransferTo()实现了Zero-Copy。
测试
在Windows10上测试:
测试结果仅供参考 ,并不是平均数,所以可能偏差较大。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/191178.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...