什么是零拷贝(sendfile,mmap)

一、普通的文件拷贝 

         零拷贝的使用场景是从某台机器将一份数据(比如一个文件)通过网络传输到另外一台机器。

        现在设想一下,我们要把设备A 上的一个文件通过socket发送到设备B,期间涉及到调用一次read()函数和write()函数。

        当我们调用read()函数时:

  1. 系统调用read导致了从用户空间到内核空间的上下文切换。接着,切换到内核态后,DMA(直接存储访问器)模块从磁盘中读取文件内容,并将其存储在内核空间的缓冲区内,完成了第1次复制。
  2. 数据从内核空间缓冲区复制到用户空间缓冲区(这次的拷贝是通过CPU进行的),之后系统调用read返回,这导致了从内核空间向用户空间的上下文切换。此时,需要的数据已存放在指定的用户空间缓冲区内(参数tmp_buf)。

        当我们调用write()函数时:

  1. 系统调用write导致从用户空间到内核空间的上下文切换。数据从用户空间缓冲区被再次复制到内核空间中的socket缓冲区中(这次的拷贝是通过CPU进行的)。
  2. 系统调用write返回,导致了从内核空间向用户空间的上下文切换。并且此时DMA模块将数据从内核中的socket缓冲区传递至协议引擎。

        可以看到,这一过程总共发生了4次上下文切换,以及2次涉及到CPU的数据拷贝(这意味了在拷贝数据的期间,CPU无法处理其他数据,十分的浪费CPU资源)。

二、零拷贝的实现

        为了实现零拷贝,我们要从两方面来切入,即减少上下文切换的次数和避免使用CPU来进行数据的拷贝。

(1)mmap

        mmap的核心思想就是将内核缓存区、用户空间缓存区映射到同一个物理地址上,可以减少用户缓存区与内核缓存区之间的数据拷贝。

这样一来就变成了:

  1. 文件的内容通过DMA模块被复制到内核缓冲区中,该缓冲区之后与用户进程共享,这样就内核缓冲区与用户缓冲区之间的复制就不会发生。
  2. write系统调用导致内核将数据从内核缓冲区复制到与socket相关联的内核缓冲区中。
  3. DMA模块将数据由socket的缓冲区传递给协议引擎时。

(2)sendfile

        sendfile系统调用导致文件内容通过DMA模块被复制到内核缓冲区中。

        此时数据并未被复制到socket关联的缓冲区内。取而代之的是,只有记录数据位置和长度的描述符被加入到socket缓冲区中。DMA模块将数据直接从内核缓冲区传递给协议引擎,从而消除了遗留的最后一次复制。

        此时全部流程变为了,调用sendfile:

  1. 由用户态切换到内核态。
  2. 系统将记录数据位置和长度的描述符写入到socket缓冲区中。
  3. 由DMA模块将数据直接从内核缓冲区传递给协议引擎。
  4. sendfile返回,由内核态切换到用户态。

        这时我们发现,整个过程只涉及了两次上下文的切换,并且整个数据的拷贝过程都没有涉及到CPU,即这就是零拷贝。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值