8.12 sendfile: Fast Data Transfers

The sendfile system call provides an efficient mechanism for copying data from one file descriptor to another. The file descriptors may be open to disk files, sockets, or other devices.

Typically, to copy from one file descriptor to another, a program allocates a fixed-size buffer, copies some data from one descriptor into the buffer, writes the buffer out to the other descriptor, and repeats until all the data has been copied. This is inefficient in both time and space because it requires additional memory for the buffer and performs an extra copy of the data into that buffer.

Using sendfile, the intermediate buffer can be eliminated. Call sendfile, passing the file descriptor to write to; the descriptor to read from; a pointer to an offset variable; and the number of bytes to transfer. The offset variable contains the offset in the input file from which the read should start (0 indicates the beginning of the file) and is updated to the position in the file after the transfer. The return value is the number of bytes transferred. Include <sys/sendfile.h> in your program if it uses sendfile.

The program in Listing 8.10 is a simple but extremely efficient implementation of a file copy. When invoked with two filenames on the command line, it copies the contents of the first file into a file named by the second. It uses fstat to determine the size, in bytes, of the source file.

Listing 8.10 (copy.c) File Copy Using sendfile
#include <fcntl.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <sys/sendfile.h> 
#include <sys/stat.h> 
#include <sys/types.h> 
#include <unistd.h> 
 
int main (int argc, char* argv[]) 
{
  int read_fd; 
  int write_fd; 
  struct stat stat_buf; 
  off_t offset = 0; 
 
  /* Open the input file.  */ 
  read_fd = open (argv[1], O_RDONLY); 
  /* Stat the input file to obtain its size.  */ 
  fstat (read_fd, &stat_buf); 
  /* Open the output file for writing, with the same permissions as the 
     source file.  */ 
  write_fd = open (argv[2], O_WRONLY | O_CREAT, stat_buf.st_mode); 
  /* Blast the bytes from one file to the other.  */ 
  sendfile (write_fd, read_fd, &offset, stat_buf.st_size); 
  /* Close up.  */ 
  close (read_fd); 
  close (write_fd); 
 
  return 0; 
} 

The sendfile call can be used in many places to make copies more efficient. One good example is in a Web server or other network daemon, that serves the contents of a file over the network to a client program. Typically, a request is received from a socket connected to the client computer. The server program opens a local disk file to retrieve the data to serve and writes the file's contents to the network socket. Using sendfile can speed up this operation considerably. Other steps need to be taken to make the network transfer as efficient as possible, such as setting the socket parameters correctly. However, these are outside the scope of this book.