Java NIO——Zero-copy

技術介紹

零複製(英語:Zero-copy;也譯零拷貝)技術是指計算機執行操作時,CPU不需要先將數據從某處內存複製到另一個特定區域,從而可以減少上下文切換及CPU的拷貝時間,通常用於通過網絡傳輸文件時節省CPU週期和內存帶寬。

假如我們要實現這樣的功能:將文件中的字節複製到套接字中

read(file, tmp_buf, len);
write(socket, tmp_buf, len);

傳統實現方式

過程如下

  1. read()函數調用導致了一次用戶態到內核態的上下文切換。系統內部的sys_read()調用被用於把數據讀取出來。第一次複製是以DMA引擎的方式呈現的,DMA把數據讀取出來並存入kernel address space buffer。

  2. 數據被讀取返回後導致了上下文從內核態切換到用戶態,現在數據存在user address space buffer中。

  3. send()操作再次把上下文從用戶態切換到內核態。並且數據被從用戶緩存拷貝到kernel address space buffer中。

  4. send()操作返回,這時又從內核態返回到用戶態,並且發生最後一次數據拷貝,數據從kernel buffer拷貝到protocol engine中。

這個過程當中一共出現了4次數據拷貝和4次用戶態-內核態用戶態-內核態上下文切換(每一次系統調用都是兩次上下文切換:用戶態->內核態->用戶態)。

Linux 2.4之前的底層實現

仔細檢查上面的流程,其實第二次和第三次複製是不必要的(從buffer到應用程序、從應用程序到buffer),應用程序並沒有改變數據內容,只是將其返回到socket buffer中。Java中提供了transferTo()方法,可以讓你實現數據直接從read buffer輸到 socket buffer。

transferTo() 方法將數據從一個文件channel傳輸到一個可寫channel。在內部它依賴於操作系統對 Zero-copy 的支持,在UNIX/Linux系統上, transferTo() 實際會調用 sendfile() 這個系統函數,將數據從一個文件描述符傳輸到另一個。

下圖展示了使用 transferTo()時的數據拷貝情況

image 

過程如下

  1. transferTo()調用使文件內容通過DMA的方式被複制到read buffer。然後將數據複製到與輸出的socket相關的buffer中。

  2. 第三次複製發生在DMA將數據複製到protocol engine。

這已經有了改進,我們已將上下文切換次數從四次減少到兩次,並將數據拷貝的次數從四次減少到三次(其中只有一次涉及CPU操作)

 Linux 2.4之後的底層實現

image 

過程如下

  1. transferTo方法調用使文件內容通過DMA引擎被複制到kernel buffer

  2. 無數據被複制到socket buffer。只是描述了需要被寫入的數據的位置和長度。DMA引擎直接把數據從kernel buffer複製到protocol engine

現在整個過程只有兩次上下文切換和兩次數據拷貝。

在內核爲2.4或者以上版本的linux系統上,socket緩衝區描述符將被用來滿足這個需求。這個方式不僅減少了內核用戶態間的切換,而且也省去了那次需要cpu參與的複製過程。 
從用戶角度來看依舊是調用transferTo()方法,但是其本質發生了變化:

調用transferTo方法後數據被DMA從文件複製到了內核的一個緩衝區中。

數據不再被複制到socket關聯的緩衝區中了,僅僅是將一個描述符(包含了數據的位置和長度等信息)追加到socket關聯的緩衝區中。DMA直接將內核中的緩衝區中的數據傳輸給協議引擎,消除了僅剩的一次需要cpu週期的數據複製。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章