什麼是零拷貝


學習Netty時 看到Netty高性能的原因之一是使用零拷貝技術

學習Kafka時 看到其高性能的原因之一也使用了零拷貝技術

那麼到底什麼是零拷貝?本文簡單做個描述。

先解釋幾個概念

  • 用戶態:只能受限地訪問內存,不允許訪問外圍設備。佔用 CPU 的能力被剝奪,CPU資源可以被其他程序獲取。
  • 內核態:CPU可以訪問內存中所有的數據,包括外圍設備,例如硬盤、網卡,CPU也可以將自己從一個程序切換到另一個程序。
  • DMA (direct memory access):直接內存訪問,是一種不經過CPU而直接從內存存取數據的數據交換模式。在DMA模式下,CPU只須向DMA控制器下達指令,讓DMA控制器來處理數據的傳送,數據傳送完畢再把信息反饋給CPU,這樣就很大程度上減輕了CPU資源佔有率,可以大大節省系統資源

傳統的數據拷貝方法

下圖為傳統的數據拷貝方法:

什麼是零拷貝

上圖分別對應傳統 I/O 操作的數據讀寫流程,整個過程涉及 4 次 拷貝,

  • 1 .數據從磁盤讀取到內核的read buffer
  • 2. 數據從內核緩衝區拷貝到用戶緩衝區
  • 3. 數據從用戶緩衝區拷貝到內核的socket buffer
  • 4. 數據從內核的socket buffer拷貝到網卡接口的緩衝區


並且在用戶態和內核態中間進行了2次切換,無疑也加重了CPU負擔。

在此過程中,我們沒有對文件內容做任何修改,那麼在內核空間和用戶空間來回拷貝數據無疑就是一種浪費,而零拷貝主要就是為了解決這種低效性。

解決方案

一個很明顯的著力點就是減少數據在內核空間和用戶空間來回拷貝。

Linux能夠做到在數據傳輸的過程中,避免數據在操作系統內核態buffer和用戶態buffer之間進行復制。Linux中提供類似的系統調用函數主要有mmap()、sendfile()及splice()。下面介紹其中兩種。

  • mmap

我們減少拷貝次數的一種方法是調用mmap()來代替read調用:

<code>buf = mmap(diskfd, len);
write(sockfd, buf, len);/<code>

應用程序調用mmap(),磁盤上的數據會通過DMA被拷貝的內核緩衝區,接著操作系統會把這段內核緩衝區與應用程序共享,這樣就不需要把內核緩衝區的內容往用戶空間拷貝。應用程序再調用write(),操作系統直接將內核緩衝區的內容拷貝到socket緩衝區中,這一切都發生在內核態,最後,socket緩衝區再把數據發到網卡去。

什麼是零拷貝


使用mmap替代read很明顯減少了一次拷貝,當拷貝數據量很大時,無疑提升了效率。

  • sendfile

從2.1版內核開始,Linux引入了sendfile來簡化操作。#includessize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

系統調用sendfile()在代表輸入文件的描述符in_fd和代表輸出文件的描述符out_fd之間傳送文件內容(字節)。描述符out_fd必須指向一個套接字,而in_fd指向的文件必須是可以mmap的。這些侷限限制了sendfile的使用,使sendfile只能將數據從文件傳遞到套接字上,反之則不行。
使用sendfile不僅減少了數據拷貝的次數,還減少了上下文切換,數據傳送始終只發生在kernel space。


什麼是零拷貝

方案對比

限於篇幅原因並沒有把所有的零拷貝方式都介紹完整。

Kafka採用的是Linux系統的函數sendfile(),允許操作系統將數據從Page Cache直接發送到網絡,以此來避免數據複製。

Netty 中的零拷貝和上面提到的操作系統層面上的零拷貝不太一樣, 我們所說的 Netty 零拷貝完全是基於(Java 層面)用戶態的,它的更多的是偏向於數據操作優化這樣的概念,具體表現在以下幾個方面:

  • Netty 通過 DefaultFileRegion 類對 java.nio.channels.FileChannel 的 tranferTo() 方法進行包裝,在文件傳輸時可以將文件緩衝區的數據直接發送到目的通道(Channel)
  • ByteBuf 可以通過 wrap 操作把字節數組、ByteBuf、ByteBuffer 包裝成一個 ByteBuf 對象, 進而避免了拷貝操作
  • ByteBuf 支持 slice 操作, 因此可以將 ByteBuf 分解為多個共享同一個存儲區域的 ByteBuf,避免了內存的拷貝
  • Netty 提供了 CompositeByteBuf 類,它可以將多個 ByteBuf 合併為一個邏輯上的 ByteBuf,避免了各個 ByteBuf 之間的拷貝

參考 :

  • https://www.jianshu.com/p/fad3339e3448
  • https://segmentfault.com/a/1190000007560884
  • https://juejin.im/post/5d84bd1f6fb9a06b2d780df7
  • https://www.infoq.cn/article/netty-high-performance/


分享到:


相關文章: