zoukankan      html  css  js  c++  java
  • 零拷贝sendfile解析

    传统方式read/write send/recv

    在传统的文件传输里面(read/write方式),在实现上事实上是比較复杂的,须要经过多次上下文的切换。我们看一下例如以下两行代码:   

    1. read(file, tmp_buf, len);       

    2. write(socket, tmp_buf, len);  

     以上两行代码是传统的read/write方式进行文件到socket的传输。

    当须要对一个文件进行传输的时候,其详细流程细节例如以下:

    1、调用read函数,文件数据被copy到内核缓冲区

    2、read函数返回。文件数据从内核缓冲区copy到用户缓冲区

    3、write函数调用。将文件数据从用户缓冲区copy到内核与socket相关的缓冲区。

    4、数据从socket缓冲区copy到相关协议引擎。

    以上细节是传统read/write方式进行网络文件传输的方式,我们能够看到,在这个过程其中。文件数据实际上是经过了四次copy操作:

    硬盘—>内核buf—>用户buf—>socket相关缓冲区(内核)—>协议引擎

    新方式sendfile

    而sendfile系统调用则提供了一种降低以上多次copy。提升文件传输性能的方法。

    Sendfile系统调用是在2.1版本号内核时引进的:

    1. sendfile(socket, file, len);   

    执行流程例如以下:

    1、sendfile系统调用,文件数据被copy至内核缓冲区

    2、再从内核缓冲区copy至内核中socket相关的缓冲区

    3、最后再socket相关的缓冲区copy到协议引擎

    相较传统read/write方式,2.1版本号内核引进的sendfile已经降低了内核缓冲区到user缓冲区。再由user缓冲区到socket相关 缓冲区的文件copy,而在内核版本号2.4之后,文件描写叙述符结果被改变,sendfile实现了更简单的方式,系统调用方式仍然一样,细节与2.1版本号的 不同之处在于,当文件数据被拷贝到内核缓冲区时,不再将全部数据copy到socket相关的缓冲区,而是只将记录数据位置和长度相关的数据保存到 socket相关的缓存,而实际数据将由DMA模块直接发送到协议引擎,再次降低了一次copy操作。

    一、典型IO调用的问题
    一个典型的web服务器传送静态文件(如CSS,JS,图片等)的过程如下:

    read(file, tmp_buf, len);
    write(socket, tmp_buf, len);


    首先调用read将文件从磁盘读取到tmp_buf,然后调用write将tmp_buf写入到socket,在这过程中会出现四次数据copy,过程如图1所示

                    图1

    1。当调用read系统调用时,通过DMA(Direct Memory Access)将数据copy到内核模式
    2。然后由CPU控制将内核模式数据copy到用户模式下的 buffer中
    3。read调用完成后,write调用首先将用户模式下 buffer中的数据copy到内核模式下的socket buffer中
    4。最后通过DMA copy将内核模式下的socket buffer中的数据copy到网卡设备中传送。

    从上面的过程可以看出,数据白白从内核模式到用户模式走了一 圈,浪费了两次copy,而这两次copy都是CPU copy,即占用CPU资源。

    二、Zero-Copy&Sendfile()
    Linux 2.1版本内核引入了sendfile函数,用于将文件通过socket传送。
    sendfile(socket, file, len);
    该函数通过一次系统调用完成了文件的传送,减少了原来 read/write方式的模式切换。此外更是减少了数据的copy,sendfile的详细过程图2所示:

                    图2

    通过sendfile传送文件只需要一次系统调用,当调用 sendfile时:
    1。首先通过DMA copy将数据从磁盘读取到kernel buffer中
    2。然后通过CPU copy将数据从kernel buffer copy到sokcet buffer中
    3。最终通过DMA copy将socket buffer中数据copy到网卡buffer中发送
    sendfile与read/write方式相比,少了 一次模式切换一次CPU copy。但是从上述过程中也可以发现从kernel buffer中将数据copy到socket buffer是没必要的。

    为此,Linux2.4内核对sendfile做了改进,如图3所示

                    图3

    改进后的处理过程如下:
    1。DMA copy将磁盘数据copy到kernel buffer中
    2。向socket buffer中追加当前要发送的数据在kernel buffer中的位置和偏移量
    3。DMA gather copy根据socket buffer中的位置和偏移量直接将kernel buffer中的数据copy到网卡上。
    经过上述过程,数据只经过了2次copy就从磁盘传送出去了。
    (可能有人要纠结“不是说Zero-Copy么?怎么还有两次copy啊”,事实上这个Zero copy是针对内核来讲的,数据在内核模式下是Zero-copy的。话说回来,文件本身在瓷盘上要真是完全Zero-copy就能传送,那才见鬼了 呢)。
    当前许多高性能http server都引入了sendfile机制,如nginx,lighttpd等。

    三、Java NIO中的transferTo()
    Java NIO中
    FileChannel.transferTo(long position, long count, WriteableByteChannel target)
    方法将当前通道中的数据传送到目标通道target中,在支持Zero-Copy的linux系统中,transferTo()的实现依赖于sendfile()调用。

    四、参考文档
    Zero Copy I: User-Mode Perspective》http://www.linuxjournal.com/article/6345?page=0,0
    Efficient data transfer through zero copy》http://www.ibm.com/developerworks/linux/library/j-zerocopy
    The C10K problem》http://www.kegel.com/c10k.html

  • 相关阅读:
    iSCSI又称为IPSAN
    文档类型定义DTD
    HDU 2971 Tower
    HDU 1588 Gauss Fibonacci
    URAL 1005 Stone Pile
    URAL 1003 Parity
    URAL 1002 Phone Numbers
    URAL 1007 Code Words
    HDU 3306 Another kind of Fibonacci
    FZU 1683 纪念SlingShot
  • 原文地址:https://www.cnblogs.com/lpfuture/p/7678143.html
Copyright © 2011-2022 走看看