什麼是零拷貝?
在講解零拷貝之前,我們先來了解一下爲什麼要引入零拷貝,以及零拷貝解決了什麼問題。
內核態與用戶態
操作系統爲了限制不同程序之間的訪問能力,防止他們獲取別的程序的內存數據或者外設的數據以及限制某些指令的調用,所以劃分出內核與用戶兩個權限等級。所有的用戶程序都運行在用戶態,但用戶程序有時候可能需要從磁盤讀取文件或者寫入文件,這些操作只能由內核態完成,所以這個時候就需要內核態與用戶態的切換以及數據的拷貝
假設我們的應用程序需要從磁盤中讀取文件,然後通過網絡發送出去,Linux 操作系統會基於數據排序或者校驗等各方面因素的考慮,內核會在處理數據傳輸的過程中進行多次拷貝操作。這些數據拷貝操作會降低數據傳輸的性能。
當應用程序需要訪問磁盤數據時,內核會使用DMA將數據從磁盤讀出並放到內核緩衝區,再將數據拷貝到用戶緩衝區,再將數據拷貝到socket緩衝區,再使用DMA將數據拷貝到網卡設備中。
DMA是一種無需CPU的參與就可以讓外設和系統內存之間進行雙向數據傳輸的硬件機制。使CPU從實際的I/O數據傳輸過程中擺脫出來。
從上述流程可以看出,數據經過了多次的拷貝操作,數據拷貝需要佔用CPU時間片,也經歷了多次內核與用戶態的轉換,對性能影響較大。爲了解決數據多次拷貝這個問題,Linux引入了零拷貝技術。
Linux mmap()
mmap用來代替Linux read接口來讀取數據,調用之後,數據會通過DMA拷貝到操作系統內核緩衝區,接着用戶程序與內核公用這個緩衝區,這樣用戶空間與內核空間就不需要互相拷貝數據了。
對應Java
Java對mmap的實現就是MappedByteBuffer類,使用它我們可以直接在內存中進行文件讀寫。
Linux sendfile()
sendfile接口利用DMA將數據拷貝到內核緩衝區,然後數據被拷貝到socket緩衝區,接下來DMA再將數據拷貝到網卡設備中。sendfile() 調用不需要將數據拷貝或者映射到應用程序地址空間中去,所以 sendfile() 只是適用於應用程序地址空間不需要對所訪問數據進行處理的情況。
而在帶有DMA收集拷貝的情況下:
sendfile() 系統調用利用 DMA 引擎將文件內容拷貝到內核緩衝區去;然後,將帶有文件位置和長度信息的緩衝區描述符添加到 socket 緩衝區中去,此過程不需要將數據從操作系統內核緩衝區拷貝到 socket 緩衝區中,DMA 引擎會將數據直接從內核緩衝區拷貝到網卡設備中。
通過這種方法,CPU 在數據傳輸的過程中不但避免了數據拷貝操作
對應Java
Java就sendfile的實現就是FileChannel的transferTo()和transferFrom(),將數據從一個channel傳輸到另一個channel
參考:
https://www.ibm.com/developerworks/cn/linux/l-cn-zerocopy1/index.html
https://www.ibm.com/developerworks/cn/linux/l-cn-zerocopy2/index.html