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

Search for a command to run...

No comments yet. Be the first to comment.
Modern software systems often separate control logic from high-performance execution logic. This design is common in networking, distributed systems, operating systems, storage engines, and embedded s

Introduction Engineering software often combines multiple programming languages to leverage their individual strengths. A common approach is to implement computational algorithms in native C or C++ wh

Deadlocks are one of the most common synchronization problems encountered in operating systems and concurrent programming. Although the concept is frequently introduced in textbooks, observing it insi

Memory and resource allocation are fundamental operations inside the Linux kernel. Whether assigning device IDs, managing CPU masks, allocating interrupt vectors, or tracking hardware resources, the k

The Linux scheduler is one of the most important components of the operating system. Every running program, background service, and kernel thread eventually interacts with the scheduler. In this artic

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().
Scatter/Gather I/O enables a single system call to transfer data between multiple non-contiguous memory buffers and a file descriptor.
readv): Reads data from a file into multiple buffers.writev): Writes data from multiple buffers into a file.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.
struct iovecThe 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 |
#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]);
}
iov[0] โ First string iov[1] โ Second string iov[2] โ Third stringThis array is known as an I/O vector, representing three independent memory buffers.
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]));
writev()Scatter/gather I/O solves these issues:
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
| Part | Meaning |
|---|---|
fd |
File descriptor |
iov |
Array of struct iovec |
iovcnt |
Number of buffers |
| Return Value | Total bytes written |
ssize_t nr;
nr = writev(fd, iov, 3);
printf("Wrote %zd bytes\n", nr);
#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;
}
gcc scatter_gather.c -o scatter_gather
./scatter_gather
cat buccaneer.txt
buccaneer.txtThe 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:
+-------------------------------+-------------------------------+-------------------------------+
| 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" |
+--------------------------------------------------------------+
open() Flagsopen("buccaneer.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
| 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 |
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 |
| Operation | System Call | Description |
|---|---|---|
| Scatter I/O | readv() |
Reads data into multiple buffers |
| Gather I/O | writev() |
Writes data from multiple buffers |
When writev() is invoked:
iovec array.[C Program]
|
v
writev(fd, iov, 3)
|
v
[Linux Kernel]
|
v
[File System]
|
v
[buccaneer.txt]
[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.
write() and read() are special cases of vectored I/O with a single buffer.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 |
writev() writes several buffers in a single atomic operation.readv() reads data into multiple memory locations.