Advanced File I/O - Return values (
Page 3 of 4 )
On success, readv()
and
writev()
return the number of bytes read or written, respectively. This number should be the sum of all
count iov_len
values. On error, the system calls return
-1
, and set
errno
as appropriate. These system calls can experience any of the errors of the
read()
and
write()
system calls, and will, upon receiving such errors, set the same
errno
codes. In addition, the standards define two other error situations.
First, because the return type is an
ssize_t
, if the sum of all
count iov_len
values is greater than
SSIZE_MAX
, no data will be transferred,
-1
will be returned, and
errno
will be set to
EINVAL
.
Second, POSIX dictates that
count
must be larger than zero, and less than or equal to
IOV_MAX
, which is defined in
<limits.h>
. In Linux,
IOV_MAX
is currently
1024
. If
count
is
0
, the system calls return
0
.* If
count
is greater than
IOV_MAX
, no data is transferred, the calls return
-1
, and
errno
is set to
EINVAL
.
Optimizing the Count
During a vectored I/O operation, the Linux kernel must allocate internal data structures to represent each segment. Normally, this allocation would occur dynamically, based on the size of count
. As an optimization, however, the Linux kernel creates a small array of segments on the stack that it uses if
count
is sufficiently small, negating the need to dynamically allocate the segments, and thereby providing a small boost in performance. This threshold is currently eight, so if
count
is less than or equal to
8
, the vectored I/O operation occurs in a very memory-efficient manner off of the process’ kernel stack.
Most likely, you won’t have a choice about how many segments you need to transfer at once in a given vectored I/O operation. If you are flexible, however, and are debating over a small value, choosing a value of eight or less definitely improves efficiency.
writev( ) example
Let’s consider a simple example that writes out a vector of three segments, each containing a string of a different size. This self-contained program is complete enough to demonstrate writev()
, yet simple enough to serve as a useful code snippet:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/uio.h>
int main ()
{
struct iovec iov[3];
ssize_t nr;
int fd, i;
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" };
fd = open ("buccaneer.txt", O_WRONLY | O_CREAT | O_TRUNC);
if (fd == -1) {
perror ("open");
return 1;
}
/* fill out three iovec structures */
for (i = 0; i < 3; i++) {
iov[i].iov_base = buf[i];
iov[i].iov_len = strlen (buf[i]);
}
/* with a single call, write them all out */
nr = writev (fd, iov, 3);
if (nr == -1) {
perror ("writev");
return 1;
}
printf ("wrote %d bytes\n", nr);
if (close (fd)) {
perror ("close");
return 1;
}
return 0;
}
Running the program produces the desired result:
$ ./writev
wrote 148 bytes
As does reading the file:
$ cat 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.