Zero-copy technology in Linux, what is the difference between sendfile, splice and tee?

Zero-copy technology in Linux refers to a set of system calls designed to minimize data copying between kernel and user space, thereby reducing CPU usage and improving throughput for high-volume data transfers. Among these, `sendfile`, `splice`, and `tee` serve distinct but related purposes, primarily differentiated by their source and destination endpoints. `sendfile` is the most specialized, optimized for copying data directly from a file descriptor to a socket descriptor, making it the canonical solution for efficient static file serving from a web server. It operates entirely within the kernel, moving data from the page cache to the network stack without a round-trip to user space, which is why it is often the first zero-copy method implemented in network applications.

In contrast, `splice` offers a more generalized pipe-oriented abstraction, enabling data movement between any two file descriptors provided at least one is a pipe. Its core mechanism involves connecting a data source to a data sink via an in-kernel pipe buffer, allowing data to be "spliced" from, for example, a socket to a file or between two pipes without copying to user memory. This makes `splice` uniquely powerful for constructing complex data pipelines, such as proxying network traffic or feeding data from a disk into a processing socket, where the pipe acts as a mandatory intermediary. Unlike `sendfile`, `splice` is not limited to socket destinations and can work with arbitrary file descriptors, offering greater flexibility at the cost of requiring the setup and management of pipe file descriptors.

The `tee` system call is a specialized companion to `splice`, designed to duplicate data from one pipe to another without consuming the original data from the source pipe. It effectively performs an in-kernel "tee" operation, allowing a single data stream to be forked into two independent pipelines for multicasting or parallel processing. The critical distinction is that `tee` only operates between two pipe descriptors, and the duplicated data remains available for reading from the original pipe, a feature neither `sendfile` nor `splice` provides. This makes `tee` ideal for scenarios where the same data must be inspected or processed in two different ways downstream, such as logging a network stream while simultaneously forwarding it.

The practical implications of these differences are significant for system design. `sendfile` remains the optimal choice for straightforward file-to-network transfers, while `splice` enables more sophisticated kernel-space data routing between arbitrary endpoints, provided the application architecture can incorporate pipes. `tee` fills a specific niche within such pipe-based architectures, enabling data duplication without additional copies. Understanding their distinct domains—`sendfile` for files to sockets, `splice` for generic pipe-mediated transfers, and `tee` for pipe duplication—allows developers to select the appropriate primitive to minimize context switches and buffer copies, thereby maximizing I/O performance for specific data flow patterns.