1. 程式人生 > >netty學習十三:零拷貝底層實現原理

netty學習十三:零拷貝底層實現原理

零拷貝概述

零拷貝可以避免無謂的copy動作,為了說清楚這一點,本文會先從傳統的讀寫操作開始介紹。

傳統讀操作

這裡寫圖片描述

當應用發起一個從磁碟讀取檔案的操作時,請求會先經過核心,由核心與磁碟進行互動。資料會從磁碟拷貝到核心的快取區中。這個copy動作由DMA完成,整個過程中基本上不消耗CPU。

DMA

硬體和軟體的資訊傳輸,可以使用DMA(direct memory access)來完成

如果應用想拿到資訊,還得從核心緩衝區獲取,這裡又存在一個cpu copy的動作,將資料從核心緩衝區中拷貝到應用緩衝區中。這個copy動作是需要消耗CPU的。

如上文描述,要想從磁碟中讀取出內容,需要2次copy動作。

傳統寫操作

這裡寫圖片描述
應用想將資料傳遞給客戶端,必須經過核心,將資料先從應用緩衝區中copy(cpu copy)到核心的緩衝區中,對於寫操作而言,通常這個核心緩衝區叫

kernel socket buffer

接著DMA會從kernel socket buffer中將資料拷貝(DMA copy)到protocol engine,最終將資料傳送給客戶端。

這裡又發生2次copy動作,一次是cpu copy,另外一次是DMA copy。

由此可見,當客戶端對應用發起請求的時候,應用想將資料傳遞給客戶端,期間需要經過4次資料拷貝動作,2次cpu copy,另外2次是DMA copy。期間應用程式相當於是一箇中間者角色。

有沒有辦法將其中的2次cpu copy去掉呢?因為我們總是希望CPU能處理更多的事情,而不是浪費在這種無謂的copy操作當中。

答案是利用硬體以及作業系統核心,進行資料零拷貝

使用mmap()替代read()

在此之前,為了響應客戶端的請求,並把資料傳送給客戶端,服務端進行了兩個核心操作

read()
write()

在Linux中有一個mmap操作,可以減少拷貝次數,我們將read操作換成mmap操作

mmap()
write()

換成mmap操作後,仍然需要3次copy動作。

  • 第一次,DMA將資料從磁碟中拷貝(DMA copy)到kernel read buffer核心緩衝區中;
  • 第二次,當應用呼叫write方法時,會將kernel read buffer中的資料拷貝(CPU copy)到kernel socket buffer核心緩衝區;
  • 第三次,將kernel socket buffer中的資料拷貝到protocol engine(協議引擎)。

第二次為什麼是cpu copy呢,因為是記憶體資料拷貝到另外的記憶體區域,必須CPU參與。

注意

在傳統的讀寫操作中,是由應用將資料從應用緩衝區中寫入到核心kernel socket buffer的。而使用mmap操作進行零拷貝時,由於不需要應用參與拷貝資料,CPU必須將資料從kernel read buffer中拷貝到kernel socket buffer。

使用sendfile()減少一次上下文切換

到此為止,已經減少了一次copy動作,但是使用

mmap()
write()

的組合還是有個缺點,就是應用呼叫write方法時,需求進行

應用mode到核心mode的一次切換

能不能在copy次數為3的情況下,將這次切換去掉呢?答案是使用

sendfile()

替換上面的

mmap()
write()

使用sendfile操作,copy次數還是為3,不過少了一次上下文切換。

去掉最後一個cpu copy

無論是使用

mmap()
write()

還是

sendfile()

仍然需要3次copy動作,兩個是DMA copy,一個是cpu copy,能不能將這個cpu copy也去掉呢?

當然可以,需要藉助DMA引擎的

gather操作

gather操作可以從多個不同的緩衝區中讀取資料,DMA引擎是可以直接從kernel read buffer中讀取資料的,而不用先將資料從kernel read buffer拷貝到kernel socket buffer後再進行讀取操作。

到此,資料拷貝已經完全跟CPU沒關係了,都是由核心操作的,核心只需要兩次拷貝動作。

引用的文章