首發於個人我的博客html
零拷貝(zero copy)指的是當拷貝發生時,CPU並不參與實際的拷貝過程(也能夠指拷貝數據這個過程),CPU能夠切換到其餘線程,數據的拷貝過程異步進行,異步過程一般要由硬件DMA實現。採用傳統的讀寫操做將磁盤中的數據發送到網絡中,一般經歷2次用戶態/內核態的切換,而且讀和寫操做CPU分別要參與一次拷貝過程。java
DMA可讓CPU從數據拷貝中解放出來,這樣IO就能夠異步進行。CPU只需在DMA中初始化幾個參數,接着CPU就能夠幹其它事情,而IO依舊在發生中。CPU告知DMA內存地址、讀取的字節數和驅動的端口號。當DMA完成它的工做時,就會發生一箇中斷信號給CPU,這時數據就出如今指望的內存中。這樣CPU就沒必要輪詢IO的完成或參與到IO的流程(被稱爲programmed input/output)中。
網絡
考慮如下代碼:異步
// read a file to tmp_buf buffer read(file, tmp_buf, len); // write tem_buf's data to socket write(socket, tmp_buf, len);
這段代碼會進行如下操做:socket
這個過程進行了四次上下文切換,CPU要參與兩次拷貝過程。用戶空間的拷貝過程都是沒有必要的。spa
mmap將一個文件或設備映射到內存中。線程
#include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
mmap容許程序直接在用戶態中訪問內核空間中的數據,這樣就避免了一次無心義的拷貝。
當向socket寫入數據時,數據仍是要拷貝到socket緩衝區中,再拷貝到協議引擎中。code
#include <sys/sendfile.h> ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
sendfile()能夠在兩個文件描述符之間(fd)拷貝數據,它在內核態完成因此比read/write組合更加高效。in_fd是一個能夠被mmap映射的文件。在Linux2.6.33以前,out_fd必須是個socket。sendfile返回傳輸的字節數。htm
sendfile能夠將文件直接向socket傳遞,DMA將數據複製到內核空間後,再拷貝到socket buffer,而後DMA將數據傳遞給協議引擎。Linux2.4以後,DMA能夠直接將內核緩衝區數據直接傳輸到協議引擎,消滅最後一次拷貝。blog
Java中java.nio.FileChannel提供了一個transferTo方法,在Unix/Linux中會被傳遞到sendfile()。ByteBuffer.allocateDirect()能夠在JVM堆外分配,這樣就不受GC的影響,也能夠再也不JVM與OS之間複製。FileChannel與SocketChannel都是WritableChannel,因此能夠做爲target傳入。
public void transferTo(long position, long count, WritableByteChannel target);