什麼是零拷貝?
零拷貝是指計算機操作的過程中,CPU不需要爲數據在內存之間的拷貝消耗資源。而它通常是指計算機在網絡上發送文件時,不需要將文件內容拷貝到用戶空間(User Space)而直接在內核空間(Kernel Space)中傳輸到網絡的方式
傳統的IO
操作系統將數據從磁盤讀到內核空間的緩存
應用程序將數據從內核空間讀取到應用空間
應用程序接着將數據寫回到內核空間socket緩衝區
操作系統將數據從socket緩衝區複製到網卡緩衝區,最後數據經網絡傳輸
零拷貝
操作系統將數據從磁盤讀到內核空間的緩存
將數據的位置、長度信息描述符添加到內空間socket緩衝區
操作系統將數據從內核空間拷貝到網卡緩衝區,最後數據經網絡傳輸
零拷貝的底層實現
使用mmap()替代read(),減少一次拷貝
在Linux中有一個mmap操作,可以減少拷貝次數,我們將read操作換成mmap操作
換成mmap操作後,仍然需要3次copy動作。
第一次,DMA將數據從磁盤中拷貝(DMA copy)到kernel read buffer內核緩衝區中;
第二次,當應用調用write方法時,會將kernel read buffer中的數據拷貝(CPU copy)到kernel socket buffer內核緩衝區;
第三次,將kernel socket buffer中的數據拷貝到protocol engine(協議引擎)。
使用sendfile()減少一次上下文切換
藉助DMA引擎的gather操作減少一次內核拷貝
gather操作可以從多個不同的緩衝區中讀取數據,DMA引擎是可以直接從kernel read buffer中讀取數據的,而不用先將數據從kernel read buffer拷貝到kernel socket buffer後再進行讀取操作。
到此,數據拷貝已經完全跟CPU沒關係了,都是由內核操作的,內核只需要兩次拷貝動作。
Java對零拷貝的封裝實現
舉個簡單的例子,將某apk文件傳輸到某目錄下
String filePath="/usr/local/file/test.apk";
String toFilePath="/usr/local/target/test.apk";
try (
FileInputStream fileInputStream=new FileInputStream(new File(filePath));
FileOutputStream fileOutputStream=new FileOutputStream(toFilePath);
FileChannel fileChanneInput=fileInputStream.getChannel();
FileChannel fileChannelOutput=fileOutputStream.getChannel()
){
fileChanneInput.transferTo(0,fileChanneInput.size(),fileChannelOutput);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}