netty底层知识

零拷贝

2020-03-27  本文已影响0人  雪飘千里

我们在Java NIO,Netty,Kafka等框架中经常见到零拷贝,通常作为其性能优异的一个重要表现。

下面从 I/O 的几个概念开始,进而再分析零拷贝。

1、I/O 概念

1.1 缓冲区

缓冲区是所有 I/O 的基础,I/O 讲的无非就是把数据移进或移出缓冲区;进程执行 I/O 操作,就是向操作系统发出请求,让它要么把内核缓冲区的数据排干(写),要么填充内核缓冲区(读)。

下图是一个java进程发起Read请求的流程图:

image.png

如果进程发起 Write 请求,同样需要把用户缓冲区里面的数据 Copy 到内核的 Socket 缓冲区里面,然后再通过 DMA 把数据 Copy 到网卡中,发送出去。

如下图所示:

image.png

从读写过程中可以很明显的看出,每次都需要把内核空间的数据拷贝到用户空间(读),或者把用户空间的数据拷贝到内核空间(写)中,挺浪费空间的。

零拷贝的出现就是为了解决这种问题的

1.2 虚拟内存

所有现代操作系统都使用虚拟内存,使用虚拟的地址取代物理地址,这样做的好处是:

利用第一条特性可以把内核空间地址和用户空间的虚拟地址映射到同一个物理地址,这样 DMA 就可以填充对内核和用户空间进程同时可见的缓冲区了。

大致如下图所示:


image.png

这样就省去了内核与用户空间的往来拷贝,从而可以提升性能。

2、零拷贝实现方式之mmap+write

mmap 是一种内存映射文件的方法,即将一个文件或者其他对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对应关系,就是上面所说的虚拟内存。

DMA加载磁盘数据到kernel buffer后,用户buffer和内核缓冲区(kernel buffer)进行映射,数据在用户缓冲区和内核缓存区的copy就能省略。

但是如果我们是直接从磁盘读取数据,然后写入网卡时,还是需要从内核空间kernel buffer 把数据copy 到 内核空间socket buffer。

image.png image.png

3、零拷贝实现方式之Sendfile

Sendfile 系统调用在Linux内核版本 2.1 中被引入,目的是简化通过网络在两个通道之间进行的数据传输过程。

Sendfile 系统调用的引入,不仅减少了数据复制,还减少了上下文切换的次数,大致如下图所示:

image.png

数据传送只发生在内核空间,所以减少了一次上下文切换;但是还是存在一次 Copy,能不能把这一次 Copy 也省略掉?

Linux2.4 内核中做了改进,将内核 buffer 中对应的数据描述信息(内存地址,偏移量)记录到相应的 Socket 缓冲区当中,这样连内核空间中的一次 CPU Copy 也省掉了,当DMA copy数据时,可以根据socket buffer中的内存地址和偏移量直接从kernel buffer中读取数据

image.png image.png

4、Kafka中的零拷贝

Kafka中的零拷贝主要体现在一下两个方面:

5、Netty中的零拷贝

Kafka中的零拷贝主要体现在一下三个方面:

image.png

6、java NIO中的零拷贝——transferTo

transferTo()的实现方式就是通过系统调用sendfile(),如下图数据流向

image.png image.png
上一篇下一篇

猜你喜欢

热点阅读