$RodHat_
Syscall reference · I/O

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

NameWhat it does
SPLICE_F_MOVEHint to move pages instead of copying. A no-op since Linux 2.6.21, but still permitted.
SPLICE_F_NONBLOCKMake the pipe side nonblocking; the other fd may still block unless it has O_NONBLOCK.
SPLICE_F_MOREMore data coming; useful when fd_out is a socket (like MSG_MORE / TCP_CORK).
SPLICE_F_GIFTUnused for splice(); see vmsplice(2).

Errors you will actually hit

errnoMeaning
EINVALNeither fd is a pipe, target is append-mode or doesn't support splicing, or both ends are the same pipe.
ESPIPEoff_in or off_out was non-NULL but the corresponding fd is a pipe.
EAGAINSPLICE_F_NONBLOCK or O_NONBLOCK set and the operation would block.
EBADFA file descriptor is invalid or lacks the needed read/write mode.
ENOMEMOut 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

  1. splice(2), man7.org
  2. sendfile(2), FreeBSD
  3. getsockopt(2), FreeBSD
  4. getsockopt(2), OpenBSD

Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.