readv(2)
bestie, this is readv(2) from the System Calls Manual ๐
and it's literally all about reading or writing data into multiple buffers. main character energy for scatter/gather I/O, no cap. โจ
๐ NAME ๐
readv, writev, preadv, pwritev, preadv2, pwritev2 โ read or write data into multiple buffers. ๐ฆ
๐ LIBRARY ๐
Standard C library (libc, -lc). periodt.
โจ SYNOPSIS โจ
#include <sys/uio.h>
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
ssize_t preadv(int fd, const struct iovec *iov, int iovcnt,
off_t offset);
ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt,
off_t offset);
ssize_t preadv2(int fd, const struct iovec *iov, int iovcnt,
off_t offset, int flags);
ssize_t pwritev2(int fd, const struct iovec *iov, int iovcnt,
off_t offset, int flags);
๐ Feature Test Macro Requirements for glibc (see feature_test_macros(7)) ๐
preadv(),pwritev():- Since glibc 2.19:
_DEFAULT_SOURCE - glibc 2.19 and earlier:
_BSD_SOURCE
- Since glibc 2.19:
๐ง DESCRIPTION ๐ง
The readv() system call reads iovcnt buffers from the file associated with the file descriptor fd into the buffers described by iov โ this is the scatter input moment, bestie. ๐
The writev() system call writes iovcnt buffers of data described by iov to the file associated with the file descriptor fd โ that's the gather output vibe. ๐ซ
The pointer iov points to an array of iovec structures, described in iovec(3type).
The readv() system call works just like read(2) except that multiple buffers are filled.
The writev() system call works just like write(2) except that multiple buffers are written out.
Buffers are processed in array order. This means that readv() completely fills iov[0] before proceeding to iov[1], and so on. (If there is insufficient data, then not all buffers pointed to by iov may be filled.) Similarly, writev() writes out the entire contents of iov[0] before proceeding to iov[1], and so on.
The data transfers performed by readv() and writev() are atomic: the data written by writev() is written as a single block that is not intermingled with output from writes in other processes; analogously, readv() is guaranteed to read a contiguous block of data from the file, regardless of read operations performed in other threads or processes that have file descriptors referring to the same open file description (see open(2)). ๐
bestie, atomic means no mixing, no cap. it's giving single block energy. ๐ฅ
๐ preadv() and pwritev() ๐
The preadv() system call combines the functionality of readv() and pread(2). It performs the same task as readv(), but adds a fourth argument, offset, which specifies the file offset at which the input operation is to be performed.
The pwritev() system call combines the functionality of writev() and pwrite(2). It performs the same task as writev(), but adds a fourth argument, offset, which specifies the file offset at which the output operation is to be performed.
The file offset is not changed by these system calls. The file referred to by fd must be capable of seeking.
๐ preadv2() and pwritev2() ๐
These system calls are similar to preadv() and pwritev() calls, but add a fifth argument, flags, which modifies the behavior on a per-call basis.
Unlike preadv() and pwritev(), if the offset argument is -1, then the current file offset is used and updated.
The flags argument contains a bitwise OR of zero or more of the following flags:
RWF_DSYNC(since Linux 4.7) โ Provide a per-write equivalent of theO_DSYNCopen(2)flag. This flag is meaningful only forpwritev2(), and its effect applies only to the data range written by the system call.RWF_HIPRI(since Linux 4.6) โ High priority read/write. Allows block-based filesystems to use polling of the device, which provides lower latency, but may use additional resources. (Currently, this feature is usable only on a file descriptor opened using theO_DIRECTflag.)RWF_SYNC(since Linux 4.7) โ Provide a per-write equivalent of theO_SYNCopen(2)flag. This flag is meaningful only forpwritev2(), and its effect applies only to the data range written by the system call.RWF_NOWAIT(since Linux 4.14) โ Do not wait for data which is not immediately available. If this flag is specified, thepreadv2()system call will return instantly if it would have to read data from the backing storage or wait for a lock. If some data was successfully read, it will return the number of bytes read. If no bytes were read, it will return-1and seterrnotoEAGAIN(but see BUGS). Currently, this flag is meaningful only forpreadv2().RWF_APPEND(since Linux 4.16) โ Provide a per-write equivalent of theO_APPENDopen(2)flag. This flag is meaningful only forpwritev2(), and its effect applies only to the data range written by the system call. Theoffsetargument does not affect the write operation; the data is always appended to the end of the file. However, if theoffsetargument is-1, the current file offset is updated.RWF_NOAPPEND(since Linux 6.9) โ Do not honor theO_APPENDopen(2)flag. This flag is meaningful only forpwritev2(). Historically, Linux honoredO_APPENDflag if set and ignored theoffsetargument, which is a bug. Forpwritev2(), theoffsetargument is honored as expected ifRWF_NOAPPENDflag is set, the same as ifO_APPENDflag were not set.RWF_ATOMIC(since Linux 6.11) โ Requires that writes to regular files in block-based filesystems be issued with torn-write protection. Torn-write protection means that for a power or any other hardware failure, all or none of the data from the write will be stored, but never a mix of old and new data. This flag is meaningful only forpwritev2(), and its effect applies only to the data range written by the system call. The total write length must be power-of-2 and must be sized in the range [stx_atomic_write_unit_min,stx_atomic_write_unit_max]. The write must be at a naturally-aligned offset within the file with respect to the total write length. For example, a write of length32KiBat a file offset of32KiBis permitted, however a write of length32KiBat a file offset of48KiBis not permitted. The upper limit ofiovcntforpwritev2()is given by the value instx_atomic_write_segments_max. Torn-write protection only works withO_DIRECTflag, that is, buffered writes are not supported. To guarantee consistency from the write between a file's in-core state with the storage device,O_SYNCorO_DSYNCmust be specified foropen(2). The same synchronized I/O guarantees as described inopen(2)are provided when these flags or their equivalent flags and system calls are used (e.g., ifRWF_SYNCis specified forpwritev2()). ๐ฅRWF_DONTCACHE(since Linux 6.14) โ Reads or writes to a regular file will prune instantiated page cache content when the operation completes. This is different than normal buffered I/O, where the data usually remains in cache until such time that it gets reclaimed due to memory pressure. If ranges of the read or written I/O were already in cache before this read or write, then those ranges will not be pruned at I/O completion time.
Additionally, any range dirtied by a write operation with RWF_DONTCACHE set will get kicked off for writeback. This is similar to calling sync_file_range(2) with SYNC_FILE_RANGE_WRITE to start writeback on the given range. RWF_DONTCACHE is a hint, or best effort, where no hard guarantees are given on the state of the page cache once the operation completes.
If used on a file system or block device that doesn't support it, it will return -1, and errno will be set to EOPNOTSUPP.
๐ซถ RETURN VALUE ๐ซถ
On success, readv(), preadv(), and preadv2() return the number of bytes read; writev(), pwritev(), and pwritev2() return the number of bytes written.
Note that it is not an error for a successful call to transfer fewer bytes than requested (see read(2) and write(2)).
On error, -1 is returned, and errno is set to indicate the error.
๐ ERRORS ๐
The errors are as given for read(2) and write(2). Furthermore, preadv(), preadv2(), pwritev(), and pwritev2() can also fail for the same reasons as lseek(2). Additionally, the following errors are defined:
EINVALโ The sum of theiov_lenvalues overflows anssize_tvalue.EINVALโ IfRWF_ATOMICis specified, the combination of the sum of theiov_lenvalues and theoffsetvalue does not comply with the length and offset torn-write protection rules.EINVALโ The vector count,iovcnt, is less than zero or greater than the permitted maximum. IfRWF_ATOMICis specified, this maximum is given by thestx_atomic_write_segments_maxvalue fromstatx.EOPNOTSUPPโRWF_DONTCACHEwas set inflagsand the file doesn't support it.EOPNOTSUPPโ An unknown flag is specified inflags.
๐ VERSIONS ๐
๐ง C library/kernel differences ๐ง
The raw preadv() and pwritev() system calls have call signatures that differ slightly from that of the corresponding GNU C library wrapper functions shown in the SYNOPSIS. The final argument, offset, is unpacked by the wrapper functions into two arguments in the system calls:
unsigned long pos_l, unsigned long pos
These arguments contain, respectively, the low order and high order 32 bits of offset.
โญ STANDARDS โญ
readv(),writev()โ POSIX.1-2024.preadv(),pwritev()โ BSD.preadv2(),pwritev2()โ Linux.
๐ HISTORY โจ
Okay so bestie, let's do a lil timeline tour, because this family goes way back ๐
readv()andwritev()โ POSIX.1-2001, and also 4.4BSD. Like, they first showed up in 4.2BSD. So these two are lowkey the ancestors of the whole squad ๐preadv()andpwritev()โ Linux 2.6.30, glibc 2.10. The glow-up era ๐ธpreadv2()andpwritev2()โ Linux 4.6, glibc 2.26. The newest siblings, arrived fashionably late, slayed ๐
๐ฆ Historical C library/kernel differences (the messy backstory)
So here's the tea โ: on early versions of Linux, IOV_MAX was so low, and the glibc wrapper functions for readv() and writev() had to do some extra work to cope. Specifically, if they clocked that the underlying kernel system call failed because this limit got exceeded, they'd spring into action:
- For
readv()โ the wrapper function allocated a temporary buffer big enough to hold all of the items specified byiov, passed that buffer into a call toread(2), copied the data from the buffer over to the locations specified by theiov_basefields of the elements ofiov, and then freed the buffer. Like, it did the whole relay race for you ๐โโ๏ธ - For
writev()โ the wrapper function did the analogous task, using a temporary buffer and a call towrite(2).
Then the plot resolved: the need for this extra effort in the glibc wrapper functions went away with Linux 2.2 and later. But glibc kept providing this behavior until glibc 2.10 anyway (iconic commitment, honestly ๐ญ). Starting with glibc 2.9, the wrapper functions only did this behavior if the library detected that the system was running a Linux kernel older than Linux 2.6.18 โ and yes, that's an arbitrarily selected kernel version, no cap ๐ซ . And since glibc 2.20 (which requires a minimum of Linux 2.6.32), the glibc wrapper functions always just directly invoke the system calls. Clean, simple, no drama. โจ
๐ NOTES ๐
- POSIX.1 allows an implementation to place a limit on the number of items that can be passed in
iov. Rude but allowed, ok?? ๐ - An implementation can advertise its limit by defining
IOV_MAXin<limits.h>, or at run time via the return value fromsysconf(_SC_IOV_MAX). - On modern Linux systems, the limit is 1024. Slay, we love capacity ๐
- Back in Linux 2.0 days, this limit was 16. Sixteen!! In this economy!! ๐ญ
๐ BUGS ๐
Linux 5.9 and Linux 5.10 have a bug wherepreadv2()with theRWF_NOWAITflag may return0even when not at end of file.
Not me finding a genuinely deranged bug in the wild ๐ซ If you're on those kernels hitting RWF_NOWAIT, just... know that a 0 might not mean what you think it means. Bestie, check your kernel. fr fr.
๐ธ EXAMPLES ๐ธ
The following code sample demonstrates the use of writev() โ look how cute and tiny it is:
char *str0 = "hello ";
char *str1 = "world\n";
ssize_t nwritten;
struct iovec iov[2];
iov[0].iov_base = str0;
iov[0].iov_len = strlen(str0);
iov[1].iov_base = str1;
iov[1].iov_len = strlen(str1);
nwritten = writev(STDOUT_FILENO, iov, 2);
Two lil string girlies, one syscall, and out comes hello world\n like magic ๐ชโจ Obsessed with how the kernel just gathers them up for you. Main character energy fr.
๐ SEE ALSO ๐
Go read those too, bestie, they're the whole friend group ๐ซถ
And that's the page!! ๐ This absolute baddie of a manpage is from Linux man-pages 6.20, dated 2026-02-22, and it's the readv(2) page โ so if you ever forget which version of the docs you were reading, now you know, iykyk ๐โจ Slay, go make some syscalls responsibly. Periodt. ๐
๐
๐ฆ where this page comes from
- arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- ๐ the page's license: Linux-man-pages-copyleft
- Copyright 1993, Thomas Koenig <ig25@rz.uni-karlsruhe.de>
- Copyright 2007-2010, Michael Kerrisk <mtk.manpages@gmail.com>
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms