零拷貝技術是指在計算機操做過程當中,CPU避免因爲數據在內存間拷貝而浪費資源的技術。特別是在將文件在網絡中輸出的場景下,經過零拷貝技術能夠節省不少的計算資源和內存資源。java
下圖描述了傳統的IO操做的流程。linux
![]() |
non-zerocopy |
不使用零拷貝技術,當文件輸出到網絡時,首先要將數據從內核緩衝區拷貝到程序緩衝區,而後再由程序將緩衝區拷貝的數據輸出到內核緩衝區的網絡輸出的socketBuffer中;咱們能夠看出拷貝到程序緩衝區這步其實是沒有必要的。網絡
引入了零拷貝,流程見下圖:socket
數據將不會拷貝到程序緩衝區,而是由程序調用系統的零拷貝命令,數據將會從輸入流直接被寫入到輸出流中,避免了一次無用的拷貝過程;性能
在linux中,能夠經過mmap(), sendfile(), splice()實現零拷貝。spa
經過使用mmap接口替代read能夠達到減小拷貝次數的目的。操作系統
tmp_buf = mmap(file, len); write(socket, tmp_buf, len);
使用mmap後,數據會經過DMA拷貝到操做系統內核緩衝區中,接着應用程序和操做系統共享這個緩衝區的數據,所以數據不須要再拷貝到應用程序緩衝區了。調用write後,操做系統將數據從內核緩衝區拷貝到與socket相關的內核緩衝區中,最後再拷貝到協議引擎中,一共三次數據拷貝;
使用 mmap 是 POSIX 兼容的,可是使用 mmap 並不必定能得到理想的數據傳輸性能。數據傳輸的過程當中仍然須要一次 CPU 拷貝操做,並且映射操做也是一個開銷很大的虛擬存儲操做,這種操做須要經過更改頁表以及沖刷 TLB (使得 TLB 的內容無效)來維持存儲的一致性。可是,由於映射一般適用於較大範圍,因此對於相同長度的數據來講,映射所帶來的開銷遠遠低於 CPU 拷貝所帶來的開銷。htm
Linux2.1引入sendfile技術,與mmap的區別主要在它不須要維持內核緩衝區數據到程序緩衝區的映射操做,所以它極大的減小了對存儲的開銷。可是它仍然有一次在操做系統內核緩衝區的數據拷貝過程,將數據拷貝到socket相關的緩衝區中。blog
sendfile() 系統調用利用 DMA 引擎將文件內容拷貝到內核緩衝區去;而後,將帶有文件位置和長度信息的緩衝區描述符添加到 socket 緩衝區中去,此過程不須要將數據從操做系統內核緩衝區拷貝到 socket 緩衝區中,DMA 引擎會將數據直接從內核緩衝區拷貝到協議引擎中去,這樣就避免了最後一次數據拷貝接口
Linux 2.6.17 內核引入了 splice() 系統調用,它和sendfile很是相似,可是它不須要指定輸出一端是socket,任何的系統文件輸出均可以。從這一點上講,sendfile其實是splice的一個子集。
Java 類庫經過 java.nio.channels.FileChannel 中的 transferTo() 方法來在 Linux 和 UNIX 系統上支持零拷貝。可使用 transferTo() 方法直接將字節從它被調用的通道上傳輸到另一個可寫字節通道上,數據無需流經應用程序。
下面是一段將文件輸出到httpServletResponse的代碼:
//輸出流 servletOutputStream = response.getOutputStream(); FileChannel channel = new FileInputStream(imgPath).getChannel(); response.setHeader("Content-Length", String.valueOf(channel == null ? 0 : channel.size())); channel.transferTo(0, channel.size(), Channels.newChannel(servletOutputStream));
經過零拷貝技術能夠提升數據輸出的時間延遲,下面是使用傳統的IO輸出和零拷貝輸出的時間對比:
文件大小 | 正常文件傳輸(ms) | transferTo(ms) |
---|---|---|
7MB | 156 | 45 |
21MB | 337 | 128 |
63MB | 843 | 387 |
98MB | 1320 | 617 |
200MB | 2124 | 1150 |
350MB | 3631 | 1762 |
700MB | 13498 | 4422 |
1GB | 18399 | 8537 |