splice(2)
Move data between a pipe and another fd without copying through userspace.
- Since
- Linux 2.6.17check your kernel →
- glibc
- glibc 2.5
- System
- Linux
- Man page
- splice(2)
Synopsis
#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64
#include <fcntl.h>
ssize_t splice(int fd_in, off_t *_Nullable off_in,
int fd_out, off_t *_Nullable off_out,
size_t size, unsigned int flags);Rod's take
read() into a buffer, write() it back out, and you've hauled page-cache data through your address space twice to accomplish nothing. splice moves page references instead of bytes. The catch, every time, no exceptions: one end must be a pipe, because the pipe is the kernel's staging buffer. File to socket is two splices with a pipe in the middle. If it really is just file to socket, sendfile is simpler; use it. Loop on short returns like any write. Twenty years in the kernel and people are still copying by hand.
Flags and arguments that matter
| Name | What it does |
|---|---|
| SPLICE_F_MOVE | Hint to move pages instead of copying. A no-op since Linux 2.6.21, but still permitted. |
| SPLICE_F_NONBLOCK | Make the pipe side nonblocking; the other fd may still block unless it has O_NONBLOCK. |
| SPLICE_F_MORE | More data coming; useful when fd_out is a socket (like MSG_MORE / TCP_CORK). |
| SPLICE_F_GIFT | Unused for splice(); see vmsplice(2). |
Errors you will actually hit
| errno | Meaning |
|---|---|
| EINVAL | Neither fd is a pipe, target is append-mode or doesn't support splicing, or both ends are the same pipe. |
| ESPIPE | off_in or off_out was non-NULL but the corresponding fd is a pipe. |
| EAGAIN | SPLICE_F_NONBLOCK or O_NONBLOCK set and the operation would block. |
| EBADF | A file descriptor is invalid or lacks the needed read/write mode. |
| ENOMEM | Out of memory. |
Example
int p[2];
if (pipe(p) < 0)
err(1, "pipe");
while (left > 0) {
ssize_t n = splice(file_fd, &off, p[1], NULL, left, SPLICE_F_MORE);
if (n <= 0)
break;
for (ssize_t m = n; m > 0; ) {
ssize_t s = splice(p[0], NULL, sock_fd, NULL, m, SPLICE_F_MORE);
if (s <= 0)
err(1, "splice to socket");
m -= s;
}
left -= n;
}Trimmed from the full walkthrough in splice and tee: zero-copy data movement, 2.6.17 edition.
BSD equivalent
FreeBSD: sendfile(2) for file-to-socket (FreeBSD 3.0), and the SO_SPLICE socket option for socket-to-socket (FreeBSD 14.3). OpenBSD: SO_SPLICE via setsockopt(2) (OpenBSD 4.9).
The long version, from the console
Sources
Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.
