Search code examples
clinuxposix

POSIX or Linux-specific method of connecting a readable file descriptor to a writable file descriptor?


I've got a file descriptor, which might've come from a socket, a pipe, or an actual file, that can be read from. I've got another file descriptor (again, might represent a socket or an actual file) which can be written to. I need to pass data from one to the other, as rapidly as possible.

Currently, I know of no way to do this except picking an arbitrary buffer size, reading into it from the first descriptor, and then writing it to the second descriptor (which may require a loop if it can't write everything that was read in one go). I would like, instead, to be able to say "read from this file descriptor and put the data directly into that file descriptor, without an intermediate buffer"--or, better yet, "read from this file descriptor until it hits EOF, and put it all directly into that file descriptor, as quickly as possible." It seems to me that there ought to be a way to do this without detouring through user space, and paying for copying the data out of kernel buffers and then back into kernel buffers again, but I can't find any such way, and every phrase I've come up with to google for it just leads me to stuff about UNIX shell pipes. Is there a way to do it, or am I just stuck?


Solution

  • On Linux, the splice system call is designed for this purpose. It can move data between file descriptors directly within the kernel space.

    Here's an example of how to use it:

    #include <fcntl.h>
    #include <unistd.h>
    #include <stdio.h>
    
    #define SPLICE_SIZE 4096  // Define a reasonable size for splicing at a time
    
    int main(int argc, char *argv[]) {
        int fd_in = ...;   // The input file descriptor
        int fd_out = ...;  // The output file descriptor
    
        ssize_t n;
        while ((n = splice(fd_in, NULL, fd_out, NULL, SPLICE_SIZE, SPLICE_F_MOVE | SPLICE_F_MORE)) > 0) {
            // Loop until all data is transferred
        }
    
        if (n < 0) {
            perror("splice");
            return 1;
        }
    
        return 0;
    }