零拷贝

所谓的零拷贝:是指不需要cpu参与的拷贝
传统I/O(read/write):
->代表数据流向
read: 磁盘-->DMA->内核缓冲->用户缓冲(其中内核->用户 是需要cpu进行拷贝,需要切换2次用户态/内核 磁盘到DMA 不需要cpu进行操作)
write 协议引擎<-DMA <-内核socket缓冲 <-用户缓冲(其中用户->内核 是需要cpu进行拷贝,需要切换2次用户态/内核 DMA到协议不需要cpu)

sendFile(零拷贝):
一次完整的读数据再写:磁盘-->DMA->内核缓冲->内核socket缓冲-->DMA-->协议引擎(内核缓冲->内核socket缓冲才是cpu拷贝,其他都是DMA拷贝)

优化版本sendFile(带有scatter/gather):
一次完整的读数据再写:磁盘-->DMA->内核缓冲-->内核socket缓冲-->DMA-->协议引擎(内核缓冲->内核socket缓冲不是拷贝数据,只是拷贝描述符,所以基本上不耗性能
到这边就没有cpu拷贝了 所以就零拷贝)

mmap:mmap(内存映射)是一个比sendfile昂贵但优于传统I/O的方法,mmap只能替换read
一次完整的读数据再写:磁盘-->DMA->内核缓冲(用户控件)->内核socket缓冲-->DMA-->协议引擎(即用户和内核共享同一块缓冲内存)
可以看到MMAP 只是比传统IO好,但是对于改良后的sendFile来说没有优势和改良前的sendFile一致 还是需要一次cpu拷贝

FileChannel:
其传输的方法transferTo、transferFrom 是根据系统底层是否支持sendFile改良版本,如果支持其也是真正的零拷贝

总结:sendFile改良前后都少了一个从内核缓冲到用户状态,如果这个时候用户需要对读取的数据进行修改,则其还是需要一次cpu copy
这个时候反而mmap 不需要cpu copy

netty的零拷贝:
DirectByteBuf通过直接在堆外分配内存的方式,避免了数据从堆内拷贝到堆外的过程
通过组合ByteBuf类:即CompositeByteBuf,将多个ByteBuf合并为一个逻辑上的ByteBuf, 而不需要进行数据拷贝
通过各种包装方法, 将 byte[]、ByteBuf、ByteBuffer等包装成一个ByteBuf对象,而不需要进行数据的拷贝
通过slice方法, 将一个ByteBuf分解为多个共享同一个存储区域的ByteBuf, 避免了内存的拷贝,这在需要进行拆包操作时非常管用
通过FileRegion包装的FileChannel.tranferTo方法进行文件传输时, 可以直接将文件缓冲区的数据发送到目标Channel, 减少了通过循环write方式导致的内存拷贝。但是这种方式是需要得到操作系统的零拷贝的支持的,如果netty所运行的操作系统不支持零拷贝的特性,则netty仍然无法做到零拷贝

你可能感兴趣的:(零拷贝)