OS层面的零拷贝和Netty零拷贝

OS层面的零拷贝和Netty零拷贝

OS层面的零拷贝

1.普通应用程序读写

OS层面的零拷贝和Netty零拷贝

DMA copy指的是直接内存存取,即不使用CPU拷贝数据到内存,而是DMA引擎传输数据到内存,用于解放CPU。

kernel buffer即内核缓冲区,从利用DMA copy直接从硬盘读到了内核的读缓冲区。

可以看到需要四次内核态和用户态的切换,4次拷贝。

2.mmap优化

OS层面的零拷贝和Netty零拷贝

mmap通过内存映射,将文件映射到内核缓冲区,用户空间可以共享内核空间的数据。数据不用从kernel buffer拷贝到user buffer了。减少了一次拷贝。

3.sendFile

OS层面的零拷贝和Netty零拷贝

sendFile系统调用时,数据被DMA引擎从文件复制到内核缓冲区,然后调write方法时,从内核缓冲区直接进入到Socket,这是没有进行态切换的。

4.继续优化

OS层面的零拷贝和Netty零拷贝

这时只需要两次拷贝,第一次使用DMA引擎从文件拷贝到内核缓冲区,第二次从内核缓冲区将数据拷贝到网络协议栈。

5.总结

所谓零拷贝不是说一次拷贝都不用,而是说避免了数据重复的副本拷贝。

mmap需要4次上下文,3次数据拷贝。sendFile需要3次上下文切换,最少2次数据拷贝。

mmap适合小数据量的读写,sendFile适合大文件传输。

sendFile可以利用DMA方式,mmap不能。

Netty中的零拷贝

  • 通过FileRegion包装的FileChannel.tranferTo实现文件传输,这个就是OS层面的sendFile。可以直接将内核文件缓冲区的数据发送到目前的Channel(Socket),避免了传统的循环读一定字节到user buffer(应用的内存)再write方式导致的内存拷贝问题。

  • netty提供的CompositeByteBuf类,可以将多个ByteBuf合并为逻辑上的一个ByteBuf,避免了各个ByteBuf之间的拷贝。这不是上面OS层,这是对于数据操作的优化,都是在用户态的。

  • ByteBuf支持slice,可以将ByteBuf分解为多个共享同一个存储内存的ByteBuf,避免了内存的拷贝。

  • 通过wrap操作实现零拷贝,例如有一个byte数组,传统的做法是创建一个ByteBuf,然后往其中写入字节。Netty提供了Unpooled.wrappedBuffer方法将bytes包装成为UnpooledHeapByteBuf对象,而在包装的过程中,是不会有拷贝操作的。即我们生成的ByteBuf对象是和bytes数组公用的同一个存储空间。