PROGRAMMING
How to use tee and splice
2017-03-02 | by David "DeMO" Martínez Oliveira
I read about
tee
and splice
long ago. I quickly went through the man pages and, like in the Matrix movie I said to myself.. "I know tee and splice". Recently, I have to write some code that perfectly matched the functionality of these two system calls... but when I tried them.
Yes, I went straight-forward and The
When researching on this topic I looked for the source code of the
■
tee
ed and splice
d my file descriptors and... the thing not even compiled. It was time to read again the man pages to figure out what was the problem.
As usual, there was no real problem, it was just me. I didn't really read what the man page said. So, in case you would even need to use these syscall here is how to do that.
Magic does not exists
Despite of what some companies with fruit logos keep saying, magic just does not exist. At least, it does not exist in the world of computers. When you first look attee
and splice
they look like some kind of advanced wizardry is performed in the kernel copying buffers around and magically transfer the data from some file descriptors to others. In a sense that is what happen but with some limitation, those buffers cannot not be anything.
Actually, we have to use pipes in order to let all this kernel code work. In short, the reason is that we do need a buffer to hold the data, even in the kernel, an a pipe, can be seen as a kernel buffer in a sense. Go through these messages from the kernel development list for more details.
http://yarchive.net/comp/linux/splice.html
Therefore, in order to use tee
and splice
we have to create pipes
to hold our data.
The tee
Tool re-implemented
When researching on this topic I looked for the source code of the tee
tool. I was expecting it to use tee
and splice
, however, what I found was the straight-forward implementation reading from some file descriptors and writing to others.
So I decide to re-implement it using these system calls, just for fun, and this is the code I come up with:
#define _GNU_SOURCE /* See feature_test_macros(7) */ #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> int main (int argc, char *argv[]) { int fd_in = 0; // stdin int fd_out = 1; // int fd_file; int pipe1[2], pipe2[2]; int n, n1; if (argc != 2) { fprintf (stderr, "Usage: %s file\n\n", argv[0]); return -1; } if ((fd_file = open (argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { perror ("open:"); return -1; } if (pipe (pipe1) < 0) { perror ("pipe1:"); return -1; } if (pipe (pipe2) < 0) { perror ("pipe2:"); return -1; } while (1) { // Copy stdin into the pipe if ((n = splice (fd_in, NULL, pipe1[1], NULL, 1024, SPLICE_F_MOVE | SPLICE_F_MORE)) < 0) { perror ("splice:"); return -1; } if (n == 0) { fprintf (stderr, "DONE"); close (fd_file); return 0; } // Duplicate pipe1 into pipe2 without consuming data // Now we have the same data into pipe1 and pipe2 if ((n1 = tee (pipe1[0], pipe2[1], n, 0)) < 0) { perror ("tee:"); close (fd_file); return 1; } if ((splice (pipe1[0], NULL, fd_out, NULL, n, SPLICE_F_MOVE | SPLICE_F_MORE)) < 0) { perror ("splice1:"); return -1; } if ((splice (pipe2[0], NULL, fd_file, NULL, n, SPLICE_F_MOVE | SPLICE_F_MORE)) < 0) { perror ("splice1:"); return -1; } } close (fd_out); }The code is self-explanatory. The only thing you need to know is that
tee
only works on pipes
and splice
expects the first parameter to be a pipe
.
Conclusion
It was interesting to figure out how to use these system call. I made some performance tests comparing the systemtee
command, and my implementation and I couldn't find any real performance difference between them.
As a final note, I checked the code above in the main scenarios and it always worked for me. However, and this is something I don't know, in the case that either tee
or splice
returns copy less bytes that the one we have requested, the code above will fail. In my tests it never happened but, as that is the default behaviour we find in the read
system call, I would expect that same behaviour here.
I haven't write the code for that case to keep this as a simple and neat example on how to use the system calls.
■
CLICKS: 3094