Linux splice 可以在内核态搬运文件和 socket 数据
splice 是 Linux 提供的零拷贝数据搬运接口。它可以在文件描述符之间移动数据,并尽量避免把数据复制到用户态缓冲区。典型用途是文件、管道、socket 之间的数据转发。
普通读写路径通常是:
kernel -> user buffer -> kernelsplice 的目标是减少中间这次用户态拷贝:
kernel -> pipe buffer -> kernel函数签名
#define _GNU_SOURCE
#include <fcntl.h>
ssize_t splice(int fd_in, loff_t *off_in,
int fd_out, loff_t *off_out,
size_t len, unsigned int flags);参数含义:
fd_in:输入文件描述符。off_in:输入偏移。为NULL时使用并更新文件描述符当前偏移;非NULL时从指定偏移读取并更新该变量。fd_out:输出文件描述符。off_out:输出偏移,规则同off_in。len:最多移动的字节数。flags:行为标志,可以为0。
返回值是实际移动的字节数。返回 0 通常表示输入已经到达 EOF。返回 -1 表示失败,并设置 errno。
pipe 是核心中转结构
splice 的一个重要限制是:输入或输出的一端通常需要是 pipe。因此常见写法是两段式:
int pipe_fds[2];
pipe(pipe_fds);
splice(file_fd, NULL, pipe_fds[1], NULL, len, 0);
splice(pipe_fds[0], NULL, socket_fd, NULL, len, 0);第一步把数据从文件移动到 pipe,第二步把数据从 pipe 移动到 socket。用户态代码不直接接触数据内容,只负责安排数据路径。
文件到 socket 的示例
下面的示例把本地文件内容发送到已连接的 socket:
#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
int send_file_by_splice(int file_fd, int socket_fd, size_t total_len) {
int pipe_fds[2];
if (pipe(pipe_fds) < 0) {
return -1;
}
size_t sent = 0;
while (sent < total_len) {
size_t chunk = total_len - sent;
if (chunk > 65536) {
chunk = 65536;
}
ssize_t moved = splice(file_fd, NULL, pipe_fds[1], NULL, chunk, 0);
if (moved <= 0) {
break;
}
ssize_t written = splice(pipe_fds[0], NULL, socket_fd, NULL, moved, 0);
if (written <= 0) {
break;
}
sent += written;
}
close(pipe_fds[0]);
close(pipe_fds[1]);
return 0;
}这个函数适合用于代理、网关、静态文件服务等数据转发场景。它减少用户态内存拷贝,但仍然需要处理短读、短写、阻塞、错误返回等情况。
常用 flags
splice 的 flags 可以组合以下选项:
SPLICE_F_MOVE:提示内核可以移动页而不是复制页。现代内核中更多是优化提示。SPLICE_F_NONBLOCK:以非阻塞方式操作,但文件描述符本身也应设置非阻塞。SPLICE_F_MORE:提示后续还有更多数据,类似MSG_MORE。SPLICE_F_GIFT:主要用于vmsplice,表示用户页可以交给内核管理。
普通阻塞式代码可以先使用 0,把正确性处理清楚后再考虑 flags。
注意事项
splice 不等于所有场景都更快。它减少数据拷贝,但会引入 pipe 管理、系统调用次数、阻塞语义和内核路径差异。小数据量、需要用户态解析或修改内容的场景,普通 read/write 更直接。
使用 splice 时应检查每一次返回值。实际移动字节数可能小于请求的 len,网络 socket 尤其需要循环处理。错误路径也要关闭 pipe 两端,避免文件描述符泄漏。
总结
splice 的核心价值是让数据在内核对象之间流动,减少用户态缓冲区参与。典型模式是 fd -> pipe -> fd。它适合大块转发和代理类场景,不适合需要频繁检查或修改数据内容的业务逻辑。
本文由 AI 辅助生成,可能存在错误或遗漏,请以实际资料和官方文档为准。