Implementations of readv and writev
This commit is contained in:
parent
aec561acc5
commit
f025e5f06b
7 changed files with 287 additions and 43 deletions
|
@ -104,7 +104,7 @@
|
|||
* be reliably traversed in the resolution of
|
||||
* a pathname in the absence of a loop.
|
||||
*/
|
||||
|
||||
#define IOV_MAX INT_MAX /* maximum number of buffers for readv/writev */
|
||||
#endif /* _POSIX_SOURCE */
|
||||
|
||||
#endif /* _LIMITS_H */
|
||||
|
|
|
@ -15,11 +15,9 @@ struct iovec
|
|||
size_t iov_len;
|
||||
};
|
||||
|
||||
#if 0
|
||||
_PROTOTYPE(ssize_t readv, (int _fildes, const struct iovec *_iov,
|
||||
int _iovcnt) );
|
||||
_PROTOTYPE(ssize_t writev, (int _fildes, const struct iovec *_iov,
|
||||
int iovcnt) );
|
||||
#endif
|
||||
|
||||
#endif /* _SYS_UIO_H */
|
||||
|
|
|
@ -100,6 +100,6 @@ libc_FILES=" \
|
|||
v8regerror.c \
|
||||
v8regexp.c \
|
||||
v8regsub.c \
|
||||
writev.c"
|
||||
vectorio.c"
|
||||
|
||||
TYPE=both
|
||||
|
|
128
lib/other/vectorio.c
Normal file
128
lib/other/vectorio.c
Normal file
|
@ -0,0 +1,128 @@
|
|||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define VECTORIO_READ 1
|
||||
#define VECTORIO_WRITE 2
|
||||
|
||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||
|
||||
static ssize_t vectorio_buffer(int fildes, const struct iovec *iov,
|
||||
int iovcnt, int readwrite, ssize_t totallen)
|
||||
{
|
||||
char *buffer;
|
||||
int iovidx, errno_saved;
|
||||
ssize_t copied, len, r;
|
||||
|
||||
/* allocate buffer */
|
||||
buffer = (char *) malloc(totallen);
|
||||
if (!buffer)
|
||||
return -1;
|
||||
|
||||
/* perform the actual read/write for the entire buffer */
|
||||
switch (readwrite)
|
||||
{
|
||||
case VECTORIO_READ:
|
||||
/* first read, then copy buffers (only part read) */
|
||||
r = read(fildes, buffer, totallen);
|
||||
|
||||
copied = 0;
|
||||
iovidx = 0;
|
||||
while (copied < r)
|
||||
{
|
||||
assert(iovidx < iovcnt);
|
||||
len = MIN(r - copied, iov[iovidx].iov_len);
|
||||
memcpy(iov[iovidx++].iov_base, buffer + copied, len);
|
||||
copied += len;
|
||||
}
|
||||
assert(r < 0 || r == copied);
|
||||
break;
|
||||
|
||||
case VECTORIO_WRITE:
|
||||
/* first copy buffers, then write */
|
||||
copied = 0;
|
||||
for (iovidx = 0; iovidx < iovcnt; iovidx++)
|
||||
{
|
||||
memcpy(buffer + copied, iov[iovidx].iov_base,
|
||||
iov[iovidx].iov_len);
|
||||
copied += iov[iovidx].iov_len;
|
||||
}
|
||||
assert(copied == totallen);
|
||||
|
||||
r = write(fildes, buffer, totallen);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
errno = EINVAL;
|
||||
r = -1;
|
||||
}
|
||||
|
||||
/* free the buffer, keeping errno unchanged */
|
||||
errno_saved = errno;
|
||||
free(buffer);
|
||||
errno = errno_saved;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static ssize_t vectorio(int fildes, const struct iovec *iov,
|
||||
int iovcnt, int readwrite)
|
||||
{
|
||||
int i;
|
||||
struct stat statbuf;
|
||||
ssize_t totallen;
|
||||
|
||||
/* parameter sanity checks */
|
||||
if (iovcnt > IOV_MAX)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
totallen = 0;
|
||||
for (i = 0; i < iovcnt; i++)
|
||||
{
|
||||
/* don't read/write anything in case of possible overflow */
|
||||
if ((ssize_t) (totallen + iov[i].iov_len) < totallen)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
totallen += iov[i].iov_len;
|
||||
|
||||
/* report on NULL pointers */
|
||||
if (iov[i].iov_len && !iov[i].iov_base)
|
||||
{
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* anything to do? */
|
||||
if (totallen == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* there aught to be a system call here; instead we use an intermediate
|
||||
* buffer; this is preferred over multiple read/write calls because
|
||||
* this function has to be atomic
|
||||
*/
|
||||
return vectorio_buffer(fildes, iov, iovcnt, readwrite, totallen);
|
||||
}
|
||||
|
||||
ssize_t readv(int fildes, const struct iovec *iov, int iovcnt)
|
||||
{
|
||||
return vectorio(fildes, iov, iovcnt, VECTORIO_READ);
|
||||
}
|
||||
|
||||
ssize_t writev(int fildes, const struct iovec *iov, int iovcnt)
|
||||
{
|
||||
return vectorio(fildes, iov, iovcnt, VECTORIO_WRITE);
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
ssize_t writev(int fildes, const struct iovec *iov, int iovcnt)
|
||||
{
|
||||
#if DEBUG
|
||||
fprintf(stderr, "bind: not implemented for fd %d\n", socket);
|
||||
#endif
|
||||
errno= ENOSYS;
|
||||
return -1;
|
||||
|
||||
#if 0
|
||||
int i, r;
|
||||
char *p;
|
||||
ssize_t l, sum;
|
||||
|
||||
/* We should buffer */
|
||||
sum= 0;
|
||||
for (i= 0; i<iovcnt; i++)
|
||||
{
|
||||
p= iov[i].iov_base;
|
||||
l= iov[i].iov_len;
|
||||
while (l > 0)
|
||||
{
|
||||
r= write(fildes, p, l);
|
||||
if (r <= 0)
|
||||
{
|
||||
assert(sum == 0);
|
||||
return r;
|
||||
}
|
||||
p += r;
|
||||
l -= r;
|
||||
sum += r;
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
#endif
|
||||
}
|
28
man/man3/readv.3
Normal file
28
man/man3/readv.3
Normal file
|
@ -0,0 +1,28 @@
|
|||
.TH READV 3 "January 6, 2009"
|
||||
.UC 4
|
||||
.SH NAME
|
||||
readv, writev \- vector-based IO
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.ft B
|
||||
#include <sys/uio.h>
|
||||
|
||||
ssize_t readv(int \fIfildes\fP, const struct iovec *\fIiov\fP, int \fIiovcnt\fP);
|
||||
ssize_t writev(int \fIfildes\fP, const struct iovec *\fIiov\fP, int \fIiovcnt\fP);
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
The \fBreadv\fP and \fBwritev\fP functions allow one to use multiple buffers
|
||||
when reading from or writing to files. The \fIfildes\fP parameter specifies the
|
||||
file descriptor as with the \fBread\fP and \fBwrite\fP functions. \fIiov\fP
|
||||
specifies an array of buffers to be read into or written from. For each element
|
||||
of this array, the iov_base member specifies the address of the buffer and
|
||||
iov_len specifies its size in bytes. The number of buffers is specified by
|
||||
\fIiovcnt\fP. At most IOV_MAX buffers may be specified and their total size may
|
||||
not exceed SSIZE_MAX (both constants are defined in limits.h).
|
||||
.SH "RETURN VALUE"
|
||||
In case of success, the total number of bytes read or written is returned.
|
||||
Zero may be returned if no buffers were specified or each buffer has size zero.
|
||||
In the case of writev, a return value zero may also indicate an end of file
|
||||
condition. If the functions fail, -1 is returned.
|
||||
.SH "SEE ALSO"
|
||||
read(2), write(2)
|
129
test/test18.c
129
test/test18.c
|
@ -14,6 +14,8 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#define NOCRASH 1 /* test11(), 2nd pipe */
|
||||
#define PDPNOHANG 1 /* test03(), write_standards() */
|
||||
|
@ -45,7 +47,9 @@
|
|||
#define UMASK "umask"
|
||||
#define CREAT "creat"
|
||||
#define WRITE "write"
|
||||
#define WRITEV "writev"
|
||||
#define READ "read"
|
||||
#define READV "readv"
|
||||
#define OPEN "open"
|
||||
#define CLOSE "close"
|
||||
#define LSEEK "lseek"
|
||||
|
@ -92,6 +96,12 @@ _PROTOTYPE(void try_open, (char *fname, int mode, int test));
|
|||
_PROTOTYPE(void test06, (void));
|
||||
_PROTOTYPE(void test07, (void));
|
||||
_PROTOTYPE(void access_standards, (void));
|
||||
_PROTOTYPE(void test08, (void));
|
||||
_PROTOTYPE(static int iovec_is_equal,
|
||||
(struct iovec *x, struct iovec *y, size_t size));
|
||||
_PROTOTYPE(static size_t iovec_setup,
|
||||
(int pattern, struct iovec *iovec, char *buffer, int count));
|
||||
_PROTOTYPE(static int power, (int base, int exponent));
|
||||
_PROTOTYPE(void try_access, (char *fname, int mode, int test));
|
||||
_PROTOTYPE(void e, (char *string));
|
||||
_PROTOTYPE(void nlcr, (void));
|
||||
|
@ -159,6 +169,7 @@ void test()
|
|||
test05();
|
||||
test06();
|
||||
test07();
|
||||
test08();
|
||||
umask(022);
|
||||
} /* test */
|
||||
|
||||
|
@ -855,6 +866,124 @@ char *fname;
|
|||
close_alot, clean_up_the_mess.
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* test READV/WRITEV *
|
||||
****************************************************************************/
|
||||
#define TEST8_BUFSZCOUNT 3
|
||||
#define TEST8_BUFSZMAX 65536
|
||||
#define TEST8_IOVCOUNT 4
|
||||
|
||||
void test08()
|
||||
{
|
||||
char buffer_read[TEST8_IOVCOUNT * TEST8_BUFSZMAX];
|
||||
char buffer_write[TEST8_IOVCOUNT * TEST8_BUFSZMAX];
|
||||
struct iovec iovec_read[TEST8_IOVCOUNT];
|
||||
struct iovec iovec_write[TEST8_IOVCOUNT];
|
||||
int fd, i, j, k, l, m;
|
||||
ssize_t sz_read, sz_write;
|
||||
size_t sz_read_exp, sz_read_sum, sz_write_sum;
|
||||
|
||||
/* try various combinations of buffer sizes */
|
||||
for (i = 0; i <= TEST8_IOVCOUNT; i++)
|
||||
for (j = 0; j < power(TEST8_BUFSZCOUNT, i); j++)
|
||||
for (k = 0; k <= TEST8_IOVCOUNT; k++)
|
||||
for (l = 0; l < power(TEST8_BUFSZCOUNT, k); l++)
|
||||
{
|
||||
/* put data in the buffers */
|
||||
for (m = 0; m < sizeof(buffer_write); m++)
|
||||
{
|
||||
buffer_write[m] = m ^ (m >> 8);
|
||||
buffer_read[m] = ~buffer_write[m];
|
||||
}
|
||||
|
||||
/* set up the vectors to point to the buffers */
|
||||
sz_read_sum = iovec_setup(j, iovec_read, buffer_read, i);
|
||||
sz_write_sum = iovec_setup(l, iovec_write, buffer_write, k);
|
||||
sz_read_exp = (sz_read_sum < sz_write_sum) ?
|
||||
sz_read_sum : sz_write_sum;
|
||||
|
||||
/* test reading and writing */
|
||||
if ((fd = open("file08", O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0)
|
||||
err(13, OPEN, "'file08'");
|
||||
else {
|
||||
sz_write = writev(fd, iovec_write, k);
|
||||
if (sz_write != sz_write_sum) err(5, WRITEV, "'file08'");
|
||||
if (lseek(fd, 0, SEEK_SET) != 0) err(5, LSEEK, "'file08'");
|
||||
sz_read = readv(fd, iovec_read, i);
|
||||
if (sz_read != sz_read_exp)
|
||||
err(5, READV, "'file08'");
|
||||
else
|
||||
{
|
||||
if (!iovec_is_equal(iovec_read, iovec_write, sz_read))
|
||||
err(8, READV, "'file08'");
|
||||
}
|
||||
|
||||
/* Remove testfile */
|
||||
Remove(fd, "file08");
|
||||
}
|
||||
}
|
||||
} /* test08 */
|
||||
|
||||
static int iovec_is_equal(struct iovec *x, struct iovec *y, size_t size)
|
||||
{
|
||||
int xpos = 0, xvec = 0, ypos = 0, yvec = 0;
|
||||
|
||||
/* compare byte by byte */
|
||||
while (size-- > 0)
|
||||
{
|
||||
/* skip over zero-byte buffers and those that have been completed */
|
||||
while (xpos >= x[xvec].iov_len)
|
||||
{
|
||||
xpos -= x[xvec++].iov_len;
|
||||
assert(xvec < TEST8_IOVCOUNT);
|
||||
}
|
||||
while (ypos >= y[yvec].iov_len)
|
||||
{
|
||||
ypos -= y[yvec++].iov_len;
|
||||
assert(yvec < TEST8_IOVCOUNT);
|
||||
}
|
||||
|
||||
/* compare */
|
||||
if (((char *) x[xvec].iov_base)[xpos++] !=
|
||||
((char *) y[yvec].iov_base)[ypos++])
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* no difference found */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static size_t iovec_setup(int pattern, struct iovec *iovec, char *buffer, int count)
|
||||
{
|
||||
static const size_t bufsizes[TEST8_BUFSZCOUNT] = { 0, 1, TEST8_BUFSZMAX };
|
||||
int i;
|
||||
size_t sum = 0;
|
||||
|
||||
/* the pattern specifies each buffer */
|
||||
for (i = 0; i < TEST8_IOVCOUNT; i++)
|
||||
{
|
||||
iovec->iov_base = buffer;
|
||||
sum += iovec->iov_len = bufsizes[pattern % TEST8_BUFSZCOUNT];
|
||||
|
||||
iovec++;
|
||||
buffer += TEST8_BUFSZMAX;
|
||||
pattern /= TEST8_BUFSZCOUNT;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
static int power(int base, int exponent)
|
||||
{
|
||||
int result = 1;
|
||||
|
||||
/* compute base^exponent */
|
||||
while (exponent-- > 0)
|
||||
result *= base;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* EXTENDED FIONS *
|
||||
**********************************************************************/
|
||||
|
|
Loading…
Reference in a new issue