流殃的博客

| Comments

what

零拷贝是相对于操作系统来说的,不需要cpu来进行拷贝数据了,所以称之为零拷贝。

但是实际上还是会进行数据拷贝的。

传统IO

image-1661677100977

传统IO

  1. 将磁盘文件,拷贝到操作系统内核缓冲区
  2. 将内核缓冲区的数据,拷贝到应用程序的buffer
  3. 将应用程序buffer中的数据,再拷贝到socket网络发送缓冲区(属于内核缓冲区)
  4. 将socket buffer数据,拷贝到网卡,由网卡进行网络传输

传统IO方式,读取磁盘文件进行网络发送,经过4次数据拷贝,4次上下文切换。

如果你单独看这个过程感觉是2次上下文切换,但是其实一开始操作的时候是在用户态的,最后数据从网卡发送结束之后,也要返回用户态。

MMAP

image-1661677123599

这种方式使用mmap()代替了read()

  1. 磁盘的数据通过DMA拷贝到内核缓冲区
  2. 操作系统把这块内核缓冲区与应用程序共享,避免了用户缓冲区和内核缓冲区的跨界复制
  3. 应用程序调用write()直接从内核缓冲区的内容拷贝到socket缓冲区
  4. 最后系统将socket的数据传输到网卡,由网卡进行传输

MMAP减少了一次拷贝,提升了效率,但是并不减少上下文切换的次数,所以总的来说mmap发生了 3次数据拷贝,4次上下文切换

SendFile

2.1版本

image-1661677152020

这种方式是使用sendfile代替了read+write操作

  1. 首先sendfile系统调用,通过DMA引擎将磁盘文件拷贝到内存缓冲区
  2. 在内核缓冲区,内核将数据拷贝到socket缓冲区
  3. 最后,DMA将数据从内核拷贝到网卡,由网卡传输

数据总共发生3次拷贝,2次上下文切换

在这个版本下 其实还是有cpu 拷贝的,所以这个版本是不能称之为零拷贝的,至少2.4版本之后了

2.4版本

image-1667972765548

  1. 磁盘数据通过 DMA 拷贝到内核态 Buffer
  2. 直接通过 DMA 拷贝到 NIC Buffer

对比

  1. 都是Linux内核提供,实现零拷贝的API
  2. sendfile是将读到内核缓冲区的数据,直接转到socket buffer,进行网络发送
  3. mmap是将磁盘文件读取到内核缓冲区后进行映射,和用户缓冲区共享数据,然后CPU在拷贝数据到socket buffer,进行网络发送
  4. mmap 适合小数据量读写,sendFile 适合大文件传输。
  5. mmap 需要 4 次上下文切换,3 次数据拷贝;sendFile 需要 3 次上下文切换,最少 2 次数据拷贝。
  6. sendFile 可以利用 DMA 方式,减少 CPU 拷贝,mmap 则不能(必须从内核拷贝到 Socket 缓冲区)

使用

  • rocketMQ 在消费消息时,使用了 mmap
  • kafka 使用了 sendFile
  • 如Tomcat、Nginx、Apache等web服务器返回静态资源等,将数据用网络发送出去,都运用了sendfile。
  • Java NIO对sendfile的支持就是FileChannel.transferTo()/transferFrom()。

参考链接

Comments

评论