Netty 系列目錄 (http://www.javashuo.com/article/p-hskusway-em.html)html
本文探討 Linux 中主要的幾種零拷貝技術以及零拷貝技術適用的場景。linux
操做系統的核心是內核,獨立於普通的應用程序,能夠訪問受保護的內存空間,也有訪問底層硬件設備的全部權限。爲了保證用戶進程不能直接操做內核 (kernel),保證內核的安全,操做系統將虛擬空間劃分爲兩部分,一部分爲內核空間,一部分爲用戶空間。緩存
網絡 IO 的本質是 socket 的讀取,socket 在 linux 系統被抽象爲流,IO 能夠理解爲對流的操做。剛纔說了,對於一次 IO 訪問 (以 read 舉例),數據會先被拷貝到操做系統內核的緩衝區中,而後纔會從操做系統內核的緩衝區拷貝到應用程序的地址空間。因此說,當一個 read 操做發生時,它會經歷兩個階段:安全
本文關注的是第二個過程:如何減小拷貝,即零拷貝。網絡
Linux 的 I/O 操做默認是緩衝 I/O。使用了 read 和 write 兩個系統調用,咱們並不知道操做系統在其中作了什麼。實際上在以上 I/O 操做中,發生了屢次的數據拷貝。socket
當應用程序訪問某塊數據時,操做系統首先會檢查,是否是最近訪問過此文件,文件內容是否緩存在內核緩衝區,若是是,操做系統則直接根據 read 系統調用提供的 buf 地址,將內核緩衝區的內容拷貝到 buf 所指定的用戶空間緩衝區中去。若是不是,操做系統則首先將磁盤上的數據拷貝的內核緩衝區,這一步目前主要依靠 DMA 來傳輸,而後再把內核緩衝區上的內容拷貝到用戶緩衝區中。
接下來,write 系統調用再把用戶緩衝區的內容拷貝到網絡堆棧相關的內核緩衝區中,最後 socket 再把內核緩衝區的內容發送到網卡上。優化
整個過程當中共產生了屢次數據拷貝,即便用了 DMA 來處理了與硬件的通信,用戶空間和系統空間 CPU 仍然須要處理兩次數據拷貝,與此同時,在用戶態與內核態也發生了 4 次上下文切換,無疑也加劇了 CPU 負擔。spa
在此過程當中,咱們沒有對文件內容作任何修改,那麼在內核空間和用戶空間來回拷貝數據無疑就是一種浪費,而零拷貝主要就是爲了解決這種低效性。操作系統
零拷貝主要的任務就是避免 CPU 將數據從一塊存儲拷貝到另一塊存儲,主要就是利用各類零拷貝技術,避免讓 CPU 作大量的數據拷貝任務,減小沒必要要的拷貝,或者讓別的組件來作這一類簡單的數據傳輸任務,讓 CPU 解脫出來專一於別的任務。這樣就可讓系統資源的利用更加有效。3d
在 OS 層面上的 Zero-copy 一般指避免在用戶態(User-space)與內核態(Kernel-space)之間來回拷貝數據。
但 Netty 中的 Zero-copy 與 OS 的 Zero-copy 不太同樣,Netty 的 Zero-coyp 徹底是在用戶態(Java 層面)的,它的 Zero-copy 的更多的是偏向於優化數據操做 。
如何減小數據拷貝的次數呢?一個很明顯的着力點就是減小數據在內核空間和用戶空間來回拷貝,這也引入了零拷貝的一個類型:減小用戶空間到內核空間的拷貝。
應用程序調用 sendfile,磁盤上的數據會經過 DMA 被拷貝的內核緩衝區,接着操做系統會把這段內核緩衝區與應用程序共享,這樣就不須要把內核緩衝區的內容往用戶空間拷貝。應用程序再調用 write(),操做系統直接將內核緩衝區的內容拷貝到 socket 緩衝區中,這一切都發生在內核態,最後,socket 緩衝區再把數據發到網卡去。
目前爲止,咱們已經減小了數據拷貝的次數,可是仍然存在一次拷貝,就是頁緩存到 socket 緩存的拷貝。那麼能不能把這個拷貝也省略呢?
在上一種方案中是將頁緩存的數據拷貝到 socket 緩存中,實際上,咱們僅僅須要把緩衝區描述符傳到 socket 緩衝區,再把數據長度傳過去,這樣 DMA 控制器直接將頁緩存中的數據打包發送到網絡中就能夠了。不過這種收集拷貝功能是須要硬件以及驅動程序支持的。
在服務端響應客戶端的場景中,若是使用非直接緩衝區第一步就須要將響應的數據從 JVM 內存拷貝到系統內核中再發送,而使用直接緩衝區就能夠省略這個步驟,這就是零拷貝。
傳統的 IO 讀和寫都須要在操做系統內核和用戶空間之間拷貝,Linus 優化了內核空間和用戶空間的拷貝過程,內核空間也能夠經過傳遞文件描述符進一步減小內核中的一次拷貝過程。Linux 零拷貝演進過程:
天天用心記錄一點點。內容也許不重要,但習慣很重要!