Netty 零拷贝机制

这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战

前言

零拷贝机制(Zero-Copy)是在操作数据时不需要将数据从一块内存区域复制到另一块内存区域的技术,这样就避免了内存的拷贝,使得可以提高CPU的。零拷贝机制是一种操作数据的优化方案,通过避免数据在内存中拷贝达到的提高CPU性能的方案。

1. 什么是零拷贝机制?

场景:我们以文件服务器下载文件为例, 服务器将硬盘中的数据通过网络发送到客户端?

image.png

image.png 如图所示,整个过程可以分为4步。

1、操作系统通过DMA传输将硬盘中的数据复制到内核缓冲区

2、操作系统执行read方法将内核缓冲区的数据复制到用户空间

3、操作系统执行write方法将用户空间的数据复制到内核socket缓冲区

4、操作系统通过DMA传输将内核socket缓冲区数据复制给网卡发送数据

2. 操作系统零拷贝?

我们可以看到,操作系统从磁盘拷贝数据到内核空间, 在从内核空间拷贝到用户空间,然后在拷贝到内核的socket内核空间,然后拷贝到网卡传输。 感觉有点多次一举, 那么操作系统为什么会这么设计?

因为对于操作系统来说,可能有多个应用程序会同时使用这些数据,并有可能进行修改,如果让大家都使用同一份内核空间的数据就会产生冲突。因此,操作系统设计为:每个应用程序想使用这些数据都必须复制一份到自己的用户空间,这样就不会互相影响了。所以这个机制在碰到数据不需要做修改的场景时就产生了浪费,数据本来可以呆在内核缓冲区不动,没必要再多此一举拷贝一次到用户空间。

扫描二维码关注公众号,回复: 13425487 查看本文章

mmap优化

为了避免上面的这种浪费,最开始采用了mmap调用的方式来进行优化。 mmap 通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内核空间的数据。

image.png 现在,你只需要从内核缓冲区拷贝到 Socket 缓冲区即可,这将减少一次内存拷贝(从 4 次变成了 3 次),但不减少上下文切换次数。

sendfile优化

Linux 2.1 版本 提供了 sendFile 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到 Socket Buffer,同时,由于和用户态完全无关,就减少了一次上下文切换。

image.png

如上图,我们进行 sendFile 系统调用时,数据被 DMA 引擎从文件复制到内核缓冲区,然后调用 write 方法时,从内核缓冲区进入到 Socket,这时,是没有上下文切换的,因为都在内核空间。

sendfile再次优化

Linux 在 2.4 版本中,做了一些修改,避免了从内核缓冲区拷贝到 Socket buffer 的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝。具体如下图: image.png 原来的3次拷贝,现在只需要2 次拷贝:第一次使用 DMA 引擎从文件拷贝到内核缓冲区,第二次从内核缓冲区将数据拷贝到网络协议栈;内核缓存区只会拷贝一些 offset 和 length 信息到 SocketBuffer,基本无消耗。

零拷贝定义: "Zero-copy" describes computer operations in which the CPU does not perform the task of copying data from one memory area to another.

从用户空间到socket缓冲两次复制。但是明明还有两次数据的复制,为什么要叫“零拷贝”呢?这是因为从操作系统的角度来说,数据没有从内存复制到内存的过程,也就没有了CPU参与的过程, 所以对于操作系统来说就是零拷贝了。

mmap和sendfile区别

mmap 和 sendFile 的区别。

  1. mmap 适合小数据量读写,sendFile 适合大文件传输。
  2. mmap 需要 4 次上下文切换,3 次数据拷贝;sendFile 需要 3 次上下文切换,最少 2 次数据拷贝。
  3. sendFile 可以利用 DMA 方式,减少 CPU 拷贝,mmap 则不能(必须从内核拷贝到 Socket 缓冲区)。

实际场景中rocketMQ 在消费消息时,使用了 mmap。kafka 使用了 sendFile。

猜你喜欢

转载自juejin.im/post/7035204636936142878