零拷贝

首发于个人我的博客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);
相关文章
相关标签/搜索