知識補充:
1、DMA是直接內存訪問(Direct Memory Access) 技術,早期 DMA 只存在在主板上,如今由于 I/O 設備越來越多,數據傳輸的需求也不盡相同,所以每個 I/O 設備里面都有自己的 DMA 控制器。
2、每次系統調用都得先從用戶態切換到內核態,等內核完成任務后,再從內核態切換回用戶態。
我們都聽過 kafka 很快,其中一個原因是 kafka 使用零拷貝。看看從文件中讀取數據并通過網絡將數據傳輸到另一個程序的場景,在內部的復制操作,需要在用戶模式和內核模式之間進行四次上下文切換,并且進行數據復制四次。
- 來自磁盤的數據被復制到內核讀取緩沖區中。第一個拷貝由直接內存訪問 (DMA) 引擎執行,該引擎從磁盤讀取文件內容并將它們存儲到內核地址空間緩沖區中。
- 從內核緩沖區復制到應用程序讀取緩沖區(上下文切換,切換到用戶模式)。
- 從應用程序緩沖區復制到內核套接字緩沖區并再次將數據放入內核地址空間緩沖區(切換到內核模式)。
- 從套接字緩沖區它將復制到網絡緩沖區
完成所有四個操作后,它將再次切換到用戶模式。
回顧以上動作,其實發現實際上第二個和第三個數據拷貝是可以避免的。JAVA 類庫通過 java.nio.channels 中的 transferTo() 方法在 UNIX 系統上執行零副本,使用零拷貝的應用程序請求內核直接將數據從磁盤文件復制到套接字,而不通過應用程序。零拷貝極大提高了應用程序性能,并減少了內核和用戶模式之間的上下文切換次數。
我們看看 transferTo() 是如何復制數據的?
- transferTo()方法致使文件內容被 DMA(直接內存訪問)引擎復制到讀取緩沖區中。
- 然后內核將數據復制到套接字關聯的內核緩沖區中。
- 從套接字緩沖區它將復制到網絡緩沖區
這里我們還需要 3 個副本和 2 個上下文切換。
但當前還沒有達到零拷貝,如果底層網卡支持收集操作,可以進一步減少內核重復拷貝數據的操作。在 linux 內核 2.4 及更高版本中,套接字緩沖區描述符支持該場景。
- transferTo()方法使 DMA 引擎將文件內容復制到內核緩沖區中。
- 沒有數據被復制到套接字緩沖區中,相反,只有有關數據位置和長度信息的描述符才會附加到套接字緩沖區。DMA 引擎將數據直接從內核緩沖區傳遞到網絡緩沖區。
Kafka 和 Nginx 都有實現零拷貝技術,這將大大提高文件傳輸的性能。拷貝技術,本質上講就是通過減少非必要的內存拷貝以及上下文切換,來提高文件在通道間復制速度的一種技術。以本文中的transferTo()方法為例,通過該技術,可以將原來四次內存間拷貝減少成兩次,將四次上下文切換減少成兩次,大大提高復制的速度。但零拷貝技術并非萬能的,它有自己的使用場景,對于將大量數據從一個 I/O 通道復制到另一個通道的情況(例如 Web 服務器),都是合適的。