sendfile/splice实现了真正意义上的零拷贝:CPU完全不需要参与数据的拷贝,应用层Java NIO为开发人员提供了FileChannel.transferFrom()/transferTo()方法使用零拷贝。
sendfile
传输原理
- 用户进程通过 sendfile() 函数向内核(kernel)发起系统调用,上下文从用户态(user space)切换为内核态(kernel space);
- CPU 利用 DMA 控制器将数据从主存或硬盘拷贝到内核空间(kernel space)的读缓冲区(read buffer);
- CPU 把读缓冲区(read buffer)的文件描述符(file descriptor)和数据长度拷贝到网络缓冲区(socket buffer);
- 基于已拷贝的文件描述符(file descriptor)和数据长度,CPU 利用 DMA 控制器的 gather/scatter 操作直接批量地将数据从内核的读缓冲区(read buffer)拷贝到网卡进行数据传输;
- 上下文从内核态(kernel space)切换回用户态(user space),Sendfile 系统调用执行返回;
适用场景
sendfile适用于文件数据到网卡的传输过程,并且用户程序对数据没有修改的场景;
splice
传输原理
- 用户进程通过 splice() 函数向内核(kernel)发起系统调用,上下文从用户态(user space)切换为内核态(kernel space);
- CPU 利用 DMA 控制器将数据从主存或硬盘拷贝到内核空间(kernel space)的读缓冲区(read buffer);
- CPU 在内核空间的读缓冲区(read buffer)和网络缓冲区(socket buffer)之间建立管道(pipeline);
- CPU 利用 DMA 控制器将数据从网络缓冲区(socket buffer)拷贝到网卡进行数据传输;
- 上下文从内核态(kernel space)切换回用户态(user space),Splice 系统调用执行返回;
适用场景
splice适用于任意两个文件描述符中传输数据(两个文件描述符参数中有一个必须是管道设备),并且用户程序对数据没有修改的场景;
Java应用
Java NIO的FileChannel.transferFrom()/transferTo()底层基于sendfile/splice,不仅可以进行网络文件传输,还可以对本地文件实现零拷贝操作,示意图如下所示:
参考: