一次讲清 Linux 零拷贝:从一次文件传输说起

举报
XIANG.FAN 发表于 2026/11/11 23:24:02 2026/11/11
【摘要】 传统文件传输要经历四次拷贝和四次上下文切换,本文从一次文件传输说起,讲清零拷贝的含义,对比 mmap 加 write 与 sendfile 两种做法,并说明各自的适用场景。

一、从一次文件传输说起

服务器要把磁盘上的一个文件发给客户端,这件事几乎每天都在发生:文件下载、视频点播、静态资源服务,背后都是它。看起来只是读出来再发出去,但如果逐字节跟踪数据走过的路,会发现它并不便宜。

传统做法是应用程序先调用 read 把文件读进自己的内存缓冲区,再调用 write 把缓冲区里的数据发到网卡。两行逻辑,四个步骤,但数据在内核和用户空间之间来回搬运,每一步都要付出拷贝和切换的代价。

二、传统方式:四次拷贝,四次上下文切换

一次最朴素的文件传输,数据要经历四次拷贝:

  1. 磁盘把数据拷进内核的页缓存,这是第一次拷贝,由直接内存访问完成,特点是快但不经过处理器
  2. 处理器把页缓存里的数据拷进应用程序的缓冲区,第二次拷贝
  3. 应用程序调用 write 时,处理器再把数据从应用缓冲区拷进内核的套接字缓冲区,第三次拷贝
  4. 网卡通过直接内存访问把套接字缓冲区的数据取走,发到网络上,第四次拷贝

与此同时,进程还要在用户态和内核态之间切换四次:调用 read 进入内核、read 返回用户态、调用 write 进入内核、write 返回用户态。拷贝占用处理器,切换打断流水线,文件越大,这份开销越明显,传输速度的瓶颈往往不在网卡,而在这些看不见的搬运上。

三、什么是零拷贝

零拷贝的目标不是让拷贝次数真的变成零。磁盘到网卡这段路,数据至少还要经过内核,网卡也总要取走数据,真正省掉的是那些没有必要的中间环节:绕过用户空间,避免数据在用户态和内核态之间来回倒手。

更准确地说,零拷贝是一组技术的统称,核心只有两条:让数据尽量留在内核里流动,减少处理器的参与;用直接内存访问承担搬运,让处理器腾出来做真正需要算力的事。理解这两条,就能理解后面所有具体技术。

四、两种常见做法

做法一:mmap 加 write

mmap 把文件映射到进程的地址空间,应用程序像访问内存一样访问文件内容。不需要先把整块数据拷进用户缓冲区,内核用缺页中断按需把页缓存的内容映射过来,读取时直接操作页缓存,随后调用 write 把数据发出去。

它的优点是程序可以直接在映射的内存里修改数据,适合要对文件内容做加工的场景;缺点是写出时数据仍然要经过一次处理器拷贝进入套接字缓冲区,加上一次映射建立和缺页处理的开销,纯传输并不能省到极致。

做法二:sendfile

sendfile 是专门为文件传输设计的系统调用,一次调用完成从文件到套接字的传输。应用程序只负责发起,数据全程留在内核里:磁盘经直接内存访问进入页缓存,再经直接内存访问进入套接字缓冲区,最后由网卡取走。处理器的参与只剩下两次状态切换,数据拷贝的次数下降到两次,而且都是直接内存访问完成的。

静静地对比一下就能看出差别:传统方式四次拷贝、四次切换;mmap 加 write 是三次拷贝、四次切换;sendfile 是两次拷贝、两次切换。当文件很大、传输成为日常操作时,这份差距会直接体现在吞吐量和处理器占用率上。

五、用在哪里,用的时候注意什么

零拷贝不是万能钥匙,它有自己的适用范围:

  • 静态文件服务是最典型的场景,文件不改动,原样发给用户,用 sendfile 是最直接的选择
  • 大文件传输的收益更明显,小文件传输里固定的调用与映射开销占比更高,省出的拷贝未必抵得过额外开销
  • 需要对内容做加工时,sendfile 帮不上忙,因为数据不经过用户空间,应该选 mmap 映射后再处理
  • 记得零拷贝省去的是处理器拷贝,不是全部拷贝,磁盘和网卡的工作依然在那里,因此瓶颈转移到磁盘读取速度或网络带宽上是正常现象

最后要提醒一点,看到零拷贝不要产生错觉,好像数据凭空从磁盘飞到了网卡。它的本质是把该做的工作交给更合适的角色:处理器负责指挥,直接内存访问负责搬运。理解了这一点,面对各种新的传输优化手段,思路都是一致的:先数一数数据被拷了几次,再想一想每一次拷贝是不是真的必要。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。