什么是零拷贝?一招学会高效传输文件

差一点

我们就擦肩而过了

有趣

有用

有态度

导学问题:

1.服务器通过网络传输数据时,系统有几次拷贝?
2.大文件和小文件各有什么传输特点?

什么是零拷贝

零拷贝(Zero-copy)技术指在计算机执行操作时,CPU 不需要先将数据从一个内存区域复制到另一个内存区域,从而可以减少上下文切换以及 CPU 的拷贝时间。

从一个案例说起

某天你接到公司下发的一个安排,完成一个类似ftp文件传输的功能,要求传输效率高,cpu占用低。典型的要马儿跑得快又不给马吃草。

第一版实现:

最直接的方法:从网络请求中找出文件在磁盘中的路径后,如果这个文件比较大,假设有 100MB,可以在内存中分配 10KB 的缓冲区,再把文件分成一万份,每份只有 10KB,这样,从文件的起始位置读入10KB 到缓冲区,再通过网络 API 把这 10KB 发送到客户端。接着重复一万次,直到把完整的文件都发送完毕。

示意图如下:

现在我们可以看到1->2->3->4的整个过程一共经历了四次拷贝的方式,**但是真正消耗资源和浪费时间的是第二次和第三次,因为这两次都需要经过我们的CPU拷贝,而且还需要内核态和用户态之间的来回切换。**上下文切换的成本并不小,虽然一次切换仅消耗几十纳秒到几微秒,但高并发服务会放大这类时间的消耗。

这个方案做了 1 万次内存拷贝,对 100MB 文件拷贝的字节数也翻了 4 倍,到了1000MB。CPU资源是多么宝贵,要处理大量的任务,还要去拷贝大量的数据。无疑,过多的内存拷贝无谓地消耗了 CPU 资源。我们要想办法降低上下文切换频率和内存拷贝次数。

第二版实现:

引入mmap技术,mmap可以实现用户空间和内核空间数据共享,从而避免减少一次内核空间到用户空间数据拷贝动作,在数据量很大的时候,效率提升尤其明显。

示意图如下:

注意:这里的零拷贝其实是根据内核状态划分的,在这里没有经过CPU的拷贝,数据在用户态的状态下,经历了零次拷贝,所以才叫做零拷贝,但不是说不拷贝。所以现在内核依然依然会有一次从缓存(pagecache)拷贝到socket缓冲区的过程。这里我们能不能想办法优化呢?

第三版实现:

借助DMA(Direct Memory Access)直接内存访问技术,可以帮助我们再减少一次cpu拷贝过程,它可以让数据直接从缓存传输给网卡,整个拷贝过程减小到了两次。

示意图如下:

这已经比之前大大提高了我们的传输效率,最后我们再来看pagecache能不能再优化一下。首先磁盘比内存的速度慢许多,所以先把磁盘文件读到内存,就能用读内存替换读磁盘。而且在读取过程中,会预读一部分的磁盘文件以方便下次读取。但是在读取大文件时,预读就失效了,因为大文件会占据全部的pagecache。也就是说这一次拷贝几乎是徒劳的,并没有获得明显性能的提高。所以,高并发场景下,为了防止 PageCache 被大文件占满后不再对小文件产生作用,大文件不应使用 PageCache,进而也不应使用零拷贝技术处理。

第四版实现:

在面临高并发、大文件的数据传输时,我们需要绕过 PageCache。Linux系统下支持直接IO,所谓直接IO就是指点对点直接传输,没有中间商赚差价,性能效率极高。通过直接IO往往会搭配异步IO来应对高并发。

示意图如下:

当然,直接 IO 也有一定的缺点。除了缓存外,内核(IO 调度算法)会试图缓存尽量多的 连续 IO 在 PageCache 中,最后合并成一个更大的 IO 再发给磁盘,这样可以减少磁盘的 寻址操作;另外,内核也会预读后续的 IO 放在 PageCache 中,减少磁盘操作。直接 IO 绕过了 PageCache,所以无法享受这些性能提升。

有了直接 IO 后,异步 IO 就可以无阻塞地读取文件了。现在,大文件由异步 IO 和直接 IO 处理,小文件则交由零拷贝处理,至于判断文件大小的阈值可以灵活配置。

OK,先到这里。

分享一款Linux平台下的tcp协议栈!超级透彻!

TCP本地套接字

ARP协议是什么鬼?这一篇源码分析!

我想靠自己加载个动态库

觉得不错,请点个在看

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值