零拷贝
定义:指从内核空间到用户空间的拷贝次数为零
问题
计算机中,应用程序通常会遇到将磁盘数据写入网络的情况,会造成用户态和内核态 的频繁切换,具体过程如下:
- 应用程序将要发送某个磁盘上的文件,发起系统调用(1次 用户态到内核态切换)
- 内核态中将磁盘数据放入内核 buff (DMA (Direct Memeory Access))
- 再将内核 buff 放入应用程序 buff(2次 内核态到用户态) (CPU COPY)
- 程序请求发送网络,将应用程序 buff中的数据写入 socket buff(3次 用户态到内核态)(CPU COPY)
- 内核将socket buff 写入 nic(Network interafce controller) buff (DMA)
- 返回应用程序继续执行(4次 用户态到内核态)
从上看出,整个过程出现两次中断,四次上下文切换,很明显,第二三四五步骤可以合并的话就可以减少很多不必要开的开销
sendFile
从内核 2.1 开始,引入该方法,可以实现以下过程:
- 应用程序将要发送某个磁盘上的文件,发起系统调用(1次 用户态到内核态切换)
- 内核态将磁盘数据放入内核 buff (DMA)
- 将内核 buff 中的数据放入 socket buff (CPU COPY)
- 将soket buff 数据拷贝到 nic buff(DMA)
- 返回应用程序
从上可以看出,过程简化为一次中断,3次拷贝,两次上下文切换
scatter-gather sendFile
2.4 后的内核有了这个,可以只复制kernal buff少量元信息的基础上,直接将 kernal buff 复制到 nic buff 中,过程就变成了:
- 应用程序将要发送某个磁盘上的文件,发起系统调用(1次 用户态到内核态切换)
- 内核态将磁盘数据放入内核 buff (DMA)
- 将内核 buff 数据在内核 buff 中的位置(offset)和数据大小(size)追加到socket buff
- nic buff 通过 socket buff 中的offset 和 size 直接从内核 buff 中拷贝(DMA)
- 返回应用程序
两次DMA COPY,很快
其他相关
MMAP
内存映射文件,将文件映射到进程的地址空间,实现物理地址和进程空间地址的对应
进程可以向访问内存一样访问这个文件,而不需要通过read, write
当 kernal buff 和 app buff 共用这个文件时,可以实现零拷贝
优势
- 多进程访问时节约内存空间
- 数据在内核中,可以直接发送到网络,而应用程序无法修改数据(保护数据)
劣势
- 当映射文件写入,用时被另一个进程切断,可能被总线错误信号SIGBUS中断,而这个信号意味着KILL PROCESS或者DUMP CORE,对服务来说不可接受
- 访问多个小文件时,不如 send file 高效