OS 零拷贝
无 DMA 的I/O 过程 :
- CPU 发出对应的指令给磁盘控制器,然后返回
- 磁盘控制器收到指令后,就将数据放到磁盘控制器的缓冲区中,并产生中断
- CPU 收到中断,就将磁盘控制器的缓冲区的数据一次一个字节地读进寄存器,再将寄存器里的数据写到内存
缺点 : 数据传输 , 要 CPU 全程接入 , 浪费 CPU 资源
DMA (直接内存访问 (Direct Memory Access) ) :
- I/O 与内存交换数据时 , 不用 CPU 搬运 , 通过 DMA 传输
传统 I/O : 要进行 4 次用户/内核态的上下文切换 , 4 次数据拷贝
零拷贝
零拷贝 : 能减少上下文切换数 , 数据拷贝数
零拷贝技术实现方式 :
- mmap + write
- sendfile
mmap+ write
- read() : 把内核缓冲区的数据拷到用户的缓冲区
- mmap() : 把内核缓冲区的数据映射到用户空间, 内核与用户空间就不用数据拷贝
mmap 过程 :
- 调用 mmap() , DMA 将磁盘数据拷到内核缓冲区
- 应用进程共享内核缓冲区
- 调用 write(), 将内核缓冲区数据拷到 socket 缓冲区 (内核态进行,由 CPU 搬运)
- DMA 将 socket 缓冲区拷到网卡缓冲区
总结 :
- mmap() 代替 read() , 能减少一次数据拷贝过程
- CPU 还是要将内核缓冲区数据拷到 socket 缓冲区
- 要 4 次上下文切换
sendfile
sendfile() : 直接将内核缓冲区数据拷到 socket 缓冲区里,不用用户态
- 只有 2 次上下文切换, 3 次数据拷贝
真零拷贝
网卡支持 SG-DMA (The Scatter-Gather Direct Memory Access)
- 不用将内核缓冲区数据拷到 socket 缓冲区
ethtool -k eth0 | grep scatter-gather
# scatter-gather: on
sendfile() 过程 :
- DMA 将磁盘数据拷到内核缓冲区
- 将缓冲区描述符和数据长度传到 socket 缓冲区
- SG-DMA 直接将内核缓存数据拷到网卡缓冲区
零拷贝 (Zero-copy) : DMA传输数据, 不用 CPU 搬运数据
- 只用 2 次上下文切换和数据拷贝次数
PageCache
零拷贝用了内核缓冲区 (PageCache) , 让零拷贝性能更高
PageCache 优点 :
- 缓存最近被访问的数据: 缓存热点数据
- 预读功能: 读取周边数据
传输文件太大(GB) , PageCache 问题 :
- 长时间被大文件占据, 热点小文件无法利用缓存
- 大文件无法利用缓存 , 耗费 DMA 拷到 PageCache 耗时