零拷貝

首發於個人我的博客html

概念

零拷貝(zero copy)指的是當拷貝發生時,CPU並不參與實際的拷貝過程(也能夠指拷貝數據這個過程),CPU能夠切換到其餘線程,數據的拷貝過程異步進行,異步過程一般要由硬件DMA實現。採用傳統的讀寫操做將磁盤中的數據發送到網絡中,一般經歷2次用戶態/內核態的切換,而且讀和寫操做CPU分別要參與一次拷貝過程。java

DMA

DMA可讓CPU從數據拷貝中解放出來,這樣IO就能夠異步進行。CPU只需在DMA中初始化幾個參數,接着CPU就能夠幹其它事情,而IO依舊在發生中。CPU告知DMA內存地址、讀取的字節數和驅動的端口號。當DMA完成它的工做時,就會發生一箇中斷信號給CPU,這時數據就出如今指望的內存中。這樣CPU就沒必要輪詢IO的完成或參與到IO的流程(被稱爲programmed input/output)中。
DMA網絡

問題

考慮如下代碼:異步

// 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

  1. read()會進行一次系統調用且執行一次上下文切換到內核態。這個過程有DMA將磁盤中的數據複製到內核的緩衝區之中。
  2. 數據從內核緩衝區複製到用戶空間,read()調用返回,系統切回到用戶態。
  3. write()進行一次系統調用,而且切換到內核態。第三次拷貝發生了,數據再次被拷貝到內核空間buffer,這個buffer關聯一個socket。
  4. write()調用返回,而且切回到用戶態。第四次拷貝由DMA執行,它將內核緩衝區的數據拷貝到協議引擎(protocol engine)中。

這個過程進行了四次上下文切換,CPU要參與兩次拷貝過程。用戶空間的拷貝過程都是沒有必要的。spa

mmap

mmap將一個文件或設備映射到內存中。線程

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);

mmap容許程序直接在用戶態中訪問內核空間中的數據,這樣就避免了一次無心義的拷貝。
mmap
當向socket寫入數據時,數據仍是要拷貝到socket緩衝區中,再拷貝到協議引擎中。code

sendfile

#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

sendfile能夠將文件直接向socket傳遞,DMA將數據複製到內核空間後,再拷貝到socket buffer,而後DMA將數據傳遞給協議引擎。Linux2.4以後,DMA能夠直接將內核緩衝區數據直接傳輸到協議引擎,消滅最後一次拷貝。blog

sendfile

Java NIO transferTO

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);
相關文章
相關標籤/搜索