Netty 零拷貝(一)Linux 零拷貝
Netty 零拷貝(一)Linux 零拷貝
本文探討 Linux 中主要的幾種零拷貝技術以及零拷貝技術適用的場景。
一、幾個重要的概念
1.1 使用者空間與核心空間
作業系統的核心是核心,獨立於普通的應用程式,可以訪問受保護的記憶體空間,也有訪問底層硬體裝置的所有許可權。為了保證使用者程序不能直接操作核心 (kernel),保證核心的安全,作業系統將虛擬空間劃分為兩部分,一部分為核心空間,一部分為使用者空間。
1.2 IO 兩個流程
網路 IO 的本質是 socket 的讀取,socket 在 linux 系統被抽象為流,IO 可以理解為對流的操作。剛才說了,對於一次 IO 訪問 (以 read 舉例),資料會先被拷貝到作業系統核心的緩衝區中,然後才會從作業系統核心的緩衝區拷貝到應用程式的地址空間。所以說,當一個 read 操作發生時,它會經歷兩個階段:
- 第一階段:等待網路上的資料分組到達,然後被複制到核心的某個緩衝區 (Waiting for the data to be ready)。
- 第二階段:把資料從核心緩衝區複製到應用程序緩衝區 (Copying the data from the kernel to the process)。
本文關注的是第二個過程:如何減少拷貝,即零拷貝。
二、傳統的 IO 流程
Linux 的 I/O 操作預設是緩衝 I/O。使用了 read 和 write 兩個系統呼叫,我們並不知道作業系統在其中做了什麼。實際上在以上 I/O 操作中,發生了多次的資料拷貝。
當應用程式訪問某塊資料時,作業系統首先會檢查,是不是最近訪問過此檔案,檔案內容是否快取在核心緩衝區,如果是,作業系統則直接根據 read 系統呼叫提供的 buf 地址,將核心緩衝區的內容拷貝到 buf 所指定的使用者空間緩衝區中去。如果不是,作業系統則首先將磁碟上的資料拷貝的核心緩衝區,這一步目前主要依靠 DMA 來傳輸,然後再把核心緩衝區上的內容拷貝到使用者緩衝區中。
接下來,write 系統呼叫再把使用者緩衝區的內容拷貝到網路堆疊相關的核心緩衝區中,最後 socket 再把核心緩衝區的內容傳送到網絡卡上。
整個過程中共產生了多次資料拷貝,即使用了 DMA 來處理了與硬體的通訊,使用者空間和系統空間 CPU 仍然需要處理兩次資料拷貝,與此同時,在使用者態與核心態也發生了 4 次上下文切換,無疑也加重了 CPU 負擔。
- 通過 DMA copy 資料從 hard drive 拷貝到 kernel buffer
- 通過 CPU copy 資料從 kernel buffer 拷貝到 user buffer
- 通過 CPU copy 資料從 user buffer 拷貝到 kernel buffer
- 通過 CPU copy 資料從 kernel buffer 拷貝到 socket buffer
- 通過 DMA copy 將 socket buffer 中的資料傳送出去
在此過程中,我們沒有對檔案內容做任何修改,那麼在核心空間和使用者空間來回拷貝資料無疑就是一種浪費,而零拷貝主要就是為了解決這種低效性。
三、零拷貝(zero-copy)
3.1 零拷貝概念
零拷貝主要的任務就是避免 CPU 將資料從一塊儲存拷貝到另外一塊儲存,主要就是利用各種零拷貝技術,避免讓 CPU 做大量的資料拷貝任務,減少不必要的拷貝,或者讓別的元件來做這一類簡單的資料傳輸任務,讓 CPU 解脫出來專注於別的任務。這樣就可以讓系統資源的利用更加有效。
如何減少資料拷貝的次數呢?一個很明顯的著力點就是減少資料在核心空間和使用者空間來回拷貝,這也引入了零拷貝的一個型別:減少使用者空間到核心空間的拷貝。
2.2 減少使用者空間和核心空間的拷貝
應用程式呼叫 sendfile,磁碟上的資料會通過 DMA 被拷貝的核心緩衝區,接著作業系統會把這段核心緩衝區與應用程式共享,這樣就不需要把核心緩衝區的內容往使用者空間拷貝。應用程式再呼叫 write(),作業系統直接將核心緩衝區的內容拷貝到 socket 緩衝區中,這一切都發生在核心態,最後,socket 緩衝區再把資料發到網絡卡去。
目前為止,我們已經減少了資料拷貝的次數,但是仍然存在一次拷貝,就是頁快取到 socket 快取的拷貝。那麼能不能把這個拷貝也省略呢?
2.3 直接傳遞檔案描述符
在上一種方案中是將頁快取的資料拷貝到 socket 快取中,實際上,我們僅僅需要把緩衝區描述符傳到 socket 緩衝區,再把資料長度傳過去,這樣 DMA 控制器直接將頁快取中的資料打包傳送到網路中就可以了。不過這種收集拷貝功能是需要硬體以及驅動程式支援的。
在服務端響應客戶端的場景中,如果使用非直接緩衝區第一步就需要將響應的資料從 JVM 記憶體拷貝到系統核心中再發送,而使用直接緩衝區就可以省略這個步驟,這就是 零拷貝 。
四、總結
傳統的 IO 讀和寫都需要在作業系統核心和使用者空間之間拷貝,Linus 優化了核心空間和使用者空間的拷貝過程,核心空間也可以通過傳遞檔案描述符進一步減少核心中的一次拷貝過程。Linux 零拷貝演進過程:
每天用心記錄一點點。內容也許不重要,但習慣很重要!