Skip to main content

Command Palette

Search for a command to run...

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

Published
β€’6 min read
 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 string
  • iov[1] β†’ Second string
  • iov[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:

  1. The program issues a system call.
  2. The kernel accesses the iovec array.
  3. Data is copied from user space to kernel space.
  4. 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() and read() 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.