💡那么为什么需要零拷贝技术? 它是怎么演进的过来的呢?
首先了解零拷贝之前呢, 我们先来认识一下零拷贝的前世今生.先来回顾一下消息传输的正常流程
举个例子说明
就举寄快递的例子吧 ,一般我们先把快递送到附近的驿站, 然后由快递员前往驿站拿取快递,最后将快递进行寄出。
使用快递驿站寄快递场景
这时候你会发现,寄出快递的快慢完全由快递员决定,快递驿站仅仅是个“中转站” 而已, 那么你会想了,有没有更快的方法?
那我能不能直接将快递拿给快递员 直接寄出呢?这样会不会快点
家里取快递案例
答案是肯定的,这样做直接省去了快递驿站的中转, 直接交由快递小哥,减少了你拿快递去驿站的时间 以及 快件在驿站的逗留时间。
💡那么在实际计算机里面是这部分是怎么流转的呢?
首先我们先来了解一下计算机各自有自己的操作空间,一般由软件操作的空间 我们叫用户态,由操作系统操作的空间一般叫内核态。
首先由软件进行发起文件查询, 然后由操作系统去操作底层的硬盘进行文件获取,然后在由操作系统将文件传递到软件中进行展示。
这是一次的数据传输操作流程
主要过程如下
1.内核态通过DMA进行拷贝磁盘文件到内核缓冲区中。
备注:DMA拷贝是指由DMA控制器将磁盘控制器缓冲区中的数据拷贝到内核缓冲区中
磁盘文件DMA拷贝
2. 用户态在从内核缓冲区中奖数据拷贝到用户缓冲区中,这时候应用程序才可以使用这部分数据,并且这个过程主要由CPU完成的。
磁盘文件到用户态数据传输流程
大家应该还有留意上图中有切换1 切換2 的示意,这里主要是上下文的切换,由于需要从用户态切换到内核态进行数据读取, 所以需要上下文间也进行切换,先进行保存当前执行状态方便切换后续的流程正常进行,所以这里需要进行两次的上下文切换,并且每次上下文切换是一种开销较大的操作,因为它涉及保存和恢复大量的执行状态信息,所以会造成一定程度的系统资源开销。
备注:
上下文切换是指操作系统在多任务处理时,从一个任务(或进程)切换到另一个任务(或进程)时,保存当前任务的执行状态(包括寄存器状态、程序计数器值等),以便稍后能够恢复该任务的执行。这种切换是操作系统进行任务调度和管理的基本操作之一。
3. 如果要将这个数据进行发送给其他机器,那么又需要调用操作系统进行消息发送
用户态发送消息到操作系统
这里和上述第二个流程一样,我们又需要操作系统帮忙将消息进行发送,这里又涉及两次上下文的切换,用户缓冲区在将里面的数据通过CPU拷贝到内核态的Socket缓冲区中(网络传输缓冲区)
4.最后操作系统操作网卡将数据通过DMA拷贝到网卡上进行消息发送。
传统磁盘发送数据流程
💡唉,好长的流程,就不能简化么?
确实,但这是操作系统的操作流程,为了更好的保证操作系统的安全以及各个层级之间的界限,这个做是合理且正常的 ,但不是所有数据都需要通过用户态进行的,就像开头说的那样,
我想直接将快递发给快递小哥,不需要快递驿站的中转也是可以的,所以零拷贝技术就油然而生了。
💡零拷贝
原理:传统的数据传输方式通常涉及将数据从一个缓冲区复制到另一个缓冲区,这会产生额外的开销和延迟。而零拷贝技术则通过避免数据在传输过程中的复制,直接在内存中操作数据的指针,从而节省了CPU和内存资源的消耗,也就是说比较传统的消息,在消息处理 I/O操作的时候减少频繁复制数据的问题,从而节省CPU和内存的消耗。
简而言之就是简化流程,我们想要操作系统帮我做到,主要的目标直接帮我将数据直接发送出去,不需要绕这么一圈的流程,所以零拷贝主要目的是减少流程中转,提升数据传输的效率,降低系统资源的消耗。
💡那么零拷贝的实现方式有多少种?什么是mmap?sendfile呢?
关于零拷贝主要实现的技术有mmap+write ,sendfile ,splice 等几种方式。接着我们一起来探讨主流的mmap 以及sendFile模式吧
💡mmap +write
嗯, 相对于传统的4次拷贝,我们来优化一下吧,看看能不能减少一下传输的流程。目前觉得最应该优化的就是用户缓冲区,他和快递驿站一样就是过度的,
那么能不能由快递驿站通知快递员直接上门取件呢?这样岂不是减少了用户拿快递去驿站的时间么
减少一次拷贝到过程示图
mmap 技术就是利用虚拟内存(内存地址映射),让原来用户空间和内存空间分开种开辟一个共享的空间进行使用,那么内核态只需要将数据复制到共享区,然后应用程序将读取的数据写回socket的时候就不需要进行一次多余的拷贝了,具体如下图所示
mmap虚拟缓冲
这么做的原因由于虚拟内存地址的映射,这样在用户缓冲区读取信息,可以直接吧内存缓冲区的数据进行读取 然后复制到Socket缓冲区即可,不需要从用户态进行复制内核态,直接从内核缓冲区进行复制即可,因为这一块内存是共享了的,物理内存地址一致,用户态可以直接操作。这样做法可以减少一次CPU复制到过程, 但是还是由用户态发起的上下文的切换还是需要切换4次 用户缓冲区还是需要将数据读取到用户缓冲区,然后在写回去缓冲区。
那么还有更加完美的方法吗?
💡sendFile
sendFile
快看 目前我们只有三次拷贝 和 两次上下文切换啦,主要减少了一次由应用程序发起的write请求,直接发起sendfile 操作 所以只需要读的两次上下文切换
💡还能继续减少吗? 能不能把CPU copy 也减少了呢?
sendfile的scatter和gather
那就是采取文件描述符的形式,让socket缓冲区直接使用文件描述符直接读取内核缓冲区的内容,避免CPU进行数据的拷贝,然后直接DMA到网卡中,再一次减少一个CPU copy,这样只有两个上下文的切换 以及两次数据的拷贝。
备注:
文件描述符是操作系统提供给进程的一种标识文件和I/O资源的方式,它允许进程通过唯一的整数来访问和操作这些资源。
总结:
零拷贝技术作为数据传输优化的重要特性之一,在大数据处理领域发挥着重要作用。引入了零拷贝技术后,通过减少复制带来的资源消耗以及上下文切换带来的开销,进一步提升系统的性能和效率,实现更高效的数据传输和处理。
好啦,这就是零拷贝技术的介绍。
推荐阅读
https://juejin.cn/post/6995519558475841550
🗣️那么后续想要了解kafka的高可用是如何做到的?
那么请留意后期的推文,或者关注我集中更新哦~