• 零拷贝


    一、问题

      传统的Linux io操作,直接与io交互的话,首先得寻址,这个寻址是个很耗时的过程。为了减少与磁盘的io,加了一个page cache层,也就是os cache。如果page cache有的话就不需要到磁盘里找了,直接返回,这个速度就很快了。如果没有再到磁盘里查询,进行读写。那么一次io的操作就是:

      

         1.从磁盘文件中拷贝到内核中的页缓存。这步是DMA copy的,发生第一次copy。

      2.从页缓存拷贝到用户空间缓存里,也就是应用程序对应的内存空间里。这步是CPU copy的,发生第二次copy,而且从内核态切换到用户态,发生了第一次切换。

      3.从用户空间缓存拷贝到socket缓存区。这步是CPU copy的,发生第三次copy,从用户态切换到内核态,又发生了一次切换。   

      4.从socket缓冲区拷贝到网络中。这步是DMA copy的,发生第四次copy。      

      可以看到一共发生了4次拷贝,其中cpu 参与了两次拷贝,而且用户态和内核态也发生了多次上下文切换,非常的耗cpu资源。

    二、定义

      为了避免拷贝对cpu的压力,我们可以通过减少不必要的拷贝来进行优化。所谓的零拷贝,并不是不发生一次拷贝,而是减少不必要的拷贝去进行优化。

    三、方法

      1.mmap

      

       应用程序调用mmap,从磁盘文件DMA copy到页缓存中,用户空间缓存与页缓存共享这部分内存,所以就不需要从页缓存拷贝到用户空间中了。然后直接通过CPU copy到Socket缓冲区中,再DMA copy到网络中。可以发现,少了一次CPU copy, 而且没有了用户态和内核态的上下文切换了。

      2.sendfile

      

       Linux从2.1版内核开始引入了sendfile,直接从页缓存拷贝到socket缓存中。

      

    #include<sys/sendfile.h>
    ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count)

      从out_fd到in_fd传输数据,但是页缓存到socket缓存还是发生了一次CPU copy的,这步不是必要的,也可以进行优化。

       sendfile通过DMA 引擎进行优化,只是将将文件描述符传输给socket缓存,数据直接从页缓存发送到网络中。

     缺点:只适用与将文件拷贝到套接字上。

     3.splice

      Linux在2.6.17版本引入splice系统调用,用于在两个文件描述符中移动数据:

    #define _GNU_SOURCE         /* See feature_test_macros(7) */
    #include <fcntl.h>
    ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);

      缺点:至少有一个文件描述符为管道。

    参考文档:https://mp.weixin.qq.com/s/7IT10kFfg8bxchaN_n-GEg

  • 相关阅读:
    guava cache 缓存
    策略模式干掉if-else,switch
    一.投资理论
    死锁问题
    Redis连接数居高不下的问题
    Entity Framework Core To SQL问题
    数据字典
    Core2.0升级2.2
    NetCoreAPI版本控制
    WEBAPI的请求方式与请求类型
  • 原文地址:https://www.cnblogs.com/ITyannic/p/12436736.html
Copyright © 2020-2023  润新知