Scatter/Gather I/O in Linux: A Deep Dive into `readv()` and `writev()`

Scatter/Gather I/O in Linux: A Deep Dive into readv() and writev()
Efficient data transfer is a cornerstone of high-performance systems programming. Linux provides powerful mechanisms to optimize input/output operations, one of which is Scatter/Gather I/O. This technique allows multiple buffers to be read from or written to a file or socket using a single system call, reducing overhead and improving performance.
This article explores the concept, implementation, and practical applications of scatter/gather I/O using readv() and writev().
π What is Scatter/Gather I/O?
Scatter/Gather I/O enables a single system call to transfer data between multiple non-contiguous memory buffers and a file descriptor.
- Scatter I/O (
readv): Reads data from a file into multiple buffers. - Gather I/O (
writev): Writes data from multiple buffers into a file.
Benefits
- β Reduces system call overhead
- β Ensures atomic data transfers
- β Improves performance
- β Eliminates unnecessary data copying
- β Ideal for networking and structured data handling
π¦ Example Buffers in Memory
char *buf[] = {
"The term buccaneer comes from the word boucan.\n",
"A boucan is a wooden frame used for cooking meat.\n",
"Buccaneer is the West Indies name for a pirate.\n"
};
These buffers will be written to a file using a single writev() system call.
π Understanding struct iovec
The iovec structure describes a memory buffer:
#include <sys/uio.h>
struct iovec {
void *iov_base; // Pointer to data
size_t iov_len; // Length of data in bytes
};
| Field | Purpose |
|---|---|
iov_base |
Pointer to the start of the buffer in memory |
iov_len |
Length in bytes of the buffer to read or write |
π οΈ Preparing the I/O Vector
#include <string.h>
#include <sys/uio.h>
struct iovec iov[3];
for (int i = 0; i < 3; i++) {
iov[i].iov_base = buf[i];
iov[i].iov_len = strlen(buf[i]);
}
Interpretation
iov[0]β First stringiov[1]β Second stringiov[2]β Third string
This array is known as an I/O vector, representing three independent memory buffers.
βοΈ Writing Data Using write()
Traditionally, data is written using multiple system calls:
write(fd, buf[0], strlen(buf[0]));
write(fd, buf[1], strlen(buf[1]));
write(fd, buf[2], strlen(buf[2]));
Drawbacks
- β Three system calls increase overhead
- β Data writes may be interleaved with other processes
- β Less efficient and non-atomic
π Writing Data Using writev()
Scatter/gather I/O solves these issues:
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
Parameters
| Part | Meaning |
|---|---|
fd |
File descriptor |
iov |
Array of struct iovec |
iovcnt |
Number of buffers |
| Return Value | Total bytes written |
Example
ssize_t nr;
nr = writev(fd, iov, 3);
printf("Wrote %zd bytes\n", nr);
Advantages
- β Single system call
- β Atomic write operation
- β Improved efficiency
- β Reduced kernel-user space transitions
π Complete Runnable C Program
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/uio.h>
int main() {
char *buf[] = {
"The term buccaneer comes from the word boucan.\n",
"A boucan is a wooden frame used for cooking meat.\n",
"Buccaneer is the West Indies name for a pirate.\n"
};
struct iovec iov[3];
for (int i = 0; i < 3; i++) {
iov[i].iov_base = buf[i];
iov[i].iov_len = strlen(buf[i]);
}
int fd = open("buccaneer.txt",
O_WRONLY | O_CREAT | O_TRUNC,
0644);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
ssize_t nr = writev(fd, iov, 3);
if (nr == -1) {
perror("writev");
close(fd);
exit(EXIT_FAILURE);
}
printf("Wrote %zd bytes to buccaneer.txt\n", nr);
close(fd);
return 0;
}
βοΈ Compilation and Execution
gcc scatter_gather.c -o scatter_gather
./scatter_gather
cat buccaneer.txt
π Output in buccaneer.txt
The term buccaneer comes from the word boucan.
A boucan is a wooden frame used for cooking meat.
Buccaneer is the West Indies name for a pirate.
All buffers are concatenated in order and written atomically.
π§ Memory Visualization
Memory:
+-------------------------------+-------------------------------+-------------------------------+
| Buffer 0 | Buffer 1 | Buffer 2 |
| "The term buccaneer..." | "A boucan is a..." | "Buccaneer is the..." |
+-------------------------------+-------------------------------+-------------------------------+
iovec Array:
+---------+----------------------+---------+
| Index | iov_base | iov_len |
+---------+----------------------+---------+
| iov[0] | -> Buffer 0 | len0 |
| iov[1] | -> Buffer 1 | len1 |
| iov[2] | -> Buffer 2 | len2 |
+---------+----------------------+---------+
writev(fd, iov, 3)
|
v
+--------------------------------------------------------------+
| File: "Buffer0Buffer1Buffer2" |
+--------------------------------------------------------------+
π Understanding open() Flags
open("buccaneer.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
File Flags
| Flag | Meaning |
|---|---|
O_WRONLY |
Open file for writing only |
O_CREAT |
Create the file if it does not exist |
O_TRUNC |
Truncate the file to zero length |
π Understanding File Permissions (0644)
0 6 4 4
| | | |
| | | +-- Others: Read
| | +---- Group: Read
| +------ Owner: Read + Write
+-------- Octal Indicator
Result:
-rw-r--r--
| Role | Permission |
|---|---|
| Owner | Read, Write |
| Group | Read |
| Others | Read |
π§© Scatter vs. Gather Operations
| Operation | System Call | Description |
|---|---|---|
| Scatter I/O | readv() |
Reads data into multiple buffers |
| Gather I/O | writev() |
Writes data from multiple buffers |
π Interaction with the Linux Kernel
When writev() is invoked:
- The program issues a system call.
- The kernel accesses the
iovecarray. - Data is copied from user space to kernel space.
- The file is updated atomically.
[C Program]
|
v
writev(fd, iov, 3)
|
v
[Linux Kernel]
|
v
[File System]
|
v
[buccaneer.txt]
π Interoperability Example: Swift Calling a C Library
[F# Program]
|
v
[C Shared Library (.so)]
|
v
[iovec Buffers in Memory]
|
v
writev(fd, iov, 3)
|
v
[Kernel]
|
v
[buccaneer.txt]
This design is common in high-performance and polyglot systems.
β‘ Performance Insights
write()andread()are special cases of vectored I/O with a single buffer.- The Linux kernel internally optimizes I/O operations using vectored mechanisms.
- Scatter/gather I/O is widely used in:
- Network servers
- Databases
- Compilers
- Logging systems
- High-frequency trading platforms
- Operating system kernels
π Comparison: write() vs writev()
| Feature | write() |
writev() |
|---|---|---|
| System Calls | Multiple | Single |
| Atomicity | No | Yes |
| Performance | Lower | Higher |
| Buffer Support | Single | Multiple |
| Use Case | Simple I/O | Structured/High-performance I/O |
β Key Takeaways
- Scatter/Gather I/O enables efficient data transfer using multiple buffers.
writev()writes several buffers in a single atomic operation.readv()reads data into multiple memory locations.- Reduces system call overhead and improves performance.
- Essential for systems programming, networking, and high-performance computing.
- Widely used in the Linux kernel and modern operating systems.



