sendfile/splice實現了真正意義上的零拷貝:CPU完全不需要參與數據的拷貝,應用層Java NIO爲開發人員提供了FileChannel.transferFrom()/transferTo()方法使用零拷貝。
sendfile
傳輸原理
- 用戶進程通過 sendfile() 函數向內核(kernel)發起系統調用,上下文從用戶態(user space)切換爲內核態(kernel space);
- CPU 利用 DMA 控制器將數據從主存或硬盤拷貝到內核空間(kernel space)的讀緩衝區(read buffer);
- CPU 把讀緩衝區(read buffer)的文件描述符(file descriptor)和數據長度拷貝到網絡緩衝區(socket buffer);
- 基於已拷貝的文件描述符(file descriptor)和數據長度,CPU 利用 DMA 控制器的 gather/scatter 操作直接批量地將數據從內核的讀緩衝區(read buffer)拷貝到網卡進行數據傳輸;
- 上下文從內核態(kernel space)切換回用戶態(user space),Sendfile 系統調用執行返回;
適用場景
sendfile適用於文件數據到網卡的傳輸過程,並且用戶程序對數據沒有修改的場景;
splice
傳輸原理
- 用戶進程通過 splice() 函數向內核(kernel)發起系統調用,上下文從用戶態(user space)切換爲內核態(kernel space);
- CPU 利用 DMA 控制器將數據從主存或硬盤拷貝到內核空間(kernel space)的讀緩衝區(read buffer);
- CPU 在內核空間的讀緩衝區(read buffer)和網絡緩衝區(socket buffer)之間建立管道(pipeline);
- CPU 利用 DMA 控制器將數據從網絡緩衝區(socket buffer)拷貝到網卡進行數據傳輸;
- 上下文從內核態(kernel space)切換回用戶態(user space),Splice 系統調用執行返回;
適用場景
splice適用於任意兩個文件描述符中傳輸數據(兩個文件描述符參數中有一個必須是管道設備),並且用戶程序對數據沒有修改的場景;
Java應用
Java NIO的FileChannel.transferFrom()/transferTo()底層基於sendfile/splice,不僅可以進行網絡文件傳輸,還可以對本地文件實現零拷貝操作,示意圖如下所示:
參考: