引言
最常见的发送文件模型如下图所示:
这个模型涉及以下内存拷贝:
磁盘 -> PageCache
PageCache -> 用户缓冲区
用户缓冲区 -> Socket 缓冲区
Socket 缓冲区 -> 网卡
因为涉及到多次内存拷贝,消耗过多的 CPU 资源,降低系统并发处理能力。
零拷贝技术
从技术实现上来讲,零拷贝不是真的一次拷贝都没有,而是取消了用户缓冲区的拷贝。但是内核态还是需要将磁盘文件内容拷贝到pageCache,然后再将内容从pageCache拷贝到socket缓冲区。如果网卡支持 SG-DMA(The Scatter-Gather Direct Memory Access)技术,还可以再去除 Socket 缓冲区的拷贝。
但是在高并发场景处理大文件时,应当使用异步 IO 和直接 IO 来替换零拷贝技术。
因为PageChache 不适应传输大文件的场景,大文件容易把 PageCache 占满,而且由于文件太大,文件中某一个部分被再次访问的概率低。这样会导致大文件在 PageCache 中没有享受到缓存的优势,同时也因为 PageCache 被大文件占据,影响其他热点小文件的缓存。异步 IO 可以把读操作分为两部分,前半部分向内核发起读请求,但不用等待数据就位就返回,然后可以继续处理其他任务。当内核把磁盘中的数据拷贝到进程缓冲区后,会通知进程去处理数据。异步 IO 是不会阻塞用户进程的
对于磁盘,异步 IO 只支持直接 IO。直接 IO 是应用程序绕过 PageCache,即不经过内核缓冲区,直接访问磁盘中的数据,从而减少了内核缓存与用户程序之间的数据拷贝。
总结:
大文件交给异步 IO 和直接 IO 处理,小文件交给零拷贝处理。
(文中图片来自于极客时间)