3

I'm trying to test the sendfile() system call under Linux 2.6.32 to zero-copy data between two regular files. As far as I understand, it should work: ever since 2.6.22, sendfile() has been implemented using splice(), and both the input file and the output file can be either regular files or sockets.

The following is the content of sendfile_test.c:

#include <sys/sendfile.h>

#include <fcntl.h>
#include <stdio.h>

int main(int argc, char **argv) {
  int result;
  int in_file;
  int out_file;

  in_file = open(argv[1], O_RDONLY);
  out_file = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);

  result = sendfile(out_file, in_file, NULL, 1);
  if (result == -1)
    perror("sendfile");

  close(in_file);
  close(out_file);

  return 0;
}

And when I'm running the following commands:

$ gcc sendfile_test.c 
$ ./a.out infile outfile

The output is

sendfile: Invalid argument

And when running

$ strace ./a.out infile outfile

The output contains

open("infile", O_RDONLY)                = 3
open("outfile", O_WRONLY|O_CREAT|O_TRUNC, 0644) = 4
sendfile(4, 3, NULL, 1)                 = -1 EINVAL (Invalid argument)

What am I doing wrong?

Daniel Hershcovich
  • 3,751
  • 2
  • 30
  • 35
  • 2
    First, check that your open() calls don't return errors. – cjg May 31 '10 at 18:48
  • I fixed the command line input to what I originally intended. Now you can see the resulting error in strace: sendfile(4, 3, NULL, 1) = -1 EINVAL (Invalid argument) – Daniel Hershcovich May 31 '10 at 20:37
  • And regarding the fact that sendfile() can get either a regular file or a socket on both ends: splice() must have a pipe as one of its arguments, but the current implementation of sendfile() allocates a temporary pipe buffer in the pipe cache, and splices the data to it from the input file, and then from it to the output file. – Daniel Hershcovich May 31 '10 at 21:35

1 Answers1

6

You forgot to check that argc is equal to 3, i.e. you are opening the output file by the name argv[2] but only give your program one argument (and you are not checking for errors after open(2).)

You can use strace(1) to find out which system call fails.

Edit:

This looks like older kernel to me. Same source (modulo error checking) works fine here under 2.6.33.4 #3 SMP. Also, any particular reason you are copying just one byte?

#include <sys/sendfile.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main( int argc, char *argv[] )
{
    int in_file, out_file;

    if ( argc != 3 )
    {
        fprintf( stderr, "usage: %s <in-file> <out-file>\n", argv[0] );
        exit( 1 );
    }

    if (( in_file = open( argv[1], O_RDONLY )) == -1 ) err( 1, "open" );
    if (( out_file = open( argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644 )) == -1 )
        err( 1, "open(2)" );

    if ( sendfile( out_file, in_file, NULL, 4096 ) == -1 ) err( 1, "sendfile" );

    exit( 0 );
}

Trace:

nickf@slack:~/csource/linux/splice$ cc -Wall -pedantic -ggdb -g3 -o sf sndf.c 
nickf@slack:~/csource/linux/splice$ strace ./sf Makefile mm
...
open("Makefile", O_RDONLY)              = 3
open("mm", O_WRONLY|O_CREAT|O_TRUNC, 0644) = 4
sendfile(4, 3, NULL, 4096)              = 239
exit_group(0)                           = ?
nickf@slack:~/csource/linux/splice$ diff Makefile mm 
nickf@slack:~/csource/linux/splice$
Nikolai Fetissov
  • 82,306
  • 11
  • 110
  • 171